blob: 3107181a61101493bf4251e6ce157aad2a660242 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersen5bb16772001-09-06 18:00:41 +000045#define IFS_BROKEN
Eric Andersenc470f442003-07-28 09:56:35 +000046#define PROFILE 0
Denis Vlasenko131ae172007-02-18 13:00:19 +000047#if ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000048#define JOBS 1
49#else
Denis Vlasenko666da5e2006-12-26 18:17:42 +000050#define JOBS 0
Eric Andersenc470f442003-07-28 09:56:35 +000051#endif
52
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#if DEBUG
54#define _GNU_SOURCE
55#endif
56#include "busybox.h"
57#include <paths.h>
58#include <setjmp.h>
59#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000060#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000061#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000062#endif
63
Denis Vlasenkob012b102007-02-19 22:43:01 +000064#if defined(__uClinux__)
65#error "Do not even bother, ash will not run on uClinux"
66#endif
67
Denis Vlasenkoaa744452007-02-23 01:04:22 +000068#if DEBUG
69#define TRACE(param) trace param
70#define TRACEV(param) tracev param
71#else
72#define TRACE(param)
73#define TRACEV(param)
74#endif
Eric Andersen2870d962001-07-02 17:27:21 +000075
Eric Andersenc470f442003-07-28 09:56:35 +000076#ifdef __GLIBC__
77/* glibc sucks */
78static int *dash_errno;
79#undef errno
80#define errno (*dash_errno)
81#endif
82
Eric Andersen2870d962001-07-02 17:27:21 +000083
Denis Vlasenko131ae172007-02-18 13:00:19 +000084#if ENABLE_ASH_ALIAS
Denis Vlasenkob012b102007-02-19 22:43:01 +000085#define ALIASINUSE 1
86#define ALIASDEAD 2
Eric Andersenc470f442003-07-28 09:56:35 +000087struct alias {
88 struct alias *next;
89 char *name;
90 char *val;
91 int flag;
Eric Andersen2870d962001-07-02 17:27:21 +000092};
Eric Andersenc470f442003-07-28 09:56:35 +000093static int aliascmd(int, char **);
94static int unaliascmd(int, char **);
Eric Andersenc470f442003-07-28 09:56:35 +000095static void printalias(const struct alias *);
Eric Andersen2870d962001-07-02 17:27:21 +000096#endif
97
Denis Vlasenkob012b102007-02-19 22:43:01 +000098
99/* ============ Shell options */
100
101static const char *const optletters_optnames[] = {
102 "e" "errexit",
103 "f" "noglob",
104 "I" "ignoreeof",
105 "i" "interactive",
106 "m" "monitor",
107 "n" "noexec",
108 "s" "stdin",
109 "x" "xtrace",
110 "v" "verbose",
111 "C" "noclobber",
112 "a" "allexport",
113 "b" "notify",
114 "u" "nounset",
115 "\0" "vi",
116#if DEBUG
117 "\0" "nolog",
118 "\0" "debug",
119#endif
120};
121
122#define optletters(n) optletters_optnames[(n)][0]
123#define optnames(n) (&optletters_optnames[(n)][1])
124
125#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
126
127static char optlist[NOPTS];
128
129#define eflag optlist[0]
130#define fflag optlist[1]
131#define Iflag optlist[2]
132#define iflag optlist[3]
133#define mflag optlist[4]
134#define nflag optlist[5]
135#define sflag optlist[6]
136#define xflag optlist[7]
137#define vflag optlist[8]
138#define Cflag optlist[9]
139#define aflag optlist[10]
140#define bflag optlist[11]
141#define uflag optlist[12]
142#define viflag optlist[13]
143#if DEBUG
144#define nolog optlist[14]
145#define debug optlist[15]
146#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000147
148
Denis Vlasenkob012b102007-02-19 22:43:01 +0000149/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000150
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000151static char nullstr[1]; /* zero length string */
152static const char homestr[] = "HOME";
153static const char snlfmt[] = "%s\n";
154static const char illnum[] = "Illegal number: %s";
155
Denis Vlasenkoa624c112007-02-19 22:45:43 +0000156static int isloginsh;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000157/* pid of main shell */
158static int rootpid;
159/* shell level: 0 for the main shell, 1 for its children, and so on */
160static int shlvl;
161#define rootshell (!shlvl)
162/* trap handler commands */
163static char *trap[NSIG];
164/* current value of signal */
165static char sigmode[NSIG - 1];
166/* indicates specified signal received */
167static char gotsig[NSIG - 1];
168static char *arg0; /* value of $0 */
Eric Andersenc470f442003-07-28 09:56:35 +0000169
170
Denis Vlasenkob012b102007-02-19 22:43:01 +0000171/* ============ Interrupts / exceptions
172 *
Eric Andersenc470f442003-07-28 09:56:35 +0000173 * We enclose jmp_buf in a structure so that we can declare pointers to
174 * jump locations. The global variable handler contains the location to
175 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000176 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000177 * exception handlers, the user should save the value of handler on entry
178 * to an inner scope, set handler to point to a jmploc structure for the
179 * inner scope, and restore handler on exit from the scope.
180 */
Eric Andersenc470f442003-07-28 09:56:35 +0000181struct jmploc {
182 jmp_buf loc;
183};
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000184static struct jmploc *exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +0000185static int exception;
Eric Andersenc470f442003-07-28 09:56:35 +0000186/* exceptions */
187#define EXINT 0 /* SIGINT received */
188#define EXERROR 1 /* a generic error */
189#define EXSHELLPROC 2 /* execute a shell procedure */
190#define EXEXEC 3 /* command execution failed */
191#define EXEXIT 4 /* exit the shell */
192#define EXSIG 5 /* trapped signal in wait(1) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000193static volatile int suppressint;
194static volatile sig_atomic_t intpending;
Eric Andersenc470f442003-07-28 09:56:35 +0000195/* do we generate EXSIG events */
196static int exsig;
197/* last pending signal */
198static volatile sig_atomic_t pendingsigs;
Eric Andersen2870d962001-07-02 17:27:21 +0000199
200/*
201 * These macros allow the user to suspend the handling of interrupt signals
202 * over a period of time. This is similar to SIGHOLD to or sigblock, but
203 * much more efficient and portable. (But hacking the kernel is so much
204 * more fun than worrying about efficiency and portability. :-))
205 */
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000206#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000207#define INT_OFF \
Eric Andersenc470f442003-07-28 09:56:35 +0000208 ({ \
209 suppressint++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000210 xbarrier(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000211 0; \
212 })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000213
214/*
215 * Called to raise an exception. Since C doesn't include exceptions, we
216 * just do a longjmp to the exception handler. The type of exception is
217 * stored in the global variable "exception".
218 */
219static void raise_exception(int) ATTRIBUTE_NORETURN;
220static void
221raise_exception(int e)
222{
223#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000224 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000225 abort();
226#endif
227 INT_OFF;
228 exception = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000229 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000230}
231
232/*
233 * Called from trap.c when a SIGINT is received. (If the user specifies
234 * that SIGINT is to be trapped or ignored using the trap builtin, then
235 * this routine is not called.) Suppressint is nonzero when interrupts
236 * are held using the INT_OFF macro. (The test for iflag is just
237 * defensive programming.)
238 */
239static void raise_interrupt(void) ATTRIBUTE_NORETURN;
240static void
241raise_interrupt(void)
242{
243 int i;
244
245 intpending = 0;
246 i = EXSIG;
247 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
248 if (!(rootshell && iflag)) {
249 signal(SIGINT, SIG_DFL);
250 raise(SIGINT);
251 }
252 i = EXINT;
253 }
254 raise_exception(i);
255 /* NOTREACHED */
256}
257
258#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
259static void int_on(void)
260{
261 if (--suppressint == 0 && intpending) {
262 raise_interrupt();
263 }
264}
265#define INT_ON int_on()
266static void force_int_on(void)
267{
268 suppressint = 0;
269 if (intpending)
270 raise_interrupt();
271}
272#define FORCE_INT_ON force_int_on()
273#else
274#define INT_ON \
275 ({ \
276 xbarrier(); \
277 if (--suppressint == 0 && intpending) raise_interrupt(); \
278 0; \
279 })
280#define FORCE_INT_ON \
281 ({ \
282 xbarrier(); \
283 suppressint = 0; \
284 if (intpending) raise_interrupt(); \
285 0; \
286 })
287#endif /* ASH_OPTIMIZE_FOR_SIZE */
288
289#define SAVE_INT(v) ((v) = suppressint)
290
291#define RESTORE_INT(v) \
Eric Andersenc470f442003-07-28 09:56:35 +0000292 ({ \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000293 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000294 suppressint = (v); \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295 if (suppressint == 0 && intpending) raise_interrupt(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000296 0; \
297 })
Denis Vlasenkob012b102007-02-19 22:43:01 +0000298
299#define EXSIGON \
Eric Andersenc470f442003-07-28 09:56:35 +0000300 ({ \
301 exsig++; \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000302 xbarrier(); \
Eric Andersenc470f442003-07-28 09:56:35 +0000303 if (pendingsigs) \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000304 raise_exception(EXSIG); \
Eric Andersenc470f442003-07-28 09:56:35 +0000305 0; \
306 })
307/* EXSIG is turned off by evalbltin(). */
Eric Andersen2870d962001-07-02 17:27:21 +0000308
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000309
Denis Vlasenkob012b102007-02-19 22:43:01 +0000310/* ============ stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000311
Eric Andersenc470f442003-07-28 09:56:35 +0000312static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000313outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000314{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000315 INT_OFF;
316 fputs(p, file);
317 INT_ON;
318}
319
320static void
321flush_stdout_stderr(void)
322{
323 INT_OFF;
324 fflush(stdout);
325 fflush(stderr);
326 INT_ON;
327}
328
329static void
330flush_stderr(void)
331{
332 INT_OFF;
333 fflush(stderr);
334 INT_ON;
335}
336
337static void
338outcslow(int c, FILE *dest)
339{
340 INT_OFF;
341 putc(c, dest);
342 fflush(dest);
343 INT_ON;
344}
345
346static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
347static int
348out1fmt(const char *fmt, ...)
349{
350 va_list ap;
351 int r;
352
353 INT_OFF;
354 va_start(ap, fmt);
355 r = vprintf(fmt, ap);
356 va_end(ap);
357 INT_ON;
358 return r;
359}
360
361static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
362static int
363fmtstr(char *outbuf, size_t length, const char *fmt, ...)
364{
365 va_list ap;
366 int ret;
367
368 va_start(ap, fmt);
369 INT_OFF;
370 ret = vsnprintf(outbuf, length, fmt, ap);
371 va_end(ap);
372 INT_ON;
373 return ret;
374}
375
376static void
377out1str(const char *p)
378{
379 outstr(p, stdout);
380}
381
382static void
383out2str(const char *p)
384{
385 outstr(p, stderr);
386 flush_stderr();
387}
388
389
390/* ============ Parser data
391 *
392 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
393 */
394
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000395struct strlist {
396 struct strlist *next;
397 char *text;
398};
399
Denis Vlasenkob012b102007-02-19 22:43:01 +0000400struct strpush {
401 struct strpush *prev; /* preceding string on stack */
402 char *prevstring;
403 int prevnleft;
404#if ENABLE_ASH_ALIAS
405 struct alias *ap; /* if push was associated with an alias */
406#endif
407 char *string; /* remember the string since it may change */
408};
409
410struct parsefile {
411 struct parsefile *prev; /* preceding file on stack */
412 int linno; /* current line */
413 int fd; /* file descriptor (or -1 if string) */
414 int nleft; /* number of chars left in this line */
415 int lleft; /* number of chars left in this buffer */
416 char *nextc; /* next char in buffer */
417 char *buf; /* input buffer */
418 struct strpush *strpush; /* for pushing strings at this level */
419 struct strpush basestrpush; /* so pushing one is fast */
420};
421
422static struct parsefile basepf; /* top level input file */
423static struct parsefile *parsefile = &basepf; /* current input file */
424static int startlinno; /* line # where last token started */
425static char *commandname; /* currently executing command */
426static struct strlist *cmdenviron; /* environment for builtin command */
427static int exitstatus; /* exit status of last command */
428static int back_exitstatus; /* exit status of backquoted command */
429
430
431/* ============ Message printing */
432
433static void
434ash_vmsg(const char *msg, va_list ap)
435{
436 fprintf(stderr, "%s: ", arg0);
437 if (commandname) {
438 const char *fmt = (!iflag || parsefile->fd) ?
439 "%s: %d: " : "%s: ";
440 fprintf(stderr, fmt, commandname, startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +0000441 }
Denis Vlasenkob012b102007-02-19 22:43:01 +0000442 vfprintf(stderr, msg, ap);
443 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +0000444}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000445
446/*
447 * Exverror is called to raise the error exception. If the second argument
448 * is not NULL then error prints an error message using printf style
449 * formatting. It then raises the error exception.
450 */
451static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
452static void
453ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +0000454{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000455#if DEBUG
456 if (msg) {
457 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
458 TRACEV((msg, ap));
459 TRACE(("\") pid=%d\n", getpid()));
460 } else
461 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
462 if (msg)
463#endif
464 ash_vmsg(msg, ap);
465
466 flush_stdout_stderr();
467 raise_exception(cond);
468 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +0000469}
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470
471static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
472static void
473ash_msg_and_raise_error(const char *msg, ...)
474{
475 va_list ap;
476
477 va_start(ap, msg);
478 ash_vmsg_and_raise(EXERROR, msg, ap);
479 /* NOTREACHED */
480 va_end(ap);
481}
482
483static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
484static void
485ash_msg_and_raise(int cond, const char *msg, ...)
486{
487 va_list ap;
488
489 va_start(ap, msg);
490 ash_vmsg_and_raise(cond, msg, ap);
491 /* NOTREACHED */
492 va_end(ap);
493}
494
495/*
496 * error/warning routines for external builtins
497 */
498static void
499ash_msg(const char *fmt, ...)
500{
501 va_list ap;
502
503 va_start(ap, fmt);
504 ash_vmsg(fmt, ap);
505 va_end(ap);
506}
507
508/*
509 * Return a string describing an error. The returned string may be a
510 * pointer to a static buffer that will be overwritten on the next call.
511 * Action describes the operation that got the error.
512 */
513static const char *
514errmsg(int e, const char *em)
515{
516 if (e == ENOENT || e == ENOTDIR) {
517 return em;
518 }
519 return strerror(e);
520}
521
522
Denis Vlasenko0c032a42007-02-23 01:03:40 +0000523/* ============ Memory allocation */
524
525/*
526 * It appears that grabstackstr() will barf with such alignments
527 * because stalloc() will return a string allocated in a new stackblock.
528 */
529#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
530enum {
531 /* Most machines require the value returned from malloc to be aligned
532 * in some way. The following macro will get this right
533 * on many machines. */
534 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
535 /* Minimum size of a block */
536 MINSIZE = SHELL_ALIGN(504),
537};
538
539struct stack_block {
540 struct stack_block *prev;
541 char space[MINSIZE];
542};
543
544struct stackmark {
545 struct stack_block *stackp;
546 char *stacknxt;
547 size_t stacknleft;
548 struct stackmark *marknext;
549};
550
551static struct stack_block stackbase;
552static struct stack_block *stackp = &stackbase;
553static struct stackmark *markp;
554static char *stacknxt = stackbase.space;
555static size_t stacknleft = MINSIZE;
556static char *sstrend = stackbase.space + MINSIZE;
557static int herefd = -1;
558
559#define stackblock() ((void *)stacknxt)
560#define stackblocksize() stacknleft
561
562static void *
563ckrealloc(void * p, size_t nbytes)
564{
565 p = realloc(p, nbytes);
566 if (!p)
567 ash_msg_and_raise_error(bb_msg_memory_exhausted);
568 return p;
569}
570
571static void *
572ckmalloc(size_t nbytes)
573{
574 return ckrealloc(NULL, nbytes);
575}
576
577/*
578 * Make a copy of a string in safe storage.
579 */
580static char *
581ckstrdup(const char *s)
582{
583 char *p = strdup(s);
584 if (!p)
585 ash_msg_and_raise_error(bb_msg_memory_exhausted);
586 return p;
587}
588
589/*
590 * Parse trees for commands are allocated in lifo order, so we use a stack
591 * to make this more efficient, and also to avoid all sorts of exception
592 * handling code to handle interrupts in the middle of a parse.
593 *
594 * The size 504 was chosen because the Ultrix malloc handles that size
595 * well.
596 */
597static void *
598stalloc(size_t nbytes)
599{
600 char *p;
601 size_t aligned;
602
603 aligned = SHELL_ALIGN(nbytes);
604 if (aligned > stacknleft) {
605 size_t len;
606 size_t blocksize;
607 struct stack_block *sp;
608
609 blocksize = aligned;
610 if (blocksize < MINSIZE)
611 blocksize = MINSIZE;
612 len = sizeof(struct stack_block) - MINSIZE + blocksize;
613 if (len < blocksize)
614 ash_msg_and_raise_error(bb_msg_memory_exhausted);
615 INT_OFF;
616 sp = ckmalloc(len);
617 sp->prev = stackp;
618 stacknxt = sp->space;
619 stacknleft = blocksize;
620 sstrend = stacknxt + blocksize;
621 stackp = sp;
622 INT_ON;
623 }
624 p = stacknxt;
625 stacknxt += aligned;
626 stacknleft -= aligned;
627 return p;
628}
629
630static void
631stunalloc(void *p)
632{
633#if DEBUG
634 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
635 write(2, "stunalloc\n", 10);
636 abort();
637 }
638#endif
639 stacknleft += stacknxt - (char *)p;
640 stacknxt = p;
641}
642
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000643/*
644 * Like strdup but works with the ash stack.
645 */
646static char *
647ststrdup(const char *p)
648{
649 size_t len = strlen(p) + 1;
650 return memcpy(stalloc(len), p, len);
651}
652
Denis Vlasenko0c032a42007-02-23 01:03:40 +0000653static void
654setstackmark(struct stackmark *mark)
655{
656 mark->stackp = stackp;
657 mark->stacknxt = stacknxt;
658 mark->stacknleft = stacknleft;
659 mark->marknext = markp;
660 markp = mark;
661}
662
663static void
664popstackmark(struct stackmark *mark)
665{
666 struct stack_block *sp;
667
668 INT_OFF;
669 markp = mark->marknext;
670 while (stackp != mark->stackp) {
671 sp = stackp;
672 stackp = sp->prev;
673 free(sp);
674 }
675 stacknxt = mark->stacknxt;
676 stacknleft = mark->stacknleft;
677 sstrend = mark->stacknxt + mark->stacknleft;
678 INT_ON;
679}
680
681/*
682 * When the parser reads in a string, it wants to stick the string on the
683 * stack and only adjust the stack pointer when it knows how big the
684 * string is. Stackblock (defined in stack.h) returns a pointer to a block
685 * of space on top of the stack and stackblocklen returns the length of
686 * this block. Growstackblock will grow this space by at least one byte,
687 * possibly moving it (like realloc). Grabstackblock actually allocates the
688 * part of the block that has been used.
689 */
690static void
691growstackblock(void)
692{
693 size_t newlen;
694
695 newlen = stacknleft * 2;
696 if (newlen < stacknleft)
697 ash_msg_and_raise_error(bb_msg_memory_exhausted);
698 if (newlen < 128)
699 newlen += 128;
700
701 if (stacknxt == stackp->space && stackp != &stackbase) {
702 struct stack_block *oldstackp;
703 struct stackmark *xmark;
704 struct stack_block *sp;
705 struct stack_block *prevstackp;
706 size_t grosslen;
707
708 INT_OFF;
709 oldstackp = stackp;
710 sp = stackp;
711 prevstackp = sp->prev;
712 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
713 sp = ckrealloc(sp, grosslen);
714 sp->prev = prevstackp;
715 stackp = sp;
716 stacknxt = sp->space;
717 stacknleft = newlen;
718 sstrend = sp->space + newlen;
719
720 /*
721 * Stack marks pointing to the start of the old block
722 * must be relocated to point to the new block
723 */
724 xmark = markp;
725 while (xmark != NULL && xmark->stackp == oldstackp) {
726 xmark->stackp = stackp;
727 xmark->stacknxt = stacknxt;
728 xmark->stacknleft = stacknleft;
729 xmark = xmark->marknext;
730 }
731 INT_ON;
732 } else {
733 char *oldspace = stacknxt;
734 int oldlen = stacknleft;
735 char *p = stalloc(newlen);
736
737 /* free the space we just allocated */
738 stacknxt = memcpy(p, oldspace, oldlen);
739 stacknleft += newlen;
740 }
741}
742
743static void
744grabstackblock(size_t len)
745{
746 len = SHELL_ALIGN(len);
747 stacknxt += len;
748 stacknleft -= len;
749}
750
751/*
752 * The following routines are somewhat easier to use than the above.
753 * The user declares a variable of type STACKSTR, which may be declared
754 * to be a register. The macro STARTSTACKSTR initializes things. Then
755 * the user uses the macro STPUTC to add characters to the string. In
756 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
757 * grown as necessary. When the user is done, she can just leave the
758 * string there and refer to it using stackblock(). Or she can allocate
759 * the space for it using grabstackstr(). If it is necessary to allow
760 * someone else to use the stack temporarily and then continue to grow
761 * the string, the user should use grabstack to allocate the space, and
762 * then call ungrabstr(p) to return to the previous mode of operation.
763 *
764 * USTPUTC is like STPUTC except that it doesn't check for overflow.
765 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
766 * is space for at least one character.
767 */
768static void *
769growstackstr(void)
770{
771 size_t len = stackblocksize();
772 if (herefd >= 0 && len >= 1024) {
773 full_write(herefd, stackblock(), len);
774 return stackblock();
775 }
776 growstackblock();
777 return stackblock() + len;
778}
779
780/*
781 * Called from CHECKSTRSPACE.
782 */
783static char *
784makestrspace(size_t newlen, char *p)
785{
786 size_t len = p - stacknxt;
787 size_t size = stackblocksize();
788
789 for (;;) {
790 size_t nleft;
791
792 size = stackblocksize();
793 nleft = size - len;
794 if (nleft >= newlen)
795 break;
796 growstackblock();
797 }
798 return stackblock() + len;
799}
800
801static char *
802stack_nputstr(const char *s, size_t n, char *p)
803{
804 p = makestrspace(n, p);
805 p = memcpy(p, s, n) + n;
806 return p;
807}
808
809static char *
810stack_putstr(const char *s, char *p)
811{
812 return stack_nputstr(s, strlen(s), p);
813}
814
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000815static char *
816_STPUTC(int c, char *p)
817{
818 if (p == sstrend)
819 p = growstackstr();
820 *p++ = c;
821 return p;
822}
823
824#define STARTSTACKSTR(p) ((p) = stackblock())
825#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
826#define CHECKSTRSPACE(n, p) \
827 ({ \
828 char *q = (p); \
829 size_t l = (n); \
830 size_t m = sstrend - q; \
831 if (l > m) \
832 (p) = makestrspace(l, q); \
833 0; \
834 })
835#define USTPUTC(c, p) (*p++ = (c))
836#define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
837#define STUNPUTC(p) (--p)
838#define STTOPC(p) p[-1]
839#define STADJUST(amount, p) (p += (amount))
840
841#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
842#define ungrabstackstr(s, p) stunalloc((s))
843#define stackstrend() ((void *)sstrend)
844
845
846/* ============ String helpers */
847
848/*
849 * prefix -- see if pfx is a prefix of string.
850 */
851static char *
852prefix(const char *string, const char *pfx)
853{
854 while (*pfx) {
855 if (*pfx++ != *string++)
856 return 0;
857 }
858 return (char *) string;
859}
860
861/*
862 * Check for a valid number. This should be elsewhere.
863 */
864static int
865is_number(const char *p)
866{
867 do {
868 if (!isdigit(*p))
869 return 0;
870 } while (*++p != '\0');
871 return 1;
872}
873
874/*
875 * Convert a string of digits to an integer, printing an error message on
876 * failure.
877 */
878static int
879number(const char *s)
880{
881 if (!is_number(s))
882 ash_msg_and_raise_error(illnum, s);
883 return atoi(s);
884}
885
886/*
887 * Produce a possibly single quoted string suitable as input to the shell.
888 * The return string is allocated on the stack.
889 */
890static char *
891single_quote(const char *s)
892{
893 char *p;
894
895 STARTSTACKSTR(p);
896
897 do {
898 char *q;
899 size_t len;
900
901 len = strchrnul(s, '\'') - s;
902
903 q = p = makestrspace(len + 3, p);
904
905 *q++ = '\'';
906 q = memcpy(q, s, len) + len;
907 *q++ = '\'';
908 s += len;
909
910 STADJUST(q - p, p);
911
912 len = strspn(s, "'");
913 if (!len)
914 break;
915
916 q = p = makestrspace(len + 3, p);
917
918 *q++ = '"';
919 q = memcpy(q, s, len) + len;
920 *q++ = '"';
921 s += len;
922
923 STADJUST(q - p, p);
924 } while (*s);
925
926 USTPUTC(0, p);
927
928 return stackblock();
929}
930
931
932/* ============ ... */
933
934static char **argptr; /* argument list for builtin commands */
935static char *optionarg; /* set by nextopt (like getopt) */
936static char *optptr; /* used by nextopt */
937
938/*
939 * XXX - should get rid of. have all builtins use getopt(3). the
940 * library getopt must have the BSD extension static variable "optreset"
941 * otherwise it can't be used within the shell safely.
942 *
943 * Standard option processing (a la getopt) for builtin routines. The
944 * only argument that is passed to nextopt is the option string; the
945 * other arguments are unnecessary. It return the character, or '\0' on
946 * end of input.
947 */
948static int
949nextopt(const char *optstring)
950{
951 char *p;
952 const char *q;
953 char c;
954
955 p = optptr;
956 if (p == NULL || *p == '\0') {
957 p = *argptr;
958 if (p == NULL || *p != '-' || *++p == '\0')
959 return '\0';
960 argptr++;
961 if (LONE_DASH(p)) /* check for "--" */
962 return '\0';
963 }
964 c = *p++;
965 for (q = optstring; *q != c; ) {
966 if (*q == '\0')
967 ash_msg_and_raise_error("Illegal option -%c", c);
968 if (*++q == ':')
969 q++;
970 }
971 if (*++q == ':') {
972 if (*p == '\0' && (p = *argptr++) == NULL)
973 ash_msg_and_raise_error("No arg for -%c option", c);
974 optionarg = p;
975 p = NULL;
976 }
977 optptr = p;
978 return c;
979}
980
981
982/* ============ Variables */
983
984/* flags */
985#define VEXPORT 0x01 /* variable is exported */
986#define VREADONLY 0x02 /* variable cannot be modified */
987#define VSTRFIXED 0x04 /* variable struct is statically allocated */
988#define VTEXTFIXED 0x08 /* text is statically allocated */
989#define VSTACK 0x10 /* text is allocated on the stack */
990#define VUNSET 0x20 /* the variable is not set */
991#define VNOFUNC 0x40 /* don't call the callback function */
992#define VNOSET 0x80 /* do not set variable - just readonly test */
993#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
994#ifdef DYNAMIC_VAR
995# define VDYNAMIC 0x200 /* dynamic variable */
996# else
997# define VDYNAMIC 0
998#endif
999
1000#if ENABLE_LOCALE_SUPPORT
1001static void change_lc_all(const char *value);
1002static void change_lc_ctype(const char *value);
1003#endif
1004
1005static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1006#ifdef IFS_BROKEN
1007static const char defifsvar[] = "IFS= \t\n";
1008#define defifs (defifsvar + 4)
1009#else
1010static const char defifs[] = " \t\n";
1011#endif
1012
1013struct var {
1014 struct var *next; /* next entry in hash list */
1015 int flags; /* flags are defined above */
1016 const char *text; /* name=value */
1017 void (*func)(const char *); /* function to be called when */
1018 /* the variable gets set/unset */
1019};
1020
1021struct localvar {
1022 struct localvar *next; /* next local variable in list */
1023 struct var *vp; /* the variable that was made local */
1024 int flags; /* saved flags */
1025 const char *text; /* saved text */
1026};
1027
1028/* Forward decls for varinit[] */
1029#if ENABLE_ASH_MAIL
1030static void chkmail(void);
1031static void changemail(const char *);
1032#endif
1033static void changepath(const char *);
1034#if ENABLE_ASH_GETOPTS
1035static void getoptsreset(const char *);
1036#endif
1037#if ENABLE_ASH_RANDOM_SUPPORT
1038static void change_random(const char *);
1039#endif
1040
1041static struct var varinit[] = {
1042#ifdef IFS_BROKEN
1043 { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
1044#else
1045 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
1046#endif
1047
1048#if ENABLE_ASH_MAIL
1049 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1050 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
1051#endif
1052
1053 { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1054 { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
1055 { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
1056 { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
1057#if ENABLE_ASH_GETOPTS
1058 { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
1059#endif
1060#if ENABLE_ASH_RANDOM_SUPPORT
1061 {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
1062#endif
1063#if ENABLE_LOCALE_SUPPORT
1064 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1065 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
1066#endif
1067#if ENABLE_FEATURE_EDITING_SAVEHISTORY
1068 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
1069#endif
1070};
1071
1072#define vifs varinit[0]
1073#if ENABLE_ASH_MAIL
1074#define vmail (&vifs)[1]
1075#define vmpath (&vmail)[1]
1076#else
1077#define vmpath vifs
1078#endif
1079#define vpath (&vmpath)[1]
1080#define vps1 (&vpath)[1]
1081#define vps2 (&vps1)[1]
1082#define vps4 (&vps2)[1]
1083#define voptind (&vps4)[1]
1084#if ENABLE_ASH_GETOPTS
1085#define vrandom (&voptind)[1]
1086#else
1087#define vrandom (&vps4)[1]
1088#endif
1089#define defpath (defpathvar + 5)
1090
1091/*
1092 * The following macros access the values of the above variables.
1093 * They have to skip over the name. They return the null string
1094 * for unset variables.
1095 */
1096#define ifsval() (vifs.text + 4)
1097#define ifsset() ((vifs.flags & VUNSET) == 0)
1098#define mailval() (vmail.text + 5)
1099#define mpathval() (vmpath.text + 9)
1100#define pathval() (vpath.text + 5)
1101#define ps1val() (vps1.text + 4)
1102#define ps2val() (vps2.text + 4)
1103#define ps4val() (vps4.text + 4)
1104#define optindval() (voptind.text + 7)
1105
1106#define mpathset() ((vmpath.flags & VUNSET) == 0)
1107
1108static struct var **hashvar(const char *);
1109
1110static int loopnest; /* current loop nesting level */
1111
1112/*
1113 * The parsefile structure pointed to by the global variable parsefile
1114 * contains information about the current file being read.
1115 */
1116struct redirtab {
1117 struct redirtab *next;
1118 int renamed[10];
1119 int nullredirs;
1120};
1121
1122static struct redirtab *redirlist;
1123static int nullredirs;
1124
1125extern char **environ;
1126
1127static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1128
1129struct shparam {
1130 int nparam; /* # of positional parameters (without $0) */
1131 unsigned char malloc; /* if parameter list dynamically allocated */
1132 char **p; /* parameter list */
1133#if ENABLE_ASH_GETOPTS
1134 int optind; /* next parameter to be processed by getopts */
1135 int optoff; /* used by getopts */
1136#endif
1137};
1138
1139static struct shparam shellparam; /* $@ current positional parameters */
1140
1141#define VTABSIZE 39
1142
1143static struct var *vartab[VTABSIZE];
1144
1145#if ENABLE_ASH_GETOPTS
1146static void
1147getoptsreset(const char *value)
1148{
1149 shellparam.optind = number(value);
1150 shellparam.optoff = -1;
1151}
1152#endif
1153
1154#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1155#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1156
1157/*
1158 * Return of a legal variable name (a letter or underscore followed by zero or
1159 * more letters, underscores, and digits).
1160 */
1161static char *
1162endofname(const char *name)
1163{
1164 char *p;
1165
1166 p = (char *) name;
1167 if (!is_name(*p))
1168 return p;
1169 while (*++p) {
1170 if (!is_in_name(*p))
1171 break;
1172 }
1173 return p;
1174}
1175
1176/*
1177 * Compares two strings up to the first = or '\0'. The first
1178 * string must be terminated by '='; the second may be terminated by
1179 * either '=' or '\0'.
1180 */
1181static int
1182varcmp(const char *p, const char *q)
1183{
1184 int c, d;
1185
1186 while ((c = *p) == (d = *q)) {
1187 if (!c || c == '=')
1188 goto out;
1189 p++;
1190 q++;
1191 }
1192 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001193 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001194 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001195 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001196 out:
1197 return c - d;
1198}
1199
1200static int
1201varequal(const char *a, const char *b)
1202{
1203 return !varcmp(a, b);
1204}
1205
1206/*
1207 * Find the appropriate entry in the hash table from the name.
1208 */
1209static struct var **
1210hashvar(const char *p)
1211{
1212 unsigned hashval;
1213
1214 hashval = ((unsigned char) *p) << 4;
1215 while (*p && *p != '=')
1216 hashval += (unsigned char) *p++;
1217 return &vartab[hashval % VTABSIZE];
1218}
1219
1220static int
1221vpcmp(const void *a, const void *b)
1222{
1223 return varcmp(*(const char **)a, *(const char **)b);
1224}
1225
1226/*
1227 * This routine initializes the builtin variables.
1228 */
1229static void
1230initvar(void)
1231{
1232 struct var *vp;
1233 struct var *end;
1234 struct var **vpp;
1235
1236 /*
1237 * PS1 depends on uid
1238 */
1239#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1240 vps1.text = "PS1=\\w \\$ ";
1241#else
1242 if (!geteuid())
1243 vps1.text = "PS1=# ";
1244#endif
1245 vp = varinit;
1246 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1247 do {
1248 vpp = hashvar(vp->text);
1249 vp->next = *vpp;
1250 *vpp = vp;
1251 } while (++vp < end);
1252}
1253
1254static struct var **
1255findvar(struct var **vpp, const char *name)
1256{
1257 for (; *vpp; vpp = &(*vpp)->next) {
1258 if (varequal((*vpp)->text, name)) {
1259 break;
1260 }
1261 }
1262 return vpp;
1263}
1264
1265/*
1266 * Find the value of a variable. Returns NULL if not set.
1267 */
1268static char *
1269lookupvar(const char *name)
1270{
1271 struct var *v;
1272
1273 v = *findvar(hashvar(name), name);
1274 if (v) {
1275#ifdef DYNAMIC_VAR
1276 /*
1277 * Dynamic variables are implemented roughly the same way they are
1278 * in bash. Namely, they're "special" so long as they aren't unset.
1279 * As soon as they're unset, they're no longer dynamic, and dynamic
1280 * lookup will no longer happen at that point. -- PFM.
1281 */
1282 if ((v->flags & VDYNAMIC))
1283 (*v->func)(NULL);
1284#endif
1285 if (!(v->flags & VUNSET))
1286 return strchrnul(v->text, '=') + 1;
1287 }
1288 return NULL;
1289}
1290
1291/*
1292 * Search the environment of a builtin command.
1293 */
1294static char *
1295bltinlookup(const char *name)
1296{
1297 struct strlist *sp;
1298
1299 for (sp = cmdenviron; sp; sp = sp->next) {
1300 if (varequal(sp->text, name))
1301 return strchrnul(sp->text, '=') + 1;
1302 }
1303 return lookupvar(name);
1304}
1305
1306/*
1307 * Same as setvar except that the variable and value are passed in
1308 * the first argument as name=value. Since the first argument will
1309 * be actually stored in the table, it should not be a string that
1310 * will go away.
1311 * Called with interrupts off.
1312 */
1313static void
1314setvareq(char *s, int flags)
1315{
1316 struct var *vp, **vpp;
1317
1318 vpp = hashvar(s);
1319 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1320 vp = *findvar(vpp, s);
1321 if (vp) {
1322 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
1323 const char *n;
1324
1325 if (flags & VNOSAVE)
1326 free(s);
1327 n = vp->text;
1328 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
1329 }
1330
1331 if (flags & VNOSET)
1332 return;
1333
1334 if (vp->func && (flags & VNOFUNC) == 0)
1335 (*vp->func)(strchrnul(s, '=') + 1);
1336
1337 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1338 free((char*)vp->text);
1339
1340 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
1341 } else {
1342 if (flags & VNOSET)
1343 return;
1344 /* not found */
1345 vp = ckmalloc(sizeof(*vp));
1346 vp->next = *vpp;
1347 vp->func = NULL;
1348 *vpp = vp;
1349 }
1350 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
1351 s = ckstrdup(s);
1352 vp->text = s;
1353 vp->flags = flags;
1354}
1355
1356/*
1357 * Set the value of a variable. The flags argument is ored with the
1358 * flags of the variable. If val is NULL, the variable is unset.
1359 */
1360static void
1361setvar(const char *name, const char *val, int flags)
1362{
1363 char *p, *q;
1364 size_t namelen;
1365 char *nameeq;
1366 size_t vallen;
1367
1368 q = endofname(name);
1369 p = strchrnul(q, '=');
1370 namelen = p - name;
1371 if (!namelen || p != q)
1372 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
1373 vallen = 0;
1374 if (val == NULL) {
1375 flags |= VUNSET;
1376 } else {
1377 vallen = strlen(val);
1378 }
1379 INT_OFF;
1380 nameeq = ckmalloc(namelen + vallen + 2);
1381 p = memcpy(nameeq, name, namelen) + namelen;
1382 if (val) {
1383 *p++ = '=';
1384 p = memcpy(p, val, vallen) + vallen;
1385 }
1386 *p = '\0';
1387 setvareq(nameeq, flags | VNOSAVE);
1388 INT_ON;
1389}
1390
1391#if ENABLE_ASH_GETOPTS
1392/*
1393 * Safe version of setvar, returns 1 on success 0 on failure.
1394 */
1395static int
1396setvarsafe(const char *name, const char *val, int flags)
1397{
1398 int err;
1399 volatile int saveint;
1400 struct jmploc *volatile savehandler = exception_handler;
1401 struct jmploc jmploc;
1402
1403 SAVE_INT(saveint);
1404 if (setjmp(jmploc.loc))
1405 err = 1;
1406 else {
1407 exception_handler = &jmploc;
1408 setvar(name, val, flags);
1409 err = 0;
1410 }
1411 exception_handler = savehandler;
1412 RESTORE_INT(saveint);
1413 return err;
1414}
1415#endif
1416
1417/*
1418 * Unset the specified variable.
1419 */
1420static int
1421unsetvar(const char *s)
1422{
1423 struct var **vpp;
1424 struct var *vp;
1425 int retval;
1426
1427 vpp = findvar(hashvar(s), s);
1428 vp = *vpp;
1429 retval = 2;
1430 if (vp) {
1431 int flags = vp->flags;
1432
1433 retval = 1;
1434 if (flags & VREADONLY)
1435 goto out;
1436#ifdef DYNAMIC_VAR
1437 vp->flags &= ~VDYNAMIC;
1438#endif
1439 if (flags & VUNSET)
1440 goto ok;
1441 if ((flags & VSTRFIXED) == 0) {
1442 INT_OFF;
1443 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
1444 free((char*)vp->text);
1445 *vpp = vp->next;
1446 free(vp);
1447 INT_ON;
1448 } else {
1449 setvar(s, 0, 0);
1450 vp->flags &= ~VEXPORT;
1451 }
1452 ok:
1453 retval = 0;
1454 }
1455 out:
1456 return retval;
1457}
1458
1459/*
1460 * Process a linked list of variable assignments.
1461 */
1462static void
1463listsetvar(struct strlist *list_set_var, int flags)
1464{
1465 struct strlist *lp = list_set_var;
1466
1467 if (!lp)
1468 return;
1469 INT_OFF;
1470 do {
1471 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00001472 lp = lp->next;
1473 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001474 INT_ON;
1475}
1476
1477/*
1478 * Generate a list of variables satisfying the given conditions.
1479 */
1480static char **
1481listvars(int on, int off, char ***end)
1482{
1483 struct var **vpp;
1484 struct var *vp;
1485 char **ep;
1486 int mask;
1487
1488 STARTSTACKSTR(ep);
1489 vpp = vartab;
1490 mask = on | off;
1491 do {
1492 for (vp = *vpp; vp; vp = vp->next) {
1493 if ((vp->flags & mask) == on) {
1494 if (ep == stackstrend())
1495 ep = growstackstr();
1496 *ep++ = (char *) vp->text;
1497 }
1498 }
1499 } while (++vpp < vartab + VTABSIZE);
1500 if (ep == stackstrend())
1501 ep = growstackstr();
1502 if (end)
1503 *end = ep;
1504 *ep++ = NULL;
1505 return grabstackstr(ep);
1506}
1507
1508
1509/* ============ Path search helper
1510 *
1511 * The variable path (passed by reference) should be set to the start
1512 * of the path before the first call; padvance will update
1513 * this value as it proceeds. Successive calls to padvance will return
1514 * the possible path expansions in sequence. If an option (indicated by
1515 * a percent sign) appears in the path entry then the global variable
1516 * pathopt will be set to point to it; otherwise pathopt will be set to
1517 * NULL.
1518 */
1519static const char *pathopt; /* set by padvance */
1520
1521static char *
1522padvance(const char **path, const char *name)
1523{
1524 const char *p;
1525 char *q;
1526 const char *start;
1527 size_t len;
1528
1529 if (*path == NULL)
1530 return NULL;
1531 start = *path;
1532 for (p = start; *p && *p != ':' && *p != '%'; p++);
1533 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
1534 while (stackblocksize() < len)
1535 growstackblock();
1536 q = stackblock();
1537 if (p != start) {
1538 memcpy(q, start, p - start);
1539 q += p - start;
1540 *q++ = '/';
1541 }
1542 strcpy(q, name);
1543 pathopt = NULL;
1544 if (*p == '%') {
1545 pathopt = ++p;
1546 while (*p && *p != ':') p++;
1547 }
1548 if (*p == ':')
1549 *path = p + 1;
1550 else
1551 *path = NULL;
1552 return stalloc(len);
1553}
1554
1555
1556/* ============ Prompt */
1557
1558static int doprompt; /* if set, prompt the user */
1559static int needprompt; /* true if interactive and at start of line */
1560
1561#if ENABLE_FEATURE_EDITING
1562static line_input_t *line_input_state;
1563static const char *cmdedit_prompt;
1564static void
1565putprompt(const char *s)
1566{
1567 if (ENABLE_ASH_EXPAND_PRMT) {
1568 free((char*)cmdedit_prompt);
1569 cmdedit_prompt = xstrdup(s);
1570 return;
1571 }
1572 cmdedit_prompt = s;
1573}
1574#else
1575static void
1576putprompt(const char *s)
1577{
1578 out2str(s);
1579}
1580#endif
1581
1582#if ENABLE_ASH_EXPAND_PRMT
1583/* expandstr() needs parsing machinery, so it is far away ahead... */
1584static const char *expandstr(const char *ps);
1585#else
1586#define expandstr(s) s
1587#endif
1588
1589static void
1590setprompt(int whichprompt)
1591{
1592 const char *prompt;
1593#if ENABLE_ASH_EXPAND_PRMT
1594 struct stackmark smark;
1595#endif
1596
1597 needprompt = 0;
1598
1599 switch (whichprompt) {
1600 case 1:
1601 prompt = ps1val();
1602 break;
1603 case 2:
1604 prompt = ps2val();
1605 break;
1606 default: /* 0 */
1607 prompt = nullstr;
1608 }
1609#if ENABLE_ASH_EXPAND_PRMT
1610 setstackmark(&smark);
1611 stalloc(stackblocksize());
1612#endif
1613 putprompt(expandstr(prompt));
1614#if ENABLE_ASH_EXPAND_PRMT
1615 popstackmark(&smark);
1616#endif
1617}
1618
1619
1620/* ============ The cd and pwd commands */
1621
1622#define CD_PHYSICAL 1
1623#define CD_PRINT 2
1624
1625static int docd(const char *, int);
1626
1627static char *curdir = nullstr; /* current working directory */
1628static char *physdir = nullstr; /* physical working directory */
1629
1630static int
1631cdopt(void)
1632{
1633 int flags = 0;
1634 int i, j;
1635
1636 j = 'L';
1637 while ((i = nextopt("LP"))) {
1638 if (i != j) {
1639 flags ^= CD_PHYSICAL;
1640 j = i;
1641 }
1642 }
1643
1644 return flags;
1645}
1646
1647/*
1648 * Update curdir (the name of the current directory) in response to a
1649 * cd command.
1650 */
1651static const char *
1652updatepwd(const char *dir)
1653{
1654 char *new;
1655 char *p;
1656 char *cdcomppath;
1657 const char *lim;
1658
1659 cdcomppath = ststrdup(dir);
1660 STARTSTACKSTR(new);
1661 if (*dir != '/') {
1662 if (curdir == nullstr)
1663 return 0;
1664 new = stack_putstr(curdir, new);
1665 }
1666 new = makestrspace(strlen(dir) + 2, new);
1667 lim = stackblock() + 1;
1668 if (*dir != '/') {
1669 if (new[-1] != '/')
1670 USTPUTC('/', new);
1671 if (new > lim && *lim == '/')
1672 lim++;
1673 } else {
1674 USTPUTC('/', new);
1675 cdcomppath++;
1676 if (dir[1] == '/' && dir[2] != '/') {
1677 USTPUTC('/', new);
1678 cdcomppath++;
1679 lim++;
1680 }
1681 }
1682 p = strtok(cdcomppath, "/");
1683 while (p) {
1684 switch (*p) {
1685 case '.':
1686 if (p[1] == '.' && p[2] == '\0') {
1687 while (new > lim) {
1688 STUNPUTC(new);
1689 if (new[-1] == '/')
1690 break;
1691 }
1692 break;
1693 } else if (p[1] == '\0')
1694 break;
1695 /* fall through */
1696 default:
1697 new = stack_putstr(p, new);
1698 USTPUTC('/', new);
1699 }
1700 p = strtok(0, "/");
1701 }
1702 if (new > lim)
1703 STUNPUTC(new);
1704 *new = 0;
1705 return stackblock();
1706}
1707
1708/*
1709 * Find out what the current directory is. If we already know the current
1710 * directory, this routine returns immediately.
1711 */
1712static char *
1713getpwd(void)
1714{
1715 char *dir = getcwd(0, 0);
1716 return dir ? dir : nullstr;
1717}
1718
1719static void
1720setpwd(const char *val, int setold)
1721{
1722 char *oldcur, *dir;
1723
1724 oldcur = dir = curdir;
1725
1726 if (setold) {
1727 setvar("OLDPWD", oldcur, VEXPORT);
1728 }
1729 INT_OFF;
1730 if (physdir != nullstr) {
1731 if (physdir != oldcur)
1732 free(physdir);
1733 physdir = nullstr;
1734 }
1735 if (oldcur == val || !val) {
1736 char *s = getpwd();
1737 physdir = s;
1738 if (!val)
1739 dir = s;
1740 } else
1741 dir = ckstrdup(val);
1742 if (oldcur != dir && oldcur != nullstr) {
1743 free(oldcur);
1744 }
1745 curdir = dir;
1746 INT_ON;
1747 setvar("PWD", dir, VEXPORT);
1748}
1749
1750static void hashcd(void);
1751
1752/*
1753 * Actually do the chdir. We also call hashcd to let the routines in exec.c
1754 * know that the current directory has changed.
1755 */
1756static int
1757docd(const char *dest, int flags)
1758{
1759 const char *dir = 0;
1760 int err;
1761
1762 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
1763
1764 INT_OFF;
1765 if (!(flags & CD_PHYSICAL)) {
1766 dir = updatepwd(dest);
1767 if (dir)
1768 dest = dir;
1769 }
1770 err = chdir(dest);
1771 if (err)
1772 goto out;
1773 setpwd(dir, 1);
1774 hashcd();
1775 out:
1776 INT_ON;
1777 return err;
1778}
1779
1780static int
1781cdcmd(int argc, char **argv)
1782{
1783 const char *dest;
1784 const char *path;
1785 const char *p;
1786 char c;
1787 struct stat statb;
1788 int flags;
1789
1790 flags = cdopt();
1791 dest = *argptr;
1792 if (!dest)
1793 dest = bltinlookup(homestr);
1794 else if (LONE_DASH(dest)) {
1795 dest = bltinlookup("OLDPWD");
1796 flags |= CD_PRINT;
1797 }
1798 if (!dest)
1799 dest = nullstr;
1800 if (*dest == '/')
1801 goto step7;
1802 if (*dest == '.') {
1803 c = dest[1];
1804 dotdot:
1805 switch (c) {
1806 case '\0':
1807 case '/':
1808 goto step6;
1809 case '.':
1810 c = dest[2];
1811 if (c != '.')
1812 goto dotdot;
1813 }
1814 }
1815 if (!*dest)
1816 dest = ".";
1817 path = bltinlookup("CDPATH");
1818 if (!path) {
1819 step6:
1820 step7:
1821 p = dest;
1822 goto docd;
1823 }
1824 do {
1825 c = *path;
1826 p = padvance(&path, dest);
1827 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1828 if (c && c != ':')
1829 flags |= CD_PRINT;
1830 docd:
1831 if (!docd(p, flags))
1832 goto out;
1833 break;
1834 }
1835 } while (path);
1836 ash_msg_and_raise_error("can't cd to %s", dest);
1837 /* NOTREACHED */
1838 out:
1839 if (flags & CD_PRINT)
1840 out1fmt(snlfmt, curdir);
1841 return 0;
1842}
1843
1844static int
1845pwdcmd(int argc, char **argv)
1846{
1847 int flags;
1848 const char *dir = curdir;
1849
1850 flags = cdopt();
1851 if (flags) {
1852 if (physdir == nullstr)
1853 setpwd(dir, 0);
1854 dir = physdir;
1855 }
1856 out1fmt(snlfmt, dir);
1857 return 0;
1858}
1859
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001860
Denis Vlasenkob012b102007-02-19 22:43:01 +00001861/* ============ Unsorted yet */
1862
1863
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00001864/* expand.h */
Eric Andersen2870d962001-07-02 17:27:21 +00001865
Eric Andersen2870d962001-07-02 17:27:21 +00001866struct arglist {
1867 struct strlist *list;
1868 struct strlist **lastp;
1869};
1870
Eric Andersenc470f442003-07-28 09:56:35 +00001871/*
1872 * expandarg() flags
1873 */
1874#define EXP_FULL 0x1 /* perform word splitting & file globbing */
1875#define EXP_TILDE 0x2 /* do normal tilde expansion */
1876#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
1877#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
1878#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
1879#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
1880#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
1881#define EXP_WORD 0x80 /* expand word in parameter expansion */
1882#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
1883
1884
1885union node;
1886static void expandarg(union node *, struct arglist *, int);
1887#define rmescapes(p) _rmescapes((p), 0)
1888static char *_rmescapes(char *, int);
1889static int casematch(union node *, char *);
1890
Denis Vlasenko131ae172007-02-18 13:00:19 +00001891#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00001892static void expari(int);
Eric Andersen2870d962001-07-02 17:27:21 +00001893#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001894
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00001895/* eval.h */
Eric Andersen2870d962001-07-02 17:27:21 +00001896
Eric Andersen2870d962001-07-02 17:27:21 +00001897
Eric Andersenc470f442003-07-28 09:56:35 +00001898
1899struct backcmd { /* result of evalbackcmd */
1900 int fd; /* file descriptor to read from */
1901 char *buf; /* buffer */
1902 int nleft; /* number of chars in buffer */
1903 struct job *jp; /* job structure for command */
Eric Andersen2870d962001-07-02 17:27:21 +00001904};
1905
Eric Andersen62483552001-07-10 06:09:16 +00001906/*
Eric Andersenc470f442003-07-28 09:56:35 +00001907 * This file was generated by the mknodes program.
Eric Andersen62483552001-07-10 06:09:16 +00001908 */
Eric Andersenc470f442003-07-28 09:56:35 +00001909
1910#define NCMD 0
1911#define NPIPE 1
1912#define NREDIR 2
1913#define NBACKGND 3
1914#define NSUBSHELL 4
1915#define NAND 5
1916#define NOR 6
1917#define NSEMI 7
1918#define NIF 8
1919#define NWHILE 9
1920#define NUNTIL 10
1921#define NFOR 11
1922#define NCASE 12
1923#define NCLIST 13
1924#define NDEFUN 14
1925#define NARG 15
1926#define NTO 16
1927#define NCLOBBER 17
1928#define NFROM 18
1929#define NFROMTO 19
1930#define NAPPEND 20
1931#define NTOFD 21
1932#define NFROMFD 22
1933#define NHERE 23
1934#define NXHERE 24
1935#define NNOT 25
Eric Andersen62483552001-07-10 06:09:16 +00001936
1937
Eric Andersenc470f442003-07-28 09:56:35 +00001938struct ncmd {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001939 int type;
1940 union node *assign;
1941 union node *args;
1942 union node *redirect;
Eric Andersen62483552001-07-10 06:09:16 +00001943};
1944
Eric Andersenc470f442003-07-28 09:56:35 +00001945struct npipe {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001946 int type;
1947 int backgnd;
1948 struct nodelist *cmdlist;
Eric Andersenc470f442003-07-28 09:56:35 +00001949};
Eric Andersen62483552001-07-10 06:09:16 +00001950
Eric Andersenc470f442003-07-28 09:56:35 +00001951struct nredir {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001952 int type;
1953 union node *n;
1954 union node *redirect;
Eric Andersenc470f442003-07-28 09:56:35 +00001955};
Eric Andersen62483552001-07-10 06:09:16 +00001956
Eric Andersenc470f442003-07-28 09:56:35 +00001957struct nbinary {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001958 int type;
1959 union node *ch1;
1960 union node *ch2;
Eric Andersenc470f442003-07-28 09:56:35 +00001961};
Eric Andersen2870d962001-07-02 17:27:21 +00001962
Eric Andersenc470f442003-07-28 09:56:35 +00001963struct nif {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001964 int type;
1965 union node *test;
1966 union node *ifpart;
1967 union node *elsepart;
Eric Andersenc470f442003-07-28 09:56:35 +00001968};
1969
Eric Andersenc470f442003-07-28 09:56:35 +00001970struct nfor {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001971 int type;
1972 union node *args;
1973 union node *body;
1974 char *var;
Eric Andersenc470f442003-07-28 09:56:35 +00001975};
1976
Eric Andersenc470f442003-07-28 09:56:35 +00001977struct ncase {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001978 int type;
1979 union node *expr;
1980 union node *cases;
Eric Andersenc470f442003-07-28 09:56:35 +00001981};
1982
Eric Andersenc470f442003-07-28 09:56:35 +00001983struct nclist {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001984 int type;
1985 union node *next;
1986 union node *pattern;
1987 union node *body;
Eric Andersenc470f442003-07-28 09:56:35 +00001988};
1989
Eric Andersenc470f442003-07-28 09:56:35 +00001990struct narg {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001991 int type;
1992 union node *next;
1993 char *text;
1994 struct nodelist *backquote;
Eric Andersenc470f442003-07-28 09:56:35 +00001995};
1996
Eric Andersenc470f442003-07-28 09:56:35 +00001997struct nfile {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00001998 int type;
1999 union node *next;
2000 int fd;
2001 union node *fname;
2002 char *expfname;
Eric Andersenc470f442003-07-28 09:56:35 +00002003};
2004
Eric Andersenc470f442003-07-28 09:56:35 +00002005struct ndup {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002006 int type;
2007 union node *next;
2008 int fd;
2009 int dupfd;
2010 union node *vname;
Eric Andersenc470f442003-07-28 09:56:35 +00002011};
2012
Eric Andersenc470f442003-07-28 09:56:35 +00002013struct nhere {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002014 int type;
2015 union node *next;
2016 int fd;
2017 union node *doc;
Eric Andersenc470f442003-07-28 09:56:35 +00002018};
2019
Eric Andersenc470f442003-07-28 09:56:35 +00002020struct nnot {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002021 int type;
2022 union node *com;
Eric Andersenc470f442003-07-28 09:56:35 +00002023};
2024
Eric Andersenc470f442003-07-28 09:56:35 +00002025union node {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002026 int type;
2027 struct ncmd ncmd;
2028 struct npipe npipe;
2029 struct nredir nredir;
2030 struct nbinary nbinary;
2031 struct nif nif;
2032 struct nfor nfor;
2033 struct ncase ncase;
2034 struct nclist nclist;
2035 struct narg narg;
2036 struct nfile nfile;
2037 struct ndup ndup;
2038 struct nhere nhere;
2039 struct nnot nnot;
Eric Andersenc470f442003-07-28 09:56:35 +00002040};
2041
Eric Andersenc470f442003-07-28 09:56:35 +00002042struct nodelist {
2043 struct nodelist *next;
2044 union node *n;
2045};
2046
Eric Andersenc470f442003-07-28 09:56:35 +00002047struct funcnode {
2048 int count;
2049 union node n;
2050};
2051
2052
2053static void freefunc(struct funcnode *);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002054/* parser.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002055
2056/* control characters in argument strings */
2057#define CTL_FIRST '\201' /* first 'special' character */
2058#define CTLESC '\201' /* escape next character */
2059#define CTLVAR '\202' /* variable defn */
2060#define CTLENDVAR '\203'
2061#define CTLBACKQ '\204'
2062#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
2063/* CTLBACKQ | CTLQUOTE == '\205' */
2064#define CTLARI '\206' /* arithmetic expression */
2065#define CTLENDARI '\207'
2066#define CTLQUOTEMARK '\210'
2067#define CTL_LAST '\210' /* last 'special' character */
2068
2069/* variable substitution byte (follows CTLVAR) */
2070#define VSTYPE 0x0f /* type of variable substitution */
2071#define VSNUL 0x10 /* colon--treat the empty string as unset */
2072#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
2073
2074/* values of VSTYPE field */
2075#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
2076#define VSMINUS 0x2 /* ${var-text} */
2077#define VSPLUS 0x3 /* ${var+text} */
2078#define VSQUESTION 0x4 /* ${var?message} */
2079#define VSASSIGN 0x5 /* ${var=text} */
2080#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
2081#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
2082#define VSTRIMLEFT 0x8 /* ${var#pattern} */
2083#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
2084#define VSLENGTH 0xa /* ${#var} */
2085
2086/* values of checkkwd variable */
2087#define CHKALIAS 0x1
2088#define CHKKWD 0x2
2089#define CHKNL 0x4
2090
2091#define IBUFSIZ (BUFSIZ + 1)
2092
2093/*
2094 * NEOF is returned by parsecmd when it encounters an end of file. It
2095 * must be distinct from NULL, so we use the address of a variable that
2096 * happens to be handy.
2097 */
2098static int plinno = 1; /* input line number */
2099
2100/* number of characters left in input buffer */
2101static int parsenleft; /* copy of parsefile->nleft */
2102static int parselleft; /* copy of parsefile->lleft */
2103
2104/* next character in input buffer */
Eric Andersen90898442003-08-06 11:20:52 +00002105static char *parsenextc; /* copy of parsefile->nextc */
Glenn L McGrathd3612172003-09-17 00:22:26 +00002106
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002107#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002108
2109
2110static int tokpushback; /* last token pushed back */
2111#define NEOF ((union node *)&tokpushback)
2112static int parsebackquote; /* nonzero if we are inside backquotes */
Eric Andersenc470f442003-07-28 09:56:35 +00002113static int lasttoken; /* last token read */
2114static char *wordtext; /* text of last word returned by readtoken */
2115static int checkkwd;
2116static struct nodelist *backquotelist;
2117static union node *redirnode;
2118static struct heredoc *heredoc;
2119static int quoteflag; /* set if (part of) last token was quoted */
Eric Andersenc470f442003-07-28 09:56:35 +00002120
Eric Andersenc470f442003-07-28 09:56:35 +00002121static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00002122static char *endofname(const char *);
2123
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002124/* shell.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002125
Eric Andersenc470f442003-07-28 09:56:35 +00002126static const char spcstr[] = " ";
Eric Andersenc470f442003-07-28 09:56:35 +00002127static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
Eric Andersen2870d962001-07-02 17:27:21 +00002128
Eric Andersenc470f442003-07-28 09:56:35 +00002129#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
2130#define __builtin_expect(x, expected_value) (x)
2131#endif
2132
Glenn L McGrath2f325a02004-08-06 01:49:04 +00002133#define xlikely(x) __builtin_expect((x),1)
Eric Andersen90898442003-08-06 11:20:52 +00002134
Eric Andersenc470f442003-07-28 09:56:35 +00002135
2136#define TEOF 0
2137#define TNL 1
2138#define TREDIR 2
2139#define TWORD 3
2140#define TSEMI 4
2141#define TBACKGND 5
2142#define TAND 6
2143#define TOR 7
2144#define TPIPE 8
2145#define TLP 9
2146#define TRP 10
2147#define TENDCASE 11
2148#define TENDBQUOTE 12
2149#define TNOT 13
2150#define TCASE 14
2151#define TDO 15
2152#define TDONE 16
2153#define TELIF 17
2154#define TELSE 18
2155#define TESAC 19
2156#define TFI 20
2157#define TFOR 21
2158#define TIF 22
2159#define TIN 23
2160#define TTHEN 24
2161#define TUNTIL 25
2162#define TWHILE 26
2163#define TBEGIN 27
2164#define TEND 28
2165
2166/* first char is indicating which tokens mark the end of a list */
2167static const char *const tokname_array[] = {
2168 "\1end of file",
2169 "\0newline",
2170 "\0redirection",
2171 "\0word",
2172 "\0;",
2173 "\0&",
2174 "\0&&",
2175 "\0||",
2176 "\0|",
2177 "\0(",
2178 "\1)",
2179 "\1;;",
2180 "\1`",
2181#define KWDOFFSET 13
2182 /* the following are keywords */
2183 "\0!",
2184 "\0case",
2185 "\1do",
2186 "\1done",
2187 "\1elif",
2188 "\1else",
2189 "\1esac",
2190 "\1fi",
2191 "\0for",
2192 "\0if",
2193 "\0in",
2194 "\1then",
2195 "\0until",
2196 "\0while",
2197 "\0{",
2198 "\1}",
2199};
2200
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002201static const char *
2202tokname(int tok)
Eric Andersenc470f442003-07-28 09:56:35 +00002203{
2204 static char buf[16];
2205
2206 if (tok >= TSEMI)
2207 buf[0] = '"';
2208 sprintf(buf + (tok >= TSEMI), "%s%c",
2209 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
2210 return buf;
2211}
2212
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002213/* Wrapper around strcmp for qsort/bsearch/... */
2214static int
2215pstrcmp(const void *a, const void *b)
2216{
2217 return strcmp((const char *) a, (*(const char *const *) b) + 1);
2218}
Eric Andersenc470f442003-07-28 09:56:35 +00002219
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002220static const char *const *
2221findkwd(const char *s)
2222{
2223 return bsearch(s, tokname_array + KWDOFFSET,
2224 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
2225 sizeof(const char *), pstrcmp);
2226}
Eric Andersenc470f442003-07-28 09:56:35 +00002227
2228/* Syntax classes */
2229#define CWORD 0 /* character is nothing special */
2230#define CNL 1 /* newline character */
2231#define CBACK 2 /* a backslash character */
2232#define CSQUOTE 3 /* single quote */
2233#define CDQUOTE 4 /* double quote */
2234#define CENDQUOTE 5 /* a terminating quote */
2235#define CBQUOTE 6 /* backwards single quote */
2236#define CVAR 7 /* a dollar sign */
2237#define CENDVAR 8 /* a '}' character */
2238#define CLP 9 /* a left paren in arithmetic */
2239#define CRP 10 /* a right paren in arithmetic */
2240#define CENDFILE 11 /* end of file */
2241#define CCTL 12 /* like CWORD, except it must be escaped */
2242#define CSPCL 13 /* these terminate a word */
2243#define CIGN 14 /* character should be ignored */
2244
Denis Vlasenko131ae172007-02-18 13:00:19 +00002245#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002246#define SYNBASE 130
2247#define PEOF -130
2248#define PEOA -129
2249#define PEOA_OR_PEOF PEOA
2250#else
2251#define SYNBASE 129
2252#define PEOF -129
2253#define PEOA_OR_PEOF PEOF
2254#endif
2255
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00002256/* C99 say: "char" declaration may be signed or unsigned default */
2257#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
2258
Eric Andersenc470f442003-07-28 09:56:35 +00002259/*
2260 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
2261 * (assuming ascii char codes, as the original implementation did)
2262 */
2263#define is_special(c) \
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002264 ( (((unsigned int)c) - 33 < 32) \
Eric Andersenc470f442003-07-28 09:56:35 +00002265 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
2266
2267#define digit_val(c) ((c) - '0')
2268
2269/*
2270 * This file was generated by the mksyntax program.
2271 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002272
Denis Vlasenko131ae172007-02-18 13:00:19 +00002273#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002274#define USE_SIT_FUNCTION
2275#endif
2276
2277/* number syntax index */
Eric Andersenc470f442003-07-28 09:56:35 +00002278#define BASESYNTAX 0 /* not in quotes */
2279#define DQSYNTAX 1 /* in double quotes */
2280#define SQSYNTAX 2 /* in single quotes */
2281#define ARISYNTAX 3 /* in arithmetic */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002282
Denis Vlasenko131ae172007-02-18 13:00:19 +00002283#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002284static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002285#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002286 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
2287#endif
2288 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
2289 {CNL, CNL, CNL, CNL}, /* 2, \n */
2290 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
2291 {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
2292 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
2293 {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
2294 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
2295 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
2296 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
2297 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
2298 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002299#ifndef USE_SIT_FUNCTION
Eric Andersenc470f442003-07-28 09:56:35 +00002300 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
2301 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
2302 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002303#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002304};
Eric Andersenc470f442003-07-28 09:56:35 +00002305#else
2306static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002307#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002308 {CSPCL, CIGN, CIGN}, /* 0, PEOA */
2309#endif
2310 {CSPCL, CWORD, CWORD}, /* 1, ' ' */
2311 {CNL, CNL, CNL}, /* 2, \n */
2312 {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
2313 {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
2314 {CVAR, CVAR, CWORD}, /* 5, $ */
2315 {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
2316 {CSPCL, CWORD, CWORD}, /* 7, ( */
2317 {CSPCL, CWORD, CWORD}, /* 8, ) */
2318 {CBACK, CBACK, CCTL}, /* 9, \ */
2319 {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
2320 {CENDVAR, CENDVAR, CWORD}, /* 11, } */
2321#ifndef USE_SIT_FUNCTION
2322 {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
2323 {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
2324 {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
2325#endif
2326};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002327#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002328
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002329#ifdef USE_SIT_FUNCTION
2330
2331#define U_C(c) ((unsigned char)(c))
2332
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002333static int
2334SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002335{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002336 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002337#if ENABLE_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002338 static const char syntax_index_table[] = {
Eric Andersenc470f442003-07-28 09:56:35 +00002339 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2340 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2341 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2342 11, 3 /* "}~" */
2343 };
2344#else
2345 static const char syntax_index_table[] = {
2346 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2347 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2348 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2349 10, 2 /* "}~" */
2350 };
2351#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002352 const char *s;
2353 int indx;
2354
Eric Andersenc470f442003-07-28 09:56:35 +00002355 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002356 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002357#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002358 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002359 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002360 else
2361#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002362 if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
2363 return CCTL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002364 else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002365 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002366 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002367 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002368 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002369 }
2370 return S_I_T[indx][syntax];
2371}
2372
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00002373#else /* USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002374
2375#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
2376
Denis Vlasenko131ae172007-02-18 13:00:19 +00002377#if ENABLE_ASH_ALIAS
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002378#define CSPCL_CIGN_CIGN_CIGN 0
2379#define CSPCL_CWORD_CWORD_CWORD 1
2380#define CNL_CNL_CNL_CNL 2
2381#define CWORD_CCTL_CCTL_CWORD 3
Eric Andersenc470f442003-07-28 09:56:35 +00002382#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002383#define CVAR_CVAR_CWORD_CVAR 5
Eric Andersenc470f442003-07-28 09:56:35 +00002384#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002385#define CSPCL_CWORD_CWORD_CLP 7
2386#define CSPCL_CWORD_CWORD_CRP 8
2387#define CBACK_CBACK_CCTL_CBACK 9
2388#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2389#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2390#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2391#define CWORD_CWORD_CWORD_CWORD 13
2392#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002393#else
2394#define CSPCL_CWORD_CWORD_CWORD 0
2395#define CNL_CNL_CNL_CNL 1
2396#define CWORD_CCTL_CCTL_CWORD 2
2397#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2398#define CVAR_CVAR_CWORD_CVAR 4
2399#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2400#define CSPCL_CWORD_CWORD_CLP 6
2401#define CSPCL_CWORD_CWORD_CRP 7
2402#define CBACK_CBACK_CCTL_CBACK 8
2403#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2404#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2405#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2406#define CWORD_CWORD_CWORD_CWORD 12
2407#define CCTL_CCTL_CCTL_CCTL 13
2408#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002409
2410static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002411 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002412 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002413#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002414 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2415#endif
2416 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2417 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2418 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2419 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2420 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2421 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2422 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2423 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2424 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002425 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2426 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2427 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2428 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2429 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2430 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2431 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2432 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2433 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2434 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2435 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2436 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2437 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2438 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2439 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2440 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2441 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2442 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2443 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2444 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2445 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2446 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2447 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2448 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2449 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2450 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2451 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2452 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2453 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2454 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2455 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2456 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2457 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2458 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2459 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2460 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2461 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2462 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2463 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2464 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2465 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2466 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2467 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2468 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2469 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2470 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2471 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2472 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2473 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2474 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2475 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2476 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2477 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2478 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2479 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2480 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2481 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2482 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2483 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2484 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2485 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2486 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2487 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2488 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2489 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2490 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2491 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2492 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2493 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2494 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2495 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2496 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2497 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2498 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2499 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2500 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2501 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2502 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2503 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2504 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2505 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2506 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2507 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2508 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2509 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2510 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2511 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2512 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2513 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2514 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2515 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2516 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2517 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2518 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2519 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2520 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2521 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2522 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2523 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2524 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2525 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2526 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2527 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2528 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2529 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2530 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2531 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2532 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2533 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2534 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2535 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2536 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2537 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2538 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2539 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2540 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2541 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2542 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2543 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2544 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2545 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2546 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2547 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2548 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2549 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2550 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2551 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2552 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2553 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2554 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2555 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2556 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2557 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2558 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2559 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2560 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2561 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2562 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2563 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2564 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2565 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2566 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2567 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2568 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2569 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2570 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2571 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2572 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2573 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2574 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2575 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2576 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2577 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002578 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002579 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2580 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2581 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2582 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002583 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002584 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2585 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2586 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2587 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2588 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2589 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2590 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2591 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2592 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2593 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2594 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2595 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2596 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2597 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2598 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2599 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2600 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2601 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2602 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2603 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2604 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2605 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2606 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2607 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2608 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2609 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2610 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2611 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2612 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2613 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2614 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2615 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2616 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2617 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2618 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2619 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2620 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2621 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2622 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2623 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2624 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2625 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2626 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2627 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2628 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2629 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2630 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2631 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2632 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2633 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2634 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2635 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2636 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2637 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2638 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2639 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2640 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2641 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2642 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2643 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2644 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2645 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2646 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2647 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2648 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2649 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2650 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2651 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2652 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2653 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2654 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2655 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2656 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2657 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2658 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2659 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2660 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2661 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2662 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2663 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2664 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2665 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2666 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2667 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2668 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2669 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2670 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2671 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002672};
2673
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002674#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002675
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002676/* alias.c */
Eric Andersen2870d962001-07-02 17:27:21 +00002677
Eric Andersen2870d962001-07-02 17:27:21 +00002678
Eric Andersenc470f442003-07-28 09:56:35 +00002679#define ATABSIZE 39
2680
2681static int funcblocksize; /* size of structures in function */
2682static int funcstringsize; /* size of strings in node */
Denis Vlasenkoa624c112007-02-19 22:45:43 +00002683static void *funcblock; /* block to allocate function from */
Eric Andersenc470f442003-07-28 09:56:35 +00002684static char *funcstring; /* block to allocate strings from */
2685
2686static const short nodesize[26] = {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002687 SHELL_ALIGN(sizeof(struct ncmd)),
2688 SHELL_ALIGN(sizeof(struct npipe)),
2689 SHELL_ALIGN(sizeof(struct nredir)),
2690 SHELL_ALIGN(sizeof(struct nredir)),
2691 SHELL_ALIGN(sizeof(struct nredir)),
2692 SHELL_ALIGN(sizeof(struct nbinary)),
2693 SHELL_ALIGN(sizeof(struct nbinary)),
2694 SHELL_ALIGN(sizeof(struct nbinary)),
2695 SHELL_ALIGN(sizeof(struct nif)),
2696 SHELL_ALIGN(sizeof(struct nbinary)),
2697 SHELL_ALIGN(sizeof(struct nbinary)),
2698 SHELL_ALIGN(sizeof(struct nfor)),
2699 SHELL_ALIGN(sizeof(struct ncase)),
2700 SHELL_ALIGN(sizeof(struct nclist)),
2701 SHELL_ALIGN(sizeof(struct narg)),
2702 SHELL_ALIGN(sizeof(struct narg)),
2703 SHELL_ALIGN(sizeof(struct nfile)),
2704 SHELL_ALIGN(sizeof(struct nfile)),
2705 SHELL_ALIGN(sizeof(struct nfile)),
2706 SHELL_ALIGN(sizeof(struct nfile)),
2707 SHELL_ALIGN(sizeof(struct nfile)),
2708 SHELL_ALIGN(sizeof(struct ndup)),
2709 SHELL_ALIGN(sizeof(struct ndup)),
2710 SHELL_ALIGN(sizeof(struct nhere)),
2711 SHELL_ALIGN(sizeof(struct nhere)),
2712 SHELL_ALIGN(sizeof(struct nnot)),
Eric Andersen2870d962001-07-02 17:27:21 +00002713};
2714
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002715
Eric Andersenc470f442003-07-28 09:56:35 +00002716static void calcsize(union node *);
2717static void sizenodelist(struct nodelist *);
2718static union node *copynode(union node *);
2719static struct nodelist *copynodelist(struct nodelist *);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002720static char *nodeckstrdup(char *);
Eric Andersen2870d962001-07-02 17:27:21 +00002721
2722
Eric Andersenc470f442003-07-28 09:56:35 +00002723static int evalskip; /* set if we are skipping commands */
2724static int skipcount; /* number of levels to skip */
2725static int funcnest; /* depth of function calls */
Eric Andersen2870d962001-07-02 17:27:21 +00002726
2727/* reasons for skipping commands (see comment on breakcmd routine) */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002728#define SKIPBREAK (1 << 0)
2729#define SKIPCONT (1 << 1)
2730#define SKIPFUNC (1 << 2)
2731#define SKIPFILE (1 << 3)
2732#define SKIPEVAL (1 << 4)
Eric Andersen2870d962001-07-02 17:27:21 +00002733
Eric Andersenc470f442003-07-28 09:56:35 +00002734/*
2735 * This file was generated by the mkbuiltins program.
2736 */
Eric Andersen2870d962001-07-02 17:27:21 +00002737
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002738#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002739static int bgcmd(int, char **);
2740#endif
2741static int breakcmd(int, char **);
2742static int cdcmd(int, char **);
Denis Vlasenko131ae172007-02-18 13:00:19 +00002743#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00002744static int commandcmd(int, char **);
2745#endif
2746static int dotcmd(int, char **);
2747static int evalcmd(int, char **);
Denis Vlasenko131ae172007-02-18 13:00:19 +00002748#if ENABLE_ASH_BUILTIN_ECHO
Paul Fox0b621582005-08-09 19:38:05 +00002749static int echocmd(int, char **);
2750#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002751#if ENABLE_ASH_BUILTIN_TEST
Paul Fox6ab03782006-06-08 21:37:26 +00002752static int testcmd(int, char **);
2753#endif
Eric Andersenc470f442003-07-28 09:56:35 +00002754static int execcmd(int, char **);
2755static int exitcmd(int, char **);
Eric Andersenc470f442003-07-28 09:56:35 +00002756static int exportcmd(int, char **);
2757static int falsecmd(int, char **);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002758#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002759static int fgcmd(int, char **);
2760#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002761#if ENABLE_ASH_GETOPTS
Eric Andersenc470f442003-07-28 09:56:35 +00002762static int getoptscmd(int, char **);
2763#endif
2764static int hashcmd(int, char **);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00002765#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Eric Andersenc470f442003-07-28 09:56:35 +00002766static int helpcmd(int argc, char **argv);
2767#endif
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002768#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002769static int jobscmd(int, char **);
2770#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002771#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +00002772static int letcmd(int, char **);
2773#endif
Eric Andersenc470f442003-07-28 09:56:35 +00002774static int localcmd(int, char **);
2775static int pwdcmd(int, char **);
2776static int readcmd(int, char **);
2777static int returncmd(int, char **);
2778static int setcmd(int, char **);
2779static int shiftcmd(int, char **);
2780static int timescmd(int, char **);
2781static int trapcmd(int, char **);
2782static int truecmd(int, char **);
2783static int typecmd(int, char **);
2784static int umaskcmd(int, char **);
2785static int unsetcmd(int, char **);
2786static int waitcmd(int, char **);
2787static int ulimitcmd(int, char **);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002788#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002789static int killcmd(int, char **);
2790#endif
2791
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002792/* exec.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002793
2794/* values of cmdtype */
2795#define CMDUNKNOWN -1 /* no entry in table for command */
2796#define CMDNORMAL 0 /* command is an executable program */
2797#define CMDFUNCTION 1 /* command is a shell function */
2798#define CMDBUILTIN 2 /* command is a shell builtin */
2799
2800struct builtincmd {
2801 const char *name;
2802 int (*builtin)(int, char **);
2803 /* unsigned flags; */
2804};
2805
Paul Fox0b621582005-08-09 19:38:05 +00002806
2807#define COMMANDCMD (builtincmd + 5 + \
Paul Fox6ab03782006-06-08 21:37:26 +00002808 2 * ENABLE_ASH_BUILTIN_TEST + \
2809 ENABLE_ASH_ALIAS + \
2810 ENABLE_ASH_JOB_CONTROL)
Paul Fox0b621582005-08-09 19:38:05 +00002811#define EXECCMD (builtincmd + 7 + \
Paul Fox6ab03782006-06-08 21:37:26 +00002812 2 * ENABLE_ASH_BUILTIN_TEST + \
2813 ENABLE_ASH_ALIAS + \
2814 ENABLE_ASH_JOB_CONTROL + \
2815 ENABLE_ASH_CMDCMD + \
2816 ENABLE_ASH_BUILTIN_ECHO)
Eric Andersenc470f442003-07-28 09:56:35 +00002817
2818#define BUILTIN_NOSPEC "0"
2819#define BUILTIN_SPECIAL "1"
2820#define BUILTIN_REGULAR "2"
2821#define BUILTIN_SPEC_REG "3"
2822#define BUILTIN_ASSIGN "4"
2823#define BUILTIN_SPEC_ASSG "5"
2824#define BUILTIN_REG_ASSG "6"
2825#define BUILTIN_SPEC_REG_ASSG "7"
2826
2827#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
2828#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
Paul Foxc3850c82005-07-20 18:23:39 +00002829#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
Eric Andersenc470f442003-07-28 09:56:35 +00002830
Bernhard Reutner-Fischer35492132006-06-21 18:19:53 +00002831/* make sure to keep these in proper order since it is searched via bsearch() */
Eric Andersenc470f442003-07-28 09:56:35 +00002832static const struct builtincmd builtincmd[] = {
2833 { BUILTIN_SPEC_REG ".", dotcmd },
2834 { BUILTIN_SPEC_REG ":", truecmd },
Denis Vlasenko131ae172007-02-18 13:00:19 +00002835#if ENABLE_ASH_BUILTIN_TEST
Paul Fox6ab03782006-06-08 21:37:26 +00002836 { BUILTIN_REGULAR "[", testcmd },
2837 { BUILTIN_REGULAR "[[", testcmd },
2838#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002839#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002840 { BUILTIN_REG_ASSG "alias", aliascmd },
2841#endif
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002842#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002843 { BUILTIN_REGULAR "bg", bgcmd },
2844#endif
2845 { BUILTIN_SPEC_REG "break", breakcmd },
2846 { BUILTIN_REGULAR "cd", cdcmd },
2847 { BUILTIN_NOSPEC "chdir", cdcmd },
Denis Vlasenko131ae172007-02-18 13:00:19 +00002848#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00002849 { BUILTIN_REGULAR "command", commandcmd },
2850#endif
2851 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko131ae172007-02-18 13:00:19 +00002852#if ENABLE_ASH_BUILTIN_ECHO
Paul Fox0b621582005-08-09 19:38:05 +00002853 { BUILTIN_REGULAR "echo", echocmd },
2854#endif
Eric Andersenc470f442003-07-28 09:56:35 +00002855 { BUILTIN_SPEC_REG "eval", evalcmd },
2856 { BUILTIN_SPEC_REG "exec", execcmd },
2857 { BUILTIN_SPEC_REG "exit", exitcmd },
Eric Andersenc470f442003-07-28 09:56:35 +00002858 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
2859 { BUILTIN_REGULAR "false", falsecmd },
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002860#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002861 { BUILTIN_REGULAR "fg", fgcmd },
2862#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002863#if ENABLE_ASH_GETOPTS
Eric Andersenc470f442003-07-28 09:56:35 +00002864 { BUILTIN_REGULAR "getopts", getoptscmd },
2865#endif
2866 { BUILTIN_NOSPEC "hash", hashcmd },
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00002867#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Eric Andersenc470f442003-07-28 09:56:35 +00002868 { BUILTIN_NOSPEC "help", helpcmd },
2869#endif
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002870#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00002871 { BUILTIN_REGULAR "jobs", jobscmd },
2872 { BUILTIN_REGULAR "kill", killcmd },
2873#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +00002874#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +00002875 { BUILTIN_NOSPEC "let", letcmd },
Eric Andersenc470f442003-07-28 09:56:35 +00002876#endif
2877 { BUILTIN_ASSIGN "local", localcmd },
2878 { BUILTIN_NOSPEC "pwd", pwdcmd },
2879 { BUILTIN_REGULAR "read", readcmd },
2880 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
2881 { BUILTIN_SPEC_REG "return", returncmd },
2882 { BUILTIN_SPEC_REG "set", setcmd },
2883 { BUILTIN_SPEC_REG "shift", shiftcmd },
Mike Frysingerc2ad4f52006-06-21 18:04:49 +00002884 { BUILTIN_SPEC_REG "source", dotcmd },
Denis Vlasenko131ae172007-02-18 13:00:19 +00002885#if ENABLE_ASH_BUILTIN_TEST
Paul Fox6ab03782006-06-08 21:37:26 +00002886 { BUILTIN_REGULAR "test", testcmd },
2887#endif
Eric Andersenc470f442003-07-28 09:56:35 +00002888 { BUILTIN_SPEC_REG "times", timescmd },
2889 { BUILTIN_SPEC_REG "trap", trapcmd },
2890 { BUILTIN_REGULAR "true", truecmd },
2891 { BUILTIN_NOSPEC "type", typecmd },
2892 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
2893 { BUILTIN_REGULAR "umask", umaskcmd },
Denis Vlasenko131ae172007-02-18 13:00:19 +00002894#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002895 { BUILTIN_REGULAR "unalias", unaliascmd },
2896#endif
2897 { BUILTIN_SPEC_REG "unset", unsetcmd },
2898 { BUILTIN_REGULAR "wait", waitcmd },
2899};
2900
Denis Vlasenko8f27c342006-12-26 21:31:11 +00002901#define NUMBUILTINS (sizeof(builtincmd) / sizeof(builtincmd[0]))
Eric Andersenc470f442003-07-28 09:56:35 +00002902
2903
2904struct cmdentry {
2905 int cmdtype;
2906 union param {
2907 int index;
2908 const struct builtincmd *cmd;
2909 struct funcnode *func;
2910 } u;
2911};
2912
2913
2914/* action to find_command() */
2915#define DO_ERR 0x01 /* prints errors */
2916#define DO_ABS 0x02 /* checks absolute paths */
2917#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
2918#define DO_ALTPATH 0x08 /* using alternate path */
2919#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
2920
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00002921static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00002922static char *padvance(const char **, const char *);
2923static void find_command(char *, struct cmdentry *, int, const char *);
2924static struct builtincmd *find_builtin(const char *);
Eric Andersenc470f442003-07-28 09:56:35 +00002925static void defun(char *, union node *);
2926static void unsetfunc(const char *);
2927
Denis Vlasenko131ae172007-02-18 13:00:19 +00002928#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersened9ecf72004-06-22 08:29:45 +00002929typedef int64_t arith_t;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002930#define arith_t_type (long long)
Eric Andersened9ecf72004-06-22 08:29:45 +00002931#else
2932typedef long arith_t;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002933#define arith_t_type (long)
Eric Andersened9ecf72004-06-22 08:29:45 +00002934#endif
Glenn L McGrath5f2a23c2004-06-25 07:05:13 +00002935
Denis Vlasenko131ae172007-02-18 13:00:19 +00002936#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +00002937static arith_t dash_arith(const char *);
2938static arith_t arith(const char *expr, int *perrcode);
Eric Andersenc470f442003-07-28 09:56:35 +00002939#endif
2940
Denis Vlasenko131ae172007-02-18 13:00:19 +00002941#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersen16767e22004-03-16 05:14:10 +00002942static unsigned long rseed;
Eric Andersen16767e22004-03-16 05:14:10 +00002943# ifndef DYNAMIC_VAR
2944# define DYNAMIC_VAR
2945# endif
2946#endif
2947
Eric Andersenc470f442003-07-28 09:56:35 +00002948/* PEOF (the end of file marker) */
2949
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002950enum {
2951 INPUT_PUSH_FILE = 1,
2952 INPUT_NOFILE_OK = 2,
2953};
2954
Eric Andersenc470f442003-07-28 09:56:35 +00002955/*
2956 * The input line number. Input.c just defines this variable, and saves
2957 * and restores it when files are pushed and popped. The user of this
2958 * package must set its value.
2959 */
Eric Andersenc470f442003-07-28 09:56:35 +00002960static void pungetc(void);
2961static void pushstring(char *, void *);
2962static void popstring(void);
Eric Andersenc470f442003-07-28 09:56:35 +00002963static void setinputfd(int, int);
2964static void setinputstring(char *);
2965static void popfile(void);
2966static void popallfiles(void);
2967static void closescript(void);
2968
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00002969/* jobs.h */
Eric Andersenc470f442003-07-28 09:56:35 +00002970
2971
2972/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
2973#define FORK_FG 0
2974#define FORK_BG 1
2975#define FORK_NOJOB 2
2976
2977/* mode flags for showjob(s) */
2978#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
2979#define SHOW_PID 0x04 /* include process pid */
2980#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
2981
2982
2983/*
2984 * A job structure contains information about a job. A job is either a
2985 * single process or a set of processes contained in a pipeline. In the
2986 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
2987 * array of pids.
2988 */
2989
2990struct procstat {
2991 pid_t pid; /* process id */
2992 int status; /* last process status from wait() */
2993 char *cmd; /* text of command being run */
2994};
2995
2996struct job {
2997 struct procstat ps0; /* status of process */
2998 struct procstat *ps; /* status or processes when more than one */
2999#if JOBS
3000 int stopstatus; /* status of a stopped job */
3001#endif
3002 uint32_t
3003 nprocs: 16, /* number of processes */
3004 state: 8,
3005#define JOBRUNNING 0 /* at least one proc running */
3006#define JOBSTOPPED 1 /* all procs are stopped */
3007#define JOBDONE 2 /* all procs are completed */
3008#if JOBS
3009 sigint: 1, /* job was killed by SIGINT */
3010 jobctl: 1, /* job running under job control */
3011#endif
3012 waited: 1, /* true if this entry has been waited for */
3013 used: 1, /* true if this entry is in used */
3014 changed: 1; /* true if status has changed */
3015 struct job *prev_job; /* previous job */
3016};
3017
3018static pid_t backgndpid; /* pid of last background process */
3019static int job_warning; /* user was warned about stopped jobs */
3020#if JOBS
3021static int jobctl; /* true if doing job control */
3022#endif
3023
3024static struct job *makejob(union node *, int);
3025static int forkshell(struct job *, union node *, int);
3026static int waitforjob(struct job *);
3027static int stoppedjobs(void);
3028
3029#if ! JOBS
3030#define setjobctl(on) /* do nothing */
3031#else
3032static void setjobctl(int);
3033static void showjobs(FILE *, int);
3034#endif
3035
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003036/* main.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003037
3038
Eric Andersenc470f442003-07-28 09:56:35 +00003039static void readcmdfile(char *);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00003040
Eric Andersenc470f442003-07-28 09:56:35 +00003041
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003042/* mystring.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003043
3044
3045#define DOLATSTRLEN 4
3046
3047static char *prefix(const char *, const char *);
3048static int number(const char *);
3049static int is_number(const char *);
3050static char *single_quote(const char *);
Eric Andersenc470f442003-07-28 09:56:35 +00003051
3052#define equal(s1, s2) (strcmp(s1, s2) == 0)
3053#define scopy(s1, s2) ((void)strcpy(s2, s1))
3054
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003055/* options.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003056
Eric Andersenc470f442003-07-28 09:56:35 +00003057static char *minusc; /* argument to -c option */
3058
3059
Eric Andersenc470f442003-07-28 09:56:35 +00003060static void optschanged(void);
3061static void setparam(char **);
3062static void freeparam(volatile struct shparam *);
3063static int shiftcmd(int, char **);
3064static int setcmd(int, char **);
3065static int nextopt(const char *);
3066
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003067/* redir.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003068
3069/* flags passed to redirect */
3070#define REDIR_PUSH 01 /* save previous values of file descriptors */
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003071#define REDIR_SAVEFD2 03 /* set preverrout */
Eric Andersenc470f442003-07-28 09:56:35 +00003072
Eric Andersenc470f442003-07-28 09:56:35 +00003073static void redirect(union node *, int);
3074static void popredir(int);
3075static void clearredir(int);
3076static int copyfd(int, int);
3077static int redirectsafe(union node *, int);
3078
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003079/* show.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003080
3081
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00003082#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00003083static void showtree(union node *);
3084static void trace(const char *, ...);
3085static void tracev(const char *, va_list);
3086static void trargs(char **);
3087static void trputc(int);
3088static void trputs(const char *);
3089static void opentrace(void);
3090#endif
3091
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003092/* trap.h */
Eric Andersenc470f442003-07-28 09:56:35 +00003093
3094
Eric Andersenc470f442003-07-28 09:56:35 +00003095static void clear_traps(void);
3096static void setsignal(int);
3097static void ignoresig(int);
3098static void onsig(int);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003099static int dotrap(void);
Eric Andersenc470f442003-07-28 09:56:35 +00003100static void setinteractive(int);
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00003101static void exitshell(void) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00003102
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00003103
3104static int is_safe_applet(char *name)
3105{
Denis Vlasenko8f27c342006-12-26 21:31:11 +00003106 /* It isn't a bug to have non-existent applet here... */
3107 /* ...just a waste of space... */
Denis Vlasenkof7996f32007-01-11 17:20:00 +00003108 static const char safe_applets[][8] = {
Denis Vlasenko8f27c342006-12-26 21:31:11 +00003109 "["
3110 USE_AWK (, "awk" )
3111 USE_CAT (, "cat" )
3112 USE_CHMOD (, "chmod" )
3113 USE_CHOWN (, "chown" )
3114 USE_CP (, "cp" )
3115 USE_CUT (, "cut" )
3116 USE_DD (, "dd" )
3117 USE_ECHO (, "echo" )
3118 USE_FIND (, "find" )
3119 USE_HEXDUMP(, "hexdump")
3120 USE_LN (, "ln" )
3121 USE_LS (, "ls" )
3122 USE_MKDIR (, "mkdir" )
3123 USE_RM (, "rm" )
3124 USE_SORT (, "sort" )
3125 USE_TEST (, "test" )
3126 USE_TOUCH (, "touch" )
3127 USE_XARGS (, "xargs" )
3128 };
3129 int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00003130 int i;
3131 for (i = 0; i < n; i++)
3132 if (strcmp(safe_applets[i], name) == 0)
3133 return 1;
3134
3135 return 0;
3136}
3137
3138
Denis Vlasenko131ae172007-02-18 13:00:19 +00003139#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00003140static struct alias *atab[ATABSIZE];
3141
Eric Andersenc470f442003-07-28 09:56:35 +00003142static void setalias(const char *, const char *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003143static struct alias *freealias(struct alias *);
3144static struct alias **__lookupalias(const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00003145
Eric Andersenc470f442003-07-28 09:56:35 +00003146static void
3147setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003148{
3149 struct alias *ap, **app;
3150
3151 app = __lookupalias(name);
3152 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003153 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003154 if (ap) {
3155 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003156 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003157 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003158 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003159 ap->flag &= ~ALIASDEAD;
3160 } else {
3161 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003162 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003163 ap->name = ckstrdup(name);
3164 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003165 ap->flag = 0;
3166 ap->next = 0;
3167 *app = ap;
3168 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003169 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003170}
3171
Eric Andersenc470f442003-07-28 09:56:35 +00003172static int
3173unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003174{
Eric Andersencb57d552001-06-28 07:25:16 +00003175 struct alias **app;
3176
3177 app = __lookupalias(name);
3178
3179 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003180 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003181 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003182 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003183 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003184 }
3185
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003186 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003187}
3188
Eric Andersenc470f442003-07-28 09:56:35 +00003189static void
3190rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003191{
Eric Andersencb57d552001-06-28 07:25:16 +00003192 struct alias *ap, **app;
3193 int i;
3194
Denis Vlasenkob012b102007-02-19 22:43:01 +00003195 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003196 for (i = 0; i < ATABSIZE; i++) {
3197 app = &atab[i];
3198 for (ap = *app; ap; ap = *app) {
3199 *app = freealias(*app);
3200 if (ap == *app) {
3201 app = &ap->next;
3202 }
3203 }
3204 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003205 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003206}
3207
Eric Andersenc470f442003-07-28 09:56:35 +00003208static struct alias *
3209lookupalias(const char *name, int check)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003210{
Eric Andersenc470f442003-07-28 09:56:35 +00003211 struct alias *ap = *__lookupalias(name);
Eric Andersen2870d962001-07-02 17:27:21 +00003212
Eric Andersenc470f442003-07-28 09:56:35 +00003213 if (check && ap && (ap->flag & ALIASINUSE))
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003214 return NULL;
3215 return ap;
Eric Andersen2870d962001-07-02 17:27:21 +00003216}
3217
Eric Andersencb57d552001-06-28 07:25:16 +00003218/*
3219 * TODO - sort output
3220 */
Eric Andersenc470f442003-07-28 09:56:35 +00003221static int
3222aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003223{
3224 char *n, *v;
3225 int ret = 0;
3226 struct alias *ap;
3227
3228 if (argc == 1) {
3229 int i;
3230
3231 for (i = 0; i < ATABSIZE; i++)
3232 for (ap = atab[i]; ap; ap = ap->next) {
3233 printalias(ap);
3234 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003235 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003236 }
3237 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003238 v = strchr(n+1, '=');
3239 if (v == NULL) { /* n+1: funny ksh stuff */
3240 ap = *__lookupalias(n);
3241 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003242 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003243 ret = 1;
3244 } else
3245 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003246 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003247 *v++ = '\0';
3248 setalias(n, v);
3249 }
3250 }
3251
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003252 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003253}
3254
Eric Andersenc470f442003-07-28 09:56:35 +00003255static int
3256unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003257{
3258 int i;
3259
3260 while ((i = nextopt("a")) != '\0') {
3261 if (i == 'a') {
3262 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003263 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003264 }
3265 }
3266 for (i = 0; *argptr; argptr++) {
3267 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003268 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003269 i = 1;
3270 }
3271 }
3272
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003273 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003274}
3275
Eric Andersenc470f442003-07-28 09:56:35 +00003276static struct alias *
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00003277freealias(struct alias *ap)
3278{
Eric Andersencb57d552001-06-28 07:25:16 +00003279 struct alias *next;
3280
3281 if (ap->flag & ALIASINUSE) {
3282 ap->flag |= ALIASDEAD;
3283 return ap;
3284 }
3285
3286 next = ap->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003287 free(ap->name);
3288 free(ap->val);
3289 free(ap);
Eric Andersencb57d552001-06-28 07:25:16 +00003290 return next;
3291}
3292
Eric Andersenc470f442003-07-28 09:56:35 +00003293static void
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00003294printalias(const struct alias *ap)
3295{
Eric Andersenc470f442003-07-28 09:56:35 +00003296 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3297}
Eric Andersencb57d552001-06-28 07:25:16 +00003298
Eric Andersenc470f442003-07-28 09:56:35 +00003299static struct alias **
3300__lookupalias(const char *name) {
3301 unsigned int hashval;
3302 struct alias **app;
3303 const char *p;
3304 unsigned int ch;
3305
3306 p = name;
3307
3308 ch = (unsigned char)*p;
3309 hashval = ch << 4;
3310 while (ch) {
3311 hashval += ch;
3312 ch = (unsigned char)*++p;
3313 }
3314 app = &atab[hashval % ATABSIZE];
Eric Andersencb57d552001-06-28 07:25:16 +00003315
3316 for (; *app; app = &(*app)->next) {
3317 if (equal(name, (*app)->name)) {
3318 break;
3319 }
3320 }
3321
3322 return app;
3323}
Denis Vlasenko131ae172007-02-18 13:00:19 +00003324#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003325
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003326/* eval.c */
Eric Andersenc470f442003-07-28 09:56:35 +00003327
3328/*
3329 * Evaluate a command.
3330 */
Eric Andersencb57d552001-06-28 07:25:16 +00003331
3332/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00003333#define EV_EXIT 01 /* exit after evaluating tree */
3334#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
3335#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00003336
3337
Eric Andersenc470f442003-07-28 09:56:35 +00003338static void evalloop(union node *, int);
3339static void evalfor(union node *, int);
3340static void evalcase(union node *, int);
3341static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003342static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00003343static void evalpipe(union node *, int);
3344static void evalcommand(union node *, int);
3345static int evalbltin(const struct builtincmd *, int, char **);
3346static int evalfun(struct funcnode *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003347static void prehash(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00003348static int bltincmd(int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00003349
Eric Andersenc470f442003-07-28 09:56:35 +00003350
3351static const struct builtincmd bltin = {
3352 "\0\0", bltincmd
3353};
3354
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003355
Eric Andersenc470f442003-07-28 09:56:35 +00003356
Eric Andersen62483552001-07-10 06:09:16 +00003357/*
Eric Andersenc470f442003-07-28 09:56:35 +00003358 * Evaluate a parse tree. The value is left in the global variable
3359 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00003360 */
Eric Andersenc470f442003-07-28 09:56:35 +00003361static void
3362evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00003363{
Eric Andersenc470f442003-07-28 09:56:35 +00003364 int checkexit = 0;
3365 void (*evalfn)(union node *, int);
3366 unsigned isor;
3367 int status;
3368 if (n == NULL) {
3369 TRACE(("evaltree(NULL) called\n"));
3370 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00003371 }
Eric Andersenc470f442003-07-28 09:56:35 +00003372 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003373 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00003374 switch (n->type) {
3375 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00003376#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00003377 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003378 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00003379 break;
3380#endif
3381 case NNOT:
3382 evaltree(n->nnot.com, EV_TESTED);
3383 status = !exitstatus;
3384 goto setstatus;
3385 case NREDIR:
3386 expredir(n->nredir.redirect);
3387 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
3388 if (!status) {
3389 evaltree(n->nredir.n, flags & EV_TESTED);
3390 status = exitstatus;
3391 }
3392 popredir(0);
3393 goto setstatus;
3394 case NCMD:
3395 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003396 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00003397 if (eflag && !(flags & EV_TESTED))
3398 checkexit = ~0;
3399 goto calleval;
3400 case NFOR:
3401 evalfn = evalfor;
3402 goto calleval;
3403 case NWHILE:
3404 case NUNTIL:
3405 evalfn = evalloop;
3406 goto calleval;
3407 case NSUBSHELL:
3408 case NBACKGND:
3409 evalfn = evalsubshell;
3410 goto calleval;
3411 case NPIPE:
3412 evalfn = evalpipe;
3413 goto checkexit;
3414 case NCASE:
3415 evalfn = evalcase;
3416 goto calleval;
3417 case NAND:
3418 case NOR:
3419 case NSEMI:
3420#if NAND + 1 != NOR
3421#error NAND + 1 != NOR
3422#endif
3423#if NOR + 1 != NSEMI
3424#error NOR + 1 != NSEMI
3425#endif
3426 isor = n->type - NAND;
3427 evaltree(
3428 n->nbinary.ch1,
3429 (flags | ((isor >> 1) - 1)) & EV_TESTED
3430 );
3431 if (!exitstatus == isor)
3432 break;
3433 if (!evalskip) {
3434 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003435 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00003436 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003437 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00003438 evalfn(n, flags);
3439 break;
3440 }
3441 break;
3442 case NIF:
3443 evaltree(n->nif.test, EV_TESTED);
3444 if (evalskip)
3445 break;
3446 if (exitstatus == 0) {
3447 n = n->nif.ifpart;
3448 goto evaln;
3449 } else if (n->nif.elsepart) {
3450 n = n->nif.elsepart;
3451 goto evaln;
3452 }
3453 goto success;
3454 case NDEFUN:
3455 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003456 success:
Eric Andersenc470f442003-07-28 09:56:35 +00003457 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003458 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00003459 exitstatus = status;
3460 break;
3461 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003462 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003463 if ((checkexit & exitstatus))
3464 evalskip |= SKIPEVAL;
3465 else if (pendingsigs && dotrap())
3466 goto exexit;
3467
3468 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003469 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00003470 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00003471 }
Eric Andersen62483552001-07-10 06:09:16 +00003472}
3473
Eric Andersenc470f442003-07-28 09:56:35 +00003474
3475#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
3476static
3477#endif
3478void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
3479
3480
3481static void
3482evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003483{
3484 int status;
3485
3486 loopnest++;
3487 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003488 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00003489 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00003490 int i;
3491
Eric Andersencb57d552001-06-28 07:25:16 +00003492 evaltree(n->nbinary.ch1, EV_TESTED);
3493 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003494 skipping:
3495 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003496 evalskip = 0;
3497 continue;
3498 }
3499 if (evalskip == SKIPBREAK && --skipcount <= 0)
3500 evalskip = 0;
3501 break;
3502 }
Eric Andersenc470f442003-07-28 09:56:35 +00003503 i = exitstatus;
3504 if (n->type != NWHILE)
3505 i = !i;
3506 if (i != 0)
3507 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003508 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00003509 status = exitstatus;
3510 if (evalskip)
3511 goto skipping;
3512 }
3513 loopnest--;
3514 exitstatus = status;
3515}
3516
Eric Andersenc470f442003-07-28 09:56:35 +00003517
Eric Andersenc470f442003-07-28 09:56:35 +00003518static void
3519evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003520{
3521 struct arglist arglist;
3522 union node *argp;
3523 struct strlist *sp;
3524 struct stackmark smark;
3525
3526 setstackmark(&smark);
3527 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003528 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003529 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00003530 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00003531 if (evalskip)
3532 goto out;
3533 }
3534 *arglist.lastp = NULL;
3535
3536 exitstatus = 0;
3537 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003538 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003539 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003540 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003541 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00003542 if (evalskip) {
3543 if (evalskip == SKIPCONT && --skipcount <= 0) {
3544 evalskip = 0;
3545 continue;
3546 }
3547 if (evalskip == SKIPBREAK && --skipcount <= 0)
3548 evalskip = 0;
3549 break;
3550 }
3551 }
3552 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003553 out:
Eric Andersencb57d552001-06-28 07:25:16 +00003554 popstackmark(&smark);
3555}
3556
Eric Andersenc470f442003-07-28 09:56:35 +00003557
Eric Andersenc470f442003-07-28 09:56:35 +00003558static void
3559evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003560{
3561 union node *cp;
3562 union node *patp;
3563 struct arglist arglist;
3564 struct stackmark smark;
3565
3566 setstackmark(&smark);
3567 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00003568 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00003569 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003570 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
3571 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003572 if (casematch(patp, arglist.list->text)) {
3573 if (evalskip == 0) {
3574 evaltree(cp->nclist.body, flags);
3575 }
3576 goto out;
3577 }
3578 }
3579 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003580 out:
Eric Andersencb57d552001-06-28 07:25:16 +00003581 popstackmark(&smark);
3582}
3583
Eric Andersenc470f442003-07-28 09:56:35 +00003584
Eric Andersenc470f442003-07-28 09:56:35 +00003585/*
3586 * Kick off a subshell to evaluate a tree.
3587 */
Eric Andersenc470f442003-07-28 09:56:35 +00003588static void
3589evalsubshell(union node *n, int flags)
3590{
3591 struct job *jp;
3592 int backgnd = (n->type == NBACKGND);
3593 int status;
3594
3595 expredir(n->nredir.redirect);
3596 if (!backgnd && flags & EV_EXIT && !trap[0])
3597 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003598 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00003599 jp = makejob(n, 1);
3600 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003601 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003602 flags |= EV_EXIT;
3603 if (backgnd)
3604 flags &=~ EV_TESTED;
3605nofork:
3606 redirect(n->nredir.redirect, 0);
3607 evaltreenr(n->nredir.n, flags);
3608 /* never returns */
3609 }
3610 status = 0;
3611 if (! backgnd)
3612 status = waitforjob(jp);
3613 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003614 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003615}
3616
3617
Eric Andersenc470f442003-07-28 09:56:35 +00003618/*
3619 * Compute the names of the files in a redirection list.
3620 */
Eric Andersenc470f442003-07-28 09:56:35 +00003621static void
3622expredir(union node *n)
3623{
3624 union node *redir;
3625
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003626 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00003627 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003628
3629 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00003630 fn.lastp = &fn.list;
3631 switch (redir->type) {
3632 case NFROMTO:
3633 case NFROM:
3634 case NTO:
3635 case NCLOBBER:
3636 case NAPPEND:
3637 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3638 redir->nfile.expfname = fn.list->text;
3639 break;
3640 case NFROMFD:
3641 case NTOFD:
3642 if (redir->ndup.vname) {
3643 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003644 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00003645 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003646 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00003647 }
3648 break;
3649 }
3650 }
3651}
3652
3653
Eric Andersencb57d552001-06-28 07:25:16 +00003654/*
Eric Andersencb57d552001-06-28 07:25:16 +00003655 * Evaluate a pipeline. All the processes in the pipeline are children
3656 * of the process creating the pipeline. (This differs from some versions
3657 * of the shell, which make the last process in a pipeline the parent
3658 * of all the rest.)
3659 */
Eric Andersenc470f442003-07-28 09:56:35 +00003660static void
3661evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00003662{
3663 struct job *jp;
3664 struct nodelist *lp;
3665 int pipelen;
3666 int prevfd;
3667 int pip[2];
3668
Eric Andersenc470f442003-07-28 09:56:35 +00003669 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00003670 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003671 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00003672 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003673 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003674 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003675 jp = makejob(n, pipelen);
3676 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003677 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003678 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00003679 pip[1] = -1;
3680 if (lp->next) {
3681 if (pipe(pip) < 0) {
3682 close(prevfd);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003683 ash_msg_and_raise_error("Pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00003684 }
3685 }
3686 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003687 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003688 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003689 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00003690 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003691 if (prevfd > 0) {
3692 dup2(prevfd, 0);
3693 close(prevfd);
3694 }
3695 if (pip[1] > 1) {
3696 dup2(pip[1], 1);
3697 close(pip[1]);
3698 }
Eric Andersenc470f442003-07-28 09:56:35 +00003699 evaltreenr(lp->n, flags);
3700 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00003701 }
3702 if (prevfd >= 0)
3703 close(prevfd);
3704 prevfd = pip[0];
3705 close(pip[1]);
3706 }
Eric Andersencb57d552001-06-28 07:25:16 +00003707 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003708 exitstatus = waitforjob(jp);
3709 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00003710 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003711 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003712}
3713
Eric Andersen62483552001-07-10 06:09:16 +00003714
Eric Andersen62483552001-07-10 06:09:16 +00003715/*
3716 * Execute a command inside back quotes. If it's a builtin command, we
3717 * want to save its output in a block obtained from malloc. Otherwise
3718 * we fork off a subprocess and get the output of the command via a pipe.
3719 * Should be called with interrupts off.
3720 */
Eric Andersenc470f442003-07-28 09:56:35 +00003721static void
3722evalbackcmd(union node *n, struct backcmd *result)
Eric Andersen62483552001-07-10 06:09:16 +00003723{
Eric Andersenc470f442003-07-28 09:56:35 +00003724 int saveherefd;
Eric Andersen62483552001-07-10 06:09:16 +00003725
Eric Andersen62483552001-07-10 06:09:16 +00003726 result->fd = -1;
3727 result->buf = NULL;
3728 result->nleft = 0;
3729 result->jp = NULL;
3730 if (n == NULL) {
Eric Andersen62483552001-07-10 06:09:16 +00003731 goto out;
3732 }
Eric Andersenc470f442003-07-28 09:56:35 +00003733
3734 saveherefd = herefd;
3735 herefd = -1;
3736
3737 {
3738 int pip[2];
3739 struct job *jp;
3740
3741 if (pipe(pip) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00003742 ash_msg_and_raise_error("Pipe call failed");
Eric Andersenc470f442003-07-28 09:56:35 +00003743 jp = makejob(n, 1);
3744 if (forkshell(jp, n, FORK_NOJOB) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003745 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003746 close(pip[0]);
3747 if (pip[1] != 1) {
3748 close(1);
3749 copyfd(pip[1], 1);
3750 close(pip[1]);
3751 }
3752 eflag = 0;
3753 evaltreenr(n, EV_EXIT);
3754 /* NOTREACHED */
Eric Andersen62483552001-07-10 06:09:16 +00003755 }
Eric Andersenc470f442003-07-28 09:56:35 +00003756 close(pip[1]);
3757 result->fd = pip[0];
3758 result->jp = jp;
Eric Andersen62483552001-07-10 06:09:16 +00003759 }
Eric Andersenc470f442003-07-28 09:56:35 +00003760 herefd = saveherefd;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003761 out:
Eric Andersen62483552001-07-10 06:09:16 +00003762 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
Eric Andersenc470f442003-07-28 09:56:35 +00003763 result->fd, result->buf, result->nleft, result->jp));
Eric Andersen62483552001-07-10 06:09:16 +00003764}
3765
Denis Vlasenko131ae172007-02-18 13:00:19 +00003766#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00003767static char **
3768parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00003769{
3770 char *cp, c;
3771
3772 for (;;) {
3773 cp = *++argv;
3774 if (!cp)
3775 return 0;
3776 if (*cp++ != '-')
3777 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003778 c = *cp++;
3779 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00003780 break;
3781 if (c == '-' && !*cp) {
3782 argv++;
3783 break;
3784 }
3785 do {
3786 switch (c) {
3787 case 'p':
3788 *path = defpath;
3789 break;
3790 default:
3791 /* run 'typecmd' for other options */
3792 return 0;
3793 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00003794 c = *cp++;
3795 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00003796 }
3797 return argv;
3798}
3799#endif
3800
Rob Landley88621d72006-08-29 19:41:06 +00003801static int isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00003802{
3803 const char *q = endofname(p);
3804 if (p == q)
3805 return 0;
3806 return *q == '=';
3807}
Eric Andersen62483552001-07-10 06:09:16 +00003808
3809/*
3810 * Execute a simple command.
3811 */
Eric Andersenc470f442003-07-28 09:56:35 +00003812static void
3813evalcommand(union node *cmd, int flags)
3814{
3815 struct stackmark smark;
3816 union node *argp;
3817 struct arglist arglist;
3818 struct arglist varlist;
3819 char **argv;
3820 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003821 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00003822 struct cmdentry cmdentry;
3823 struct job *jp;
3824 char *lastarg;
3825 const char *path;
3826 int spclbltin;
3827 int cmd_is_exec;
3828 int status;
3829 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00003830 struct builtincmd *bcmd;
3831 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00003832
3833 /* First expand the arguments. */
3834 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
3835 setstackmark(&smark);
3836 back_exitstatus = 0;
3837
3838 cmdentry.cmdtype = CMDBUILTIN;
3839 cmdentry.u.cmd = &bltin;
3840 varlist.lastp = &varlist.list;
3841 *varlist.lastp = NULL;
3842 arglist.lastp = &arglist.list;
3843 *arglist.lastp = NULL;
3844
3845 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003846 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00003847 bcmd = find_builtin(cmd->ncmd.args->narg.text);
3848 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
3849 }
3850
Eric Andersenc470f442003-07-28 09:56:35 +00003851 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
3852 struct strlist **spp;
3853
3854 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00003855 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00003856 expandarg(argp, &arglist, EXP_VARTILDE);
3857 else
3858 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3859
Eric Andersenc470f442003-07-28 09:56:35 +00003860 for (sp = *spp; sp; sp = sp->next)
3861 argc++;
3862 }
3863
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003864 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00003865 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00003866 TRACE(("evalcommand arg: %s\n", sp->text));
3867 *nargv++ = sp->text;
3868 }
3869 *nargv = NULL;
3870
3871 lastarg = NULL;
3872 if (iflag && funcnest == 0 && argc > 0)
3873 lastarg = nargv[-1];
3874
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003875 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00003876 expredir(cmd->ncmd.redirect);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003877 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00003878
3879 path = vpath.text;
3880 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
3881 struct strlist **spp;
3882 char *p;
3883
3884 spp = varlist.lastp;
3885 expandarg(argp, &varlist, EXP_VARTILDE);
3886
3887 /*
3888 * Modify the command lookup path, if a PATH= assignment
3889 * is present
3890 */
3891 p = (*spp)->text;
3892 if (varequal(p, path))
3893 path = p;
3894 }
3895
3896 /* Print the command if xflag is set. */
3897 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003898 int n;
3899 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00003900
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003901 p++;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00003902 dprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003903
3904 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003905 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003906 while (sp) {
3907 dprintf(preverrout_fd, p, sp->text);
3908 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003909 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00003910 p--;
3911 }
3912 }
3913 sp = arglist.list;
3914 }
Rob Landley53437472006-07-16 08:14:35 +00003915 full_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00003916 }
3917
3918 cmd_is_exec = 0;
3919 spclbltin = -1;
3920
3921 /* Now locate the command. */
3922 if (argc) {
3923 const char *oldpath;
3924 int cmd_flag = DO_ERR;
3925
3926 path += 5;
3927 oldpath = path;
3928 for (;;) {
3929 find_command(argv[0], &cmdentry, cmd_flag, path);
3930 if (cmdentry.cmdtype == CMDUNKNOWN) {
3931 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003932 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00003933 goto bail;
3934 }
3935
3936 /* implement bltin and command here */
3937 if (cmdentry.cmdtype != CMDBUILTIN)
3938 break;
3939 if (spclbltin < 0)
3940 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
3941 if (cmdentry.u.cmd == EXECCMD)
3942 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00003943#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00003944 if (cmdentry.u.cmd == COMMANDCMD) {
3945
3946 path = oldpath;
3947 nargv = parse_command_args(argv, &path);
3948 if (!nargv)
3949 break;
3950 argc -= nargv - argv;
3951 argv = nargv;
3952 cmd_flag |= DO_NOFUNC;
3953 } else
3954#endif
3955 break;
3956 }
3957 }
3958
3959 if (status) {
3960 /* We have a redirection error. */
3961 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00003962 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003963 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00003964 exitstatus = status;
3965 goto out;
3966 }
3967
3968 /* Execute the command. */
3969 switch (cmdentry.cmdtype) {
3970 default:
3971 /* Fork off a child process if necessary. */
3972 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003973 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00003974 jp = makejob(cmd, 1);
3975 if (forkshell(jp, cmd, FORK_FG) != 0) {
3976 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003977 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003978 break;
3979 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003980 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00003981 }
3982 listsetvar(varlist.list, VEXPORT|VSTACK);
3983 shellexec(argv, path, cmdentry.u.index);
3984 /* NOTREACHED */
3985
3986 case CMDBUILTIN:
3987 cmdenviron = varlist.list;
3988 if (cmdenviron) {
3989 struct strlist *list = cmdenviron;
3990 int i = VNOSET;
3991 if (spclbltin > 0 || argc == 0) {
3992 i = 0;
3993 if (cmd_is_exec && argc > 1)
3994 i = VEXPORT;
3995 }
3996 listsetvar(list, i);
3997 }
3998 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
3999 int exit_status;
4000 int i, j;
4001
4002 i = exception;
4003 if (i == EXEXIT)
4004 goto raise;
4005
4006 exit_status = 2;
4007 j = 0;
4008 if (i == EXINT)
4009 j = SIGINT;
4010 if (i == EXSIG)
4011 j = pendingsigs;
4012 if (j)
4013 exit_status = j + 128;
4014 exitstatus = exit_status;
4015
4016 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004017 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004018 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00004019 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004020 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004021 }
4022 break;
4023
4024 case CMDFUNCTION:
4025 listsetvar(varlist.list, 0);
4026 if (evalfun(cmdentry.u.func, argc, argv, flags))
4027 goto raise;
4028 break;
4029 }
4030
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004031 out:
Eric Andersenc470f442003-07-28 09:56:35 +00004032 popredir(cmd_is_exec);
4033 if (lastarg)
4034 /* dsl: I think this is intended to be used to support
4035 * '_' in 'vi' command mode during line editing...
4036 * However I implemented that within libedit itself.
4037 */
4038 setvar("_", lastarg, 0);
4039 popstackmark(&smark);
4040}
4041
4042static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004043evalbltin(const struct builtincmd *cmd, int argc, char **argv)
4044{
Eric Andersenc470f442003-07-28 09:56:35 +00004045 char *volatile savecmdname;
4046 struct jmploc *volatile savehandler;
4047 struct jmploc jmploc;
4048 int i;
4049
4050 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004051 i = setjmp(jmploc.loc);
4052 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00004053 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004054 savehandler = exception_handler;
4055 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00004056 commandname = argv[0];
4057 argptr = argv + 1;
4058 optptr = NULL; /* initialize nextopt */
4059 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00004060 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004061 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004062 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00004063 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00004064 commandname = savecmdname;
4065 exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004066 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00004067
4068 return i;
4069}
4070
Denis Vlasenkoaa744452007-02-23 01:04:22 +00004071static struct localvar *localvars;
4072
4073/*
4074 * Called after a function returns.
4075 * Interrupts must be off.
4076 */
4077static void
4078poplocalvars(void)
4079{
4080 struct localvar *lvp;
4081 struct var *vp;
4082
4083 while ((lvp = localvars) != NULL) {
4084 localvars = lvp->next;
4085 vp = lvp->vp;
4086 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
4087 if (vp == NULL) { /* $- saved */
4088 memcpy(optlist, lvp->text, sizeof(optlist));
4089 free((char*)lvp->text);
4090 optschanged();
4091 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
4092 unsetvar(vp->text);
4093 } else {
4094 if (vp->func)
4095 (*vp->func)(strchrnul(lvp->text, '=') + 1);
4096 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
4097 free((char*)vp->text);
4098 vp->flags = lvp->flags;
4099 vp->text = lvp->text;
4100 }
4101 free(lvp);
4102 }
4103}
4104
Eric Andersenc470f442003-07-28 09:56:35 +00004105static int
4106evalfun(struct funcnode *func, int argc, char **argv, int flags)
4107{
4108 volatile struct shparam saveparam;
4109 struct localvar *volatile savelocalvars;
4110 struct jmploc *volatile savehandler;
4111 struct jmploc jmploc;
4112 int e;
4113
4114 saveparam = shellparam;
4115 savelocalvars = localvars;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004116 e = setjmp(jmploc.loc);
4117 if (e) {
Eric Andersenc470f442003-07-28 09:56:35 +00004118 goto funcdone;
4119 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004120 INT_OFF;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004121 savehandler = exception_handler;
4122 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00004123 localvars = NULL;
4124 shellparam.malloc = 0;
4125 func->count++;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004126 funcnest++;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004127 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004128 shellparam.nparam = argc - 1;
4129 shellparam.p = argv + 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00004130#if ENABLE_ASH_GETOPTS
Eric Andersenc470f442003-07-28 09:56:35 +00004131 shellparam.optind = 1;
4132 shellparam.optoff = -1;
4133#endif
Eric Andersenc470f442003-07-28 09:56:35 +00004134 evaltree(&func->n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00004135funcdone:
Denis Vlasenkob012b102007-02-19 22:43:01 +00004136 INT_OFF;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004137 funcnest--;
Eric Andersenc470f442003-07-28 09:56:35 +00004138 freefunc(func);
4139 poplocalvars();
4140 localvars = savelocalvars;
4141 freeparam(&shellparam);
4142 shellparam = saveparam;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004143 exception_handler = savehandler;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004144 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004145 evalskip &= ~SKIPFUNC;
Eric Andersenc470f442003-07-28 09:56:35 +00004146 return e;
4147}
4148
4149
Denis Vlasenkoaa744452007-02-23 01:04:22 +00004150static int
4151goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00004152{
4153 return !*endofname(p);
4154}
4155
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004156
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004157/*
4158 * Search for a command. This is called before we fork so that the
4159 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00004160 * the child. The check for "goodname" is an overly conservative
4161 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004162 */
Eric Andersenc470f442003-07-28 09:56:35 +00004163static void
4164prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004165{
4166 struct cmdentry entry;
4167
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004168 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
4169 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004170}
4171
Eric Andersencb57d552001-06-28 07:25:16 +00004172
Eric Andersenc470f442003-07-28 09:56:35 +00004173
Eric Andersencb57d552001-06-28 07:25:16 +00004174/*
4175 * Builtin commands. Builtin commands whose functions are closely
4176 * tied to evaluation are implemented here.
4177 */
4178
4179/*
Eric Andersenc470f442003-07-28 09:56:35 +00004180 * No command given.
Eric Andersencb57d552001-06-28 07:25:16 +00004181 */
Eric Andersenc470f442003-07-28 09:56:35 +00004182static int
4183bltincmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004184{
4185 /*
4186 * Preserve exitstatus of a previous possible redirection
4187 * as POSIX mandates
4188 */
Eric Andersenc470f442003-07-28 09:56:35 +00004189 return back_exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00004190}
4191
4192
4193/*
4194 * Handle break and continue commands. Break, continue, and return are
4195 * all handled by setting the evalskip flag. The evaluation routines
4196 * above all check this flag, and if it is set they start skipping
4197 * commands rather than executing them. The variable skipcount is
4198 * the number of loops to break/continue, or the number of function
4199 * levels to return. (The latter is always 1.) It should probably
4200 * be an error to break out of more loops than exist, but it isn't
4201 * in the standard shell so we don't make it one here.
4202 */
4203
Eric Andersenc470f442003-07-28 09:56:35 +00004204static int
4205breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004206{
4207 int n = argc > 1 ? number(argv[1]) : 1;
4208
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00004209 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004210 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00004211 if (n > loopnest)
4212 n = loopnest;
4213 if (n > 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004214 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00004215 skipcount = n;
4216 }
4217 return 0;
4218}
4219
4220
4221/*
4222 * The return command.
4223 */
Eric Andersenc470f442003-07-28 09:56:35 +00004224static int
4225returncmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004226{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004227 /*
4228 * If called outside a function, do what ksh does;
4229 * skip the rest of the file.
4230 */
4231 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
4232 return argv[1] ? number(argv[1]) : exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00004233}
4234
4235
Eric Andersenc470f442003-07-28 09:56:35 +00004236static int
4237falsecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004238{
4239 return 1;
4240}
4241
Eric Andersenc470f442003-07-28 09:56:35 +00004242
4243static int
4244truecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004245{
4246 return 0;
4247}
Eric Andersen2870d962001-07-02 17:27:21 +00004248
Eric Andersencb57d552001-06-28 07:25:16 +00004249
Eric Andersenc470f442003-07-28 09:56:35 +00004250static int
4251execcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004252{
4253 if (argc > 1) {
Eric Andersenc470f442003-07-28 09:56:35 +00004254 iflag = 0; /* exit on error */
Eric Andersencb57d552001-06-28 07:25:16 +00004255 mflag = 0;
4256 optschanged();
Eric Andersenc470f442003-07-28 09:56:35 +00004257 shellexec(argv + 1, pathval(), 0);
Eric Andersencb57d552001-06-28 07:25:16 +00004258 }
4259 return 0;
4260}
4261
Eric Andersenc470f442003-07-28 09:56:35 +00004262
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004263/* exec.c */
Eric Andersenc470f442003-07-28 09:56:35 +00004264
4265/*
4266 * When commands are first encountered, they are entered in a hash table.
4267 * This ensures that a full path search will not have to be done for them
4268 * on each invocation.
4269 *
4270 * We should investigate converting to a linear search, even though that
4271 * would make the command name "hash" a misnomer.
4272 */
4273
4274#define CMDTABLESIZE 31 /* should be prime */
4275#define ARB 1 /* actual size determined at run time */
4276
4277
Eric Andersenc470f442003-07-28 09:56:35 +00004278struct tblentry {
4279 struct tblentry *next; /* next entry in hash chain */
4280 union param param; /* definition of builtin function */
4281 short cmdtype; /* index identifying command */
4282 char rehash; /* if set, cd done since entry created */
4283 char cmdname[ARB]; /* name of command */
4284};
4285
4286
4287static struct tblentry *cmdtable[CMDTABLESIZE];
4288static int builtinloc = -1; /* index in path of %builtin, or -1 */
4289
4290
4291static void tryexec(char *, char **, char **);
Eric Andersenc470f442003-07-28 09:56:35 +00004292static void clearcmdentry(int);
4293static struct tblentry *cmdlookup(const char *, int);
4294static void delete_cmd_entry(void);
4295
Eric Andersencb57d552001-06-28 07:25:16 +00004296
4297/*
4298 * Exec a program. Never returns. If you change this routine, you may
4299 * have to change the find_command routine as well.
4300 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00004301#define environment() listvars(VEXPORT, VUNSET, 0)
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004302static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00004303static void
4304shellexec(char **argv, const char *path, int idx)
Eric Andersencb57d552001-06-28 07:25:16 +00004305{
4306 char *cmdname;
4307 int e;
Eric Andersenc470f442003-07-28 09:56:35 +00004308 char **envp;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004309 int exerrno;
Eric Andersencb57d552001-06-28 07:25:16 +00004310
Eric Andersenc470f442003-07-28 09:56:35 +00004311 clearredir(1);
4312 envp = environment();
Denis Vlasenko62a69832007-01-10 00:24:50 +00004313 if (strchr(argv[0], '/') || is_safe_applet(argv[0])
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00004314#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004315 || find_applet_by_name(argv[0])
Eric Andersenc470f442003-07-28 09:56:35 +00004316#endif
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004317 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00004318 tryexec(argv[0], argv, envp);
4319 e = errno;
4320 } else {
4321 e = ENOENT;
4322 while ((cmdname = padvance(&path, argv[0])) != NULL) {
4323 if (--idx < 0 && pathopt == NULL) {
4324 tryexec(cmdname, argv, envp);
4325 if (errno != ENOENT && errno != ENOTDIR)
4326 e = errno;
4327 }
4328 stunalloc(cmdname);
4329 }
4330 }
4331
4332 /* Map to POSIX errors */
4333 switch (e) {
4334 case EACCES:
4335 exerrno = 126;
4336 break;
4337 case ENOENT:
4338 exerrno = 127;
4339 break;
4340 default:
4341 exerrno = 2;
4342 break;
4343 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00004344 exitstatus = exerrno;
Eric Andersenc470f442003-07-28 09:56:35 +00004345 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
4346 argv[0], e, suppressint ));
Denis Vlasenkob012b102007-02-19 22:43:01 +00004347 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
Eric Andersencb57d552001-06-28 07:25:16 +00004348 /* NOTREACHED */
4349}
4350
Eric Andersen2870d962001-07-02 17:27:21 +00004351
Eric Andersenc470f442003-07-28 09:56:35 +00004352static void
4353tryexec(char *cmd, char **argv, char **envp)
Eric Andersen62483552001-07-10 06:09:16 +00004354{
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004355 int repeated = 0;
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004356 struct BB_applet *a;
4357 int argc = 0;
4358 char **c;
Denis Vlasenkof7996f32007-01-11 17:20:00 +00004359
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004360 if (strchr(cmd, '/') == NULL
4361 && (a = find_applet_by_name(cmd)) != NULL
4362 && is_safe_applet(cmd)
4363 ) {
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004364 c = argv;
4365 while (*c != NULL) {
4366 c++; argc++;
4367 }
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00004368 applet_name = cmd;
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004369 exit(a->main(argc, argv));
4370 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00004371#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko8f27c342006-12-26 21:31:11 +00004372 if (find_applet_by_name(cmd) != NULL) {
Rob Landley0fcd9432005-05-07 08:27:34 +00004373 /* re-exec ourselves with the new arguments */
Denis Vlasenko131ae172007-02-18 13:00:19 +00004374 execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
Rob Landley0fcd9432005-05-07 08:27:34 +00004375 /* If they called chroot or otherwise made the binary no longer
4376 * executable, fall through */
Glenn L McGrathc00c56e2002-12-23 10:23:10 +00004377 }
Eric Andersen3102ac42001-07-06 04:26:23 +00004378#endif
Eric Andersenc470f442003-07-28 09:56:35 +00004379
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004380 repeat:
Eric Andersenc470f442003-07-28 09:56:35 +00004381#ifdef SYSV
4382 do {
4383 execve(cmd, argv, envp);
4384 } while (errno == EINTR);
4385#else
Eric Andersencb57d552001-06-28 07:25:16 +00004386 execve(cmd, argv, envp);
Eric Andersenc470f442003-07-28 09:56:35 +00004387#endif
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004388 if (repeated++) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00004389 free(argv);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004390 } else if (errno == ENOEXEC) {
4391 char **ap;
4392 char **new;
4393
Eric Andersenc470f442003-07-28 09:56:35 +00004394 for (ap = argv; *ap; ap++)
4395 ;
4396 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
Glenn L McGrath47e5ca12003-09-15 12:00:19 +00004397 ap[1] = cmd;
4398 *ap = cmd = (char *)DEFAULT_SHELL;
4399 ap += 2;
4400 argv++;
Eric Andersenc470f442003-07-28 09:56:35 +00004401 while ((*ap++ = *argv++))
4402 ;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004403 argv = new;
4404 goto repeat;
Eric Andersencb57d552001-06-28 07:25:16 +00004405 }
Eric Andersencb57d552001-06-28 07:25:16 +00004406}
4407
Eric Andersenc470f442003-07-28 09:56:35 +00004408
Eric Andersencb57d552001-06-28 07:25:16 +00004409/*** Command hashing code ***/
4410
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004411static void
4412printentry(struct tblentry *cmdp)
4413{
4414 int idx;
4415 const char *path;
4416 char *name;
4417
4418 idx = cmdp->param.index;
4419 path = pathval();
4420 do {
4421 name = padvance(&path, cmdp->cmdname);
4422 stunalloc(name);
4423 } while (--idx >= 0);
4424 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
4425}
4426
Eric Andersenc470f442003-07-28 09:56:35 +00004427
4428static int
4429hashcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004430{
4431 struct tblentry **pp;
4432 struct tblentry *cmdp;
4433 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00004434 struct cmdentry entry;
4435 char *name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004436
Eric Andersenc470f442003-07-28 09:56:35 +00004437 while ((c = nextopt("r")) != '\0') {
4438 clearcmdentry(0);
4439 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004440 }
4441 if (*argptr == NULL) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004442 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
4443 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00004444 if (cmdp->cmdtype == CMDNORMAL)
4445 printentry(cmdp);
Eric Andersencb57d552001-06-28 07:25:16 +00004446 }
4447 }
4448 return 0;
4449 }
4450 c = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00004451 while ((name = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004452 cmdp = cmdlookup(name, 0);
4453 if (cmdp != NULL
Eric Andersenc470f442003-07-28 09:56:35 +00004454 && (cmdp->cmdtype == CMDNORMAL
4455 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
Eric Andersencb57d552001-06-28 07:25:16 +00004456 delete_cmd_entry();
4457 find_command(name, &entry, DO_ERR, pathval());
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004458 if (entry.cmdtype == CMDUNKNOWN)
4459 c = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00004460 argptr++;
Eric Andersencb57d552001-06-28 07:25:16 +00004461 }
4462 return c;
4463}
4464
Eric Andersenc470f442003-07-28 09:56:35 +00004465
Eric Andersencb57d552001-06-28 07:25:16 +00004466/*
4467 * Resolve a command name. If you change this routine, you may have to
4468 * change the shellexec routine as well.
4469 */
Eric Andersencb57d552001-06-28 07:25:16 +00004470static void
Eric Andersenc470f442003-07-28 09:56:35 +00004471find_command(char *name, struct cmdentry *entry, int act, const char *path)
Eric Andersencb57d552001-06-28 07:25:16 +00004472{
4473 struct tblentry *cmdp;
4474 int idx;
4475 int prev;
4476 char *fullname;
4477 struct stat statb;
4478 int e;
Eric Andersencb57d552001-06-28 07:25:16 +00004479 int updatetbl;
Eric Andersencb57d552001-06-28 07:25:16 +00004480 struct builtincmd *bcmd;
4481
Eric Andersenc470f442003-07-28 09:56:35 +00004482 /* If name contains a slash, don't use PATH or hash table */
Eric Andersencb57d552001-06-28 07:25:16 +00004483 if (strchr(name, '/') != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004484 entry->u.index = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00004485 if (act & DO_ABS) {
4486 while (stat(name, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004487#ifdef SYSV
4488 if (errno == EINTR)
4489 continue;
4490#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004491 entry->cmdtype = CMDUNKNOWN;
Eric Andersencb57d552001-06-28 07:25:16 +00004492 return;
4493 }
Eric Andersencb57d552001-06-28 07:25:16 +00004494 }
4495 entry->cmdtype = CMDNORMAL;
Eric Andersencb57d552001-06-28 07:25:16 +00004496 return;
4497 }
4498
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00004499#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Eric Andersenbf8bf102002-09-17 08:41:08 +00004500 if (find_applet_by_name(name)) {
4501 entry->cmdtype = CMDNORMAL;
4502 entry->u.index = -1;
4503 return;
4504 }
4505#endif
4506
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00004507 if (is_safe_applet(name)) {
4508 entry->cmdtype = CMDNORMAL;
4509 entry->u.index = -1;
4510 return;
4511 }
4512
Eric Andersenc470f442003-07-28 09:56:35 +00004513 updatetbl = (path == pathval());
4514 if (!updatetbl) {
4515 act |= DO_ALTPATH;
4516 if (strstr(path, "%builtin") != NULL)
4517 act |= DO_ALTBLTIN;
Eric Andersencb57d552001-06-28 07:25:16 +00004518 }
4519
Eric Andersenc470f442003-07-28 09:56:35 +00004520 /* If name is in the table, check answer will be ok */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004521 cmdp = cmdlookup(name, 0);
4522 if (cmdp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004523 int bit;
Eric Andersencb57d552001-06-28 07:25:16 +00004524
Eric Andersenc470f442003-07-28 09:56:35 +00004525 switch (cmdp->cmdtype) {
4526 default:
4527#if DEBUG
4528 abort();
4529#endif
4530 case CMDNORMAL:
4531 bit = DO_ALTPATH;
4532 break;
4533 case CMDFUNCTION:
4534 bit = DO_NOFUNC;
4535 break;
4536 case CMDBUILTIN:
4537 bit = DO_ALTBLTIN;
4538 break;
Eric Andersencb57d552001-06-28 07:25:16 +00004539 }
Eric Andersenc470f442003-07-28 09:56:35 +00004540 if (act & bit) {
Eric Andersencb57d552001-06-28 07:25:16 +00004541 updatetbl = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00004542 cmdp = NULL;
4543 } else if (cmdp->rehash == 0)
4544 /* if not invalidated by cd, we're done */
4545 goto success;
Eric Andersencb57d552001-06-28 07:25:16 +00004546 }
4547
4548 /* If %builtin not in path, check for builtin next */
Eric Andersenc470f442003-07-28 09:56:35 +00004549 bcmd = find_builtin(name);
4550 if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
4551 act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
4552 )))
4553 goto builtin_success;
Eric Andersencb57d552001-06-28 07:25:16 +00004554
4555 /* We have to search path. */
Eric Andersenc470f442003-07-28 09:56:35 +00004556 prev = -1; /* where to start */
4557 if (cmdp && cmdp->rehash) { /* doing a rehash */
Eric Andersencb57d552001-06-28 07:25:16 +00004558 if (cmdp->cmdtype == CMDBUILTIN)
4559 prev = builtinloc;
4560 else
4561 prev = cmdp->param.index;
4562 }
4563
4564 e = ENOENT;
4565 idx = -1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004566 loop:
Eric Andersencb57d552001-06-28 07:25:16 +00004567 while ((fullname = padvance(&path, name)) != NULL) {
4568 stunalloc(fullname);
4569 idx++;
Eric Andersencb57d552001-06-28 07:25:16 +00004570 if (pathopt) {
Eric Andersenc470f442003-07-28 09:56:35 +00004571 if (prefix(pathopt, "builtin")) {
4572 if (bcmd)
4573 goto builtin_success;
Eric Andersencb57d552001-06-28 07:25:16 +00004574 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00004575 } else if (!(act & DO_NOFUNC) &&
4576 prefix(pathopt, "func")) {
Eric Andersencb57d552001-06-28 07:25:16 +00004577 /* handled below */
4578 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00004579 /* ignore unimplemented options */
4580 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00004581 }
4582 }
4583 /* if rehash, don't redo absolute path names */
Eric Andersenc470f442003-07-28 09:56:35 +00004584 if (fullname[0] == '/' && idx <= prev) {
Eric Andersencb57d552001-06-28 07:25:16 +00004585 if (idx < prev)
4586 continue;
4587 TRACE(("searchexec \"%s\": no change\n", name));
4588 goto success;
4589 }
4590 while (stat(fullname, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00004591#ifdef SYSV
4592 if (errno == EINTR)
4593 continue;
4594#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004595 if (errno != ENOENT && errno != ENOTDIR)
4596 e = errno;
4597 goto loop;
4598 }
Eric Andersenc470f442003-07-28 09:56:35 +00004599 e = EACCES; /* if we fail, this will be the error */
Eric Andersencb57d552001-06-28 07:25:16 +00004600 if (!S_ISREG(statb.st_mode))
4601 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00004602 if (pathopt) { /* this is a %func directory */
Eric Andersencb57d552001-06-28 07:25:16 +00004603 stalloc(strlen(fullname) + 1);
4604 readcmdfile(fullname);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004605 cmdp = cmdlookup(name, 0);
4606 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004607 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
Eric Andersencb57d552001-06-28 07:25:16 +00004608 stunalloc(fullname);
4609 goto success;
4610 }
Eric Andersencb57d552001-06-28 07:25:16 +00004611 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
Eric Andersencb57d552001-06-28 07:25:16 +00004612 if (!updatetbl) {
4613 entry->cmdtype = CMDNORMAL;
4614 entry->u.index = idx;
4615 return;
4616 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004617 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00004618 cmdp = cmdlookup(name, 1);
4619 cmdp->cmdtype = CMDNORMAL;
4620 cmdp->param.index = idx;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004621 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00004622 goto success;
4623 }
4624
4625 /* We failed. If there was an entry for this command, delete it */
4626 if (cmdp && updatetbl)
4627 delete_cmd_entry();
4628 if (act & DO_ERR)
Denis Vlasenkob012b102007-02-19 22:43:01 +00004629 ash_msg("%s: %s", name, errmsg(e, "not found"));
Eric Andersencb57d552001-06-28 07:25:16 +00004630 entry->cmdtype = CMDUNKNOWN;
4631 return;
4632
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004633 builtin_success:
Eric Andersenc470f442003-07-28 09:56:35 +00004634 if (!updatetbl) {
4635 entry->cmdtype = CMDBUILTIN;
4636 entry->u.cmd = bcmd;
4637 return;
4638 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004639 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00004640 cmdp = cmdlookup(name, 1);
4641 cmdp->cmdtype = CMDBUILTIN;
4642 cmdp->param.cmd = bcmd;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004643 INT_ON;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004644 success:
Eric Andersencb57d552001-06-28 07:25:16 +00004645 cmdp->rehash = 0;
4646 entry->cmdtype = cmdp->cmdtype;
4647 entry->u = cmdp->param;
4648}
4649
4650
Eric Andersenc470f442003-07-28 09:56:35 +00004651/*
Eric Andersencb57d552001-06-28 07:25:16 +00004652 * Search the table of builtin commands.
4653 */
Eric Andersenc470f442003-07-28 09:56:35 +00004654static struct builtincmd *
4655find_builtin(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00004656{
4657 struct builtincmd *bp;
4658
Eric Andersenc470f442003-07-28 09:56:35 +00004659 bp = bsearch(
4660 name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
4661 pstrcmp
4662 );
Eric Andersencb57d552001-06-28 07:25:16 +00004663 return bp;
4664}
4665
4666
4667/*
4668 * Called when a cd is done. Marks all commands so the next time they
4669 * are executed they will be rehashed.
4670 */
Eric Andersenc470f442003-07-28 09:56:35 +00004671static void
4672hashcd(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004673{
Eric Andersencb57d552001-06-28 07:25:16 +00004674 struct tblentry **pp;
4675 struct tblentry *cmdp;
4676
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004677 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
4678 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00004679 if (cmdp->cmdtype == CMDNORMAL || (
4680 cmdp->cmdtype == CMDBUILTIN &&
4681 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
4682 builtinloc > 0
4683 ))
Eric Andersencb57d552001-06-28 07:25:16 +00004684 cmdp->rehash = 1;
4685 }
4686 }
4687}
4688
4689
Eric Andersencb57d552001-06-28 07:25:16 +00004690/*
Eric Andersenc470f442003-07-28 09:56:35 +00004691 * Fix command hash table when PATH changed.
Eric Andersencb57d552001-06-28 07:25:16 +00004692 * Called before PATH is changed. The argument is the new value of PATH;
Eric Andersenc470f442003-07-28 09:56:35 +00004693 * pathval() still returns the old value at this point.
4694 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00004695 */
Eric Andersenc470f442003-07-28 09:56:35 +00004696static void
4697changepath(const char *newval)
Eric Andersencb57d552001-06-28 07:25:16 +00004698{
Eric Andersenc470f442003-07-28 09:56:35 +00004699 const char *old, *new;
4700 int idx;
Eric Andersencb57d552001-06-28 07:25:16 +00004701 int firstchange;
Eric Andersenc470f442003-07-28 09:56:35 +00004702 int idx_bltin;
Eric Andersencb57d552001-06-28 07:25:16 +00004703
Eric Andersenc470f442003-07-28 09:56:35 +00004704 old = pathval();
4705 new = newval;
4706 firstchange = 9999; /* assume no change */
4707 idx = 0;
4708 idx_bltin = -1;
4709 for (;;) {
4710 if (*old != *new) {
4711 firstchange = idx;
4712 if ((*old == '\0' && *new == ':')
4713 || (*old == ':' && *new == '\0'))
4714 firstchange++;
4715 old = new; /* ignore subsequent differences */
4716 }
4717 if (*new == '\0')
4718 break;
4719 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
4720 idx_bltin = idx;
4721 if (*new == ':') {
4722 idx++;
4723 }
4724 new++, old++;
4725 }
4726 if (builtinloc < 0 && idx_bltin >= 0)
4727 builtinloc = idx_bltin; /* zap builtins */
4728 if (builtinloc >= 0 && idx_bltin < 0)
4729 firstchange = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004730 clearcmdentry(firstchange);
Eric Andersenc470f442003-07-28 09:56:35 +00004731 builtinloc = idx_bltin;
Eric Andersencb57d552001-06-28 07:25:16 +00004732}
4733
4734
4735/*
4736 * Clear out command entries. The argument specifies the first entry in
4737 * PATH which has changed.
4738 */
Eric Andersenc470f442003-07-28 09:56:35 +00004739static void
4740clearcmdentry(int firstchange)
Eric Andersencb57d552001-06-28 07:25:16 +00004741{
4742 struct tblentry **tblp;
4743 struct tblentry **pp;
4744 struct tblentry *cmdp;
4745
Denis Vlasenkob012b102007-02-19 22:43:01 +00004746 INT_OFF;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004747 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00004748 pp = tblp;
4749 while ((cmdp = *pp) != NULL) {
4750 if ((cmdp->cmdtype == CMDNORMAL &&
Eric Andersenc470f442003-07-28 09:56:35 +00004751 cmdp->param.index >= firstchange)
4752 || (cmdp->cmdtype == CMDBUILTIN &&
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004753 builtinloc >= firstchange)
4754 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00004755 *pp = cmdp->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00004756 free(cmdp);
Eric Andersencb57d552001-06-28 07:25:16 +00004757 } else {
4758 pp = &cmdp->next;
4759 }
4760 }
4761 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00004762 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00004763}
4764
4765
4766/*
Eric Andersencb57d552001-06-28 07:25:16 +00004767 * Locate a command in the command hash table. If "add" is nonzero,
4768 * add the command to the table if it is not already present. The
4769 * variable "lastcmdentry" is set to point to the address of the link
4770 * pointing to the entry, so that delete_cmd_entry can delete the
4771 * entry.
Eric Andersenc470f442003-07-28 09:56:35 +00004772 *
4773 * Interrupts must be off if called with add != 0.
Eric Andersencb57d552001-06-28 07:25:16 +00004774 */
Eric Andersen2870d962001-07-02 17:27:21 +00004775static struct tblentry **lastcmdentry;
Eric Andersencb57d552001-06-28 07:25:16 +00004776
Eric Andersenc470f442003-07-28 09:56:35 +00004777static struct tblentry *
4778cmdlookup(const char *name, int add)
Eric Andersencb57d552001-06-28 07:25:16 +00004779{
Eric Andersenc470f442003-07-28 09:56:35 +00004780 unsigned int hashval;
Eric Andersen2870d962001-07-02 17:27:21 +00004781 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +00004782 struct tblentry *cmdp;
4783 struct tblentry **pp;
4784
4785 p = name;
Eric Andersenc470f442003-07-28 09:56:35 +00004786 hashval = (unsigned char)*p << 4;
Eric Andersencb57d552001-06-28 07:25:16 +00004787 while (*p)
Eric Andersenc470f442003-07-28 09:56:35 +00004788 hashval += (unsigned char)*p++;
Eric Andersencb57d552001-06-28 07:25:16 +00004789 hashval &= 0x7FFF;
4790 pp = &cmdtable[hashval % CMDTABLESIZE];
Denis Vlasenko2da584f2007-02-19 22:44:05 +00004791 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00004792 if (equal(cmdp->cmdname, name))
4793 break;
4794 pp = &cmdp->next;
4795 }
4796 if (add && cmdp == NULL) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004797 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
Eric Andersenc470f442003-07-28 09:56:35 +00004798 + strlen(name) + 1);
Eric Andersencb57d552001-06-28 07:25:16 +00004799 cmdp->next = NULL;
4800 cmdp->cmdtype = CMDUNKNOWN;
Eric Andersencb57d552001-06-28 07:25:16 +00004801 strcpy(cmdp->cmdname, name);
Eric Andersencb57d552001-06-28 07:25:16 +00004802 }
4803 lastcmdentry = pp;
4804 return cmdp;
4805}
4806
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004807
Eric Andersencb57d552001-06-28 07:25:16 +00004808/*
4809 * Delete the command entry returned on the last lookup.
4810 */
Eric Andersenc470f442003-07-28 09:56:35 +00004811static void
4812delete_cmd_entry(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004813{
Eric Andersencb57d552001-06-28 07:25:16 +00004814 struct tblentry *cmdp;
4815
Denis Vlasenkob012b102007-02-19 22:43:01 +00004816 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00004817 cmdp = *lastcmdentry;
4818 *lastcmdentry = cmdp->next;
Eric Andersenc470f442003-07-28 09:56:35 +00004819 if (cmdp->cmdtype == CMDFUNCTION)
4820 freefunc(cmdp->param.func);
Denis Vlasenkob012b102007-02-19 22:43:01 +00004821 free(cmdp);
4822 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00004823}
4824
4825
Eric Andersenc470f442003-07-28 09:56:35 +00004826/*
4827 * Add a new command entry, replacing any existing command entry for
4828 * the same name - except special builtins.
4829 */
Rob Landley88621d72006-08-29 19:41:06 +00004830static void addcmdentry(char *name, struct cmdentry *entry)
Eric Andersenc470f442003-07-28 09:56:35 +00004831{
4832 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +00004833
Eric Andersenc470f442003-07-28 09:56:35 +00004834 cmdp = cmdlookup(name, 1);
4835 if (cmdp->cmdtype == CMDFUNCTION) {
4836 freefunc(cmdp->param.func);
4837 }
4838 cmdp->cmdtype = entry->cmdtype;
4839 cmdp->param = entry->u;
4840 cmdp->rehash = 0;
4841}
Eric Andersencb57d552001-06-28 07:25:16 +00004842
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004843
Eric Andersenc470f442003-07-28 09:56:35 +00004844/*
4845 * Make a copy of a parse tree.
4846 */
Rob Landley88621d72006-08-29 19:41:06 +00004847static struct funcnode * copyfunc(union node *n)
Eric Andersenc470f442003-07-28 09:56:35 +00004848{
4849 struct funcnode *f;
4850 size_t blocksize;
4851
4852 funcblocksize = offsetof(struct funcnode, n);
4853 funcstringsize = 0;
4854 calcsize(n);
4855 blocksize = funcblocksize;
4856 f = ckmalloc(blocksize + funcstringsize);
4857 funcblock = (char *) f + offsetof(struct funcnode, n);
4858 funcstring = (char *) f + blocksize;
4859 copynode(n);
4860 f->count = 0;
4861 return f;
4862}
4863
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004864
Eric Andersenc470f442003-07-28 09:56:35 +00004865/*
4866 * Define a shell function.
4867 */
Eric Andersenc470f442003-07-28 09:56:35 +00004868static void
4869defun(char *name, union node *func)
4870{
4871 struct cmdentry entry;
4872
Denis Vlasenkob012b102007-02-19 22:43:01 +00004873 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00004874 entry.cmdtype = CMDFUNCTION;
4875 entry.u.func = copyfunc(func);
4876 addcmdentry(name, &entry);
Denis Vlasenkob012b102007-02-19 22:43:01 +00004877 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00004878}
Eric Andersencb57d552001-06-28 07:25:16 +00004879
4880
4881/*
4882 * Delete a function if it exists.
4883 */
Eric Andersenc470f442003-07-28 09:56:35 +00004884static void
4885unsetfunc(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00004886{
Eric Andersencb57d552001-06-28 07:25:16 +00004887 struct tblentry *cmdp;
4888
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00004889 cmdp = cmdlookup(name, 0);
4890 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
Eric Andersencb57d552001-06-28 07:25:16 +00004891 delete_cmd_entry();
Eric Andersencb57d552001-06-28 07:25:16 +00004892}
4893
Eric Andersen2870d962001-07-02 17:27:21 +00004894/*
Eric Andersencb57d552001-06-28 07:25:16 +00004895 * Locate and print what a word is...
4896 */
Denis Vlasenko131ae172007-02-18 13:00:19 +00004897#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00004898static int
4899describe_command(char *command, int describe_command_verbose)
4900#else
4901#define describe_command_verbose 1
4902static int
4903describe_command(char *command)
4904#endif
4905{
4906 struct cmdentry entry;
4907 struct tblentry *cmdp;
Denis Vlasenko131ae172007-02-18 13:00:19 +00004908#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00004909 const struct alias *ap;
4910#endif
4911 const char *path = pathval();
4912
4913 if (describe_command_verbose) {
4914 out1str(command);
4915 }
4916
4917 /* First look at the keywords */
4918 if (findkwd(command)) {
4919 out1str(describe_command_verbose ? " is a shell keyword" : command);
4920 goto out;
4921 }
4922
Denis Vlasenko131ae172007-02-18 13:00:19 +00004923#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00004924 /* Then look at the aliases */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004925 ap = lookupalias(command, 0);
4926 if (ap != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004927 if (describe_command_verbose) {
4928 out1fmt(" is an alias for %s", ap->val);
4929 } else {
4930 out1str("alias ");
4931 printalias(ap);
4932 return 0;
4933 }
4934 goto out;
4935 }
4936#endif
4937 /* Then check if it is a tracked alias */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004938 cmdp = cmdlookup(command, 0);
4939 if (cmdp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00004940 entry.cmdtype = cmdp->cmdtype;
4941 entry.u = cmdp->param;
4942 } else {
4943 /* Finally use brute force */
4944 find_command(command, &entry, DO_ABS, path);
4945 }
4946
4947 switch (entry.cmdtype) {
4948 case CMDNORMAL: {
4949 int j = entry.u.index;
4950 char *p;
4951 if (j == -1) {
4952 p = command;
4953 } else {
4954 do {
4955 p = padvance(&path, command);
4956 stunalloc(p);
4957 } while (--j >= 0);
4958 }
4959 if (describe_command_verbose) {
4960 out1fmt(" is%s %s",
4961 (cmdp ? " a tracked alias for" : nullstr), p
4962 );
4963 } else {
4964 out1str(p);
4965 }
4966 break;
4967 }
4968
4969 case CMDFUNCTION:
4970 if (describe_command_verbose) {
4971 out1str(" is a shell function");
4972 } else {
4973 out1str(command);
4974 }
4975 break;
4976
4977 case CMDBUILTIN:
4978 if (describe_command_verbose) {
4979 out1fmt(" is a %sshell builtin",
4980 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
4981 "special " : nullstr
4982 );
4983 } else {
4984 out1str(command);
4985 }
4986 break;
4987
4988 default:
4989 if (describe_command_verbose) {
4990 out1str(": not found\n");
4991 }
4992 return 127;
4993 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00004994 out:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00004995 outstr("\n", stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00004996 return 0;
4997}
4998
4999static int
5000typecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005001{
5002 int i;
5003 int err = 0;
5004
5005 for (i = 1; i < argc; i++) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00005006#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00005007 err |= describe_command(argv[i], 1);
5008#else
5009 err |= describe_command(argv[i]);
5010#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005011 }
5012 return err;
5013}
5014
Denis Vlasenko131ae172007-02-18 13:00:19 +00005015#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00005016static int
5017commandcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005018{
5019 int c;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005020 enum {
5021 VERIFY_BRIEF = 1,
5022 VERIFY_VERBOSE = 2,
5023 } verify = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00005024
5025 while ((c = nextopt("pvV")) != '\0')
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005026 if (c == 'V')
5027 verify |= VERIFY_VERBOSE;
5028 else if (c == 'v')
5029 verify |= VERIFY_BRIEF;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005030#if DEBUG
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005031 else if (c != 'p')
5032 abort();
Eric Andersenc470f442003-07-28 09:56:35 +00005033#endif
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005034 if (verify)
5035 return describe_command(*argptr, verify - VERIFY_BRIEF);
Eric Andersencb57d552001-06-28 07:25:16 +00005036
5037 return 0;
5038}
Eric Andersen2870d962001-07-02 17:27:21 +00005039#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005040
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005041/* expand.c */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005042
Eric Andersencb57d552001-06-28 07:25:16 +00005043/*
5044 * Routines to expand arguments to commands. We have to deal with
5045 * backquotes, shell variables, and file metacharacters.
5046 */
Eric Andersenc470f442003-07-28 09:56:35 +00005047
Eric Andersencb57d552001-06-28 07:25:16 +00005048/*
5049 * _rmescape() flags
5050 */
Eric Andersenc470f442003-07-28 09:56:35 +00005051#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5052#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5053#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5054#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5055#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
Eric Andersencb57d552001-06-28 07:25:16 +00005056
5057/*
5058 * Structure specifying which parts of the string should be searched
5059 * for IFS characters.
5060 */
5061
5062struct ifsregion {
Eric Andersenc470f442003-07-28 09:56:35 +00005063 struct ifsregion *next; /* next region in list */
5064 int begoff; /* offset of start of region */
5065 int endoff; /* offset of end of region */
5066 int nulonly; /* search for nul bytes only */
Eric Andersencb57d552001-06-28 07:25:16 +00005067};
5068
Eric Andersenc470f442003-07-28 09:56:35 +00005069/* output of current string */
5070static char *expdest;
5071/* list of back quote expressions */
5072static struct nodelist *argbackq;
5073/* first struct in list of ifs regions */
5074static struct ifsregion ifsfirst;
5075/* last struct in list */
5076static struct ifsregion *ifslastp;
5077/* holds expanded arg list */
5078static struct arglist exparg;
Eric Andersencb57d552001-06-28 07:25:16 +00005079
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005080static void argstr(char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005081static char *exptilde(char *, char *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005082static void expbackq(union node *, int, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005083static const char *subevalvar(char *, char *, int, int, int, int, int);
5084static char *evalvar(char *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005085static void strtodest(const char *, int, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005086static void memtodest(const char *p, size_t len, int syntax, int quotes);
Glenn L McGrath76620622004-01-13 10:19:37 +00005087static ssize_t varvalue(char *, int, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005088static void recordregion(int, int, int);
5089static void removerecordregions(int);
5090static void ifsbreakup(char *, struct arglist *);
5091static void ifsfree(void);
5092static void expandmeta(struct strlist *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00005093static int patmatch(char *, const char *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005094
Eric Andersened9ecf72004-06-22 08:29:45 +00005095static int cvtnum(arith_t);
Eric Andersenc470f442003-07-28 09:56:35 +00005096static size_t esclen(const char *, const char *);
5097static char *scanleft(char *, char *, char *, char *, int, int);
5098static char *scanright(char *, char *, char *, char *, int, int);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00005099static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005100
Eric Andersenc470f442003-07-28 09:56:35 +00005101
5102#define pmatch(a, b) !fnmatch((a), (b), 0)
5103/*
Eric Andersen90898442003-08-06 11:20:52 +00005104 * Prepare a pattern for a expmeta (internal glob(3)) call.
Eric Andersenc470f442003-07-28 09:56:35 +00005105 *
5106 * Returns an stalloced string.
5107 */
5108
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005109static char * preglob(const char *pattern, int quoted, int flag)
5110{
Eric Andersenc470f442003-07-28 09:56:35 +00005111 flag |= RMESCAPE_GLOB;
5112 if (quoted) {
5113 flag |= RMESCAPE_QUOTED;
5114 }
5115 return _rmescapes((char *)pattern, flag);
5116}
5117
5118
5119static size_t
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00005120esclen(const char *start, const char *p)
5121{
Eric Andersenc470f442003-07-28 09:56:35 +00005122 size_t esc = 0;
5123
5124 while (p > start && *--p == CTLESC) {
5125 esc++;
5126 }
5127 return esc;
5128}
5129
Eric Andersencb57d552001-06-28 07:25:16 +00005130
5131/*
5132 * Expand shell variables and backquotes inside a here document.
5133 */
5134
Rob Landley88621d72006-08-29 19:41:06 +00005135static void expandhere(union node *arg, int fd)
Eric Andersen2870d962001-07-02 17:27:21 +00005136{
Eric Andersencb57d552001-06-28 07:25:16 +00005137 herefd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +00005138 expandarg(arg, (struct arglist *)NULL, 0);
Rob Landley53437472006-07-16 08:14:35 +00005139 full_write(fd, stackblock(), expdest - (char *)stackblock());
Eric Andersencb57d552001-06-28 07:25:16 +00005140}
5141
5142
5143/*
5144 * Perform variable substitution and command substitution on an argument,
5145 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
5146 * perform splitting and file name expansion. When arglist is NULL, perform
5147 * here document expansion.
5148 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005149static void
Eric Andersenc470f442003-07-28 09:56:35 +00005150expandarg(union node *arg, struct arglist *arglist, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005151{
5152 struct strlist *sp;
5153 char *p;
5154
5155 argbackq = arg->narg.backquote;
5156 STARTSTACKSTR(expdest);
5157 ifsfirst.next = NULL;
5158 ifslastp = NULL;
5159 argstr(arg->narg.text, flag);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00005160 p = _STPUTC('\0', expdest);
5161 expdest = p - 1;
Eric Andersencb57d552001-06-28 07:25:16 +00005162 if (arglist == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00005163 return; /* here document expanded */
Eric Andersencb57d552001-06-28 07:25:16 +00005164 }
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00005165 p = grabstackstr(p);
Eric Andersencb57d552001-06-28 07:25:16 +00005166 exparg.lastp = &exparg.list;
5167 /*
5168 * TODO - EXP_REDIR
5169 */
5170 if (flag & EXP_FULL) {
5171 ifsbreakup(p, &exparg);
5172 *exparg.lastp = NULL;
5173 exparg.lastp = &exparg.list;
5174 expandmeta(exparg.list, flag);
5175 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00005176 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Eric Andersencb57d552001-06-28 07:25:16 +00005177 rmescapes(p);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00005178 sp = stalloc(sizeof(*sp));
Eric Andersencb57d552001-06-28 07:25:16 +00005179 sp->text = p;
5180 *exparg.lastp = sp;
5181 exparg.lastp = &sp->next;
5182 }
Eric Andersenc470f442003-07-28 09:56:35 +00005183 if (ifsfirst.next)
5184 ifsfree();
Eric Andersencb57d552001-06-28 07:25:16 +00005185 *exparg.lastp = NULL;
5186 if (exparg.list) {
5187 *arglist->lastp = exparg.list;
5188 arglist->lastp = exparg.lastp;
5189 }
5190}
5191
5192
Eric Andersenc470f442003-07-28 09:56:35 +00005193/*
5194 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5195 * characters to allow for further processing. Otherwise treat
5196 * $@ like $* since no splitting will be performed.
5197 */
Eric Andersenc470f442003-07-28 09:56:35 +00005198static void
5199argstr(char *p, int flag)
5200{
5201 static const char spclchars[] = {
5202 '=',
5203 ':',
5204 CTLQUOTEMARK,
5205 CTLENDVAR,
5206 CTLESC,
5207 CTLVAR,
5208 CTLBACKQ,
5209 CTLBACKQ | CTLQUOTE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00005210#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005211 CTLENDARI,
5212#endif
5213 0
5214 };
5215 const char *reject = spclchars;
5216 int c;
5217 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5218 int breakall = flag & EXP_WORD;
5219 int inquotes;
5220 size_t length;
5221 int startloc;
5222
5223 if (!(flag & EXP_VARTILDE)) {
5224 reject += 2;
5225 } else if (flag & EXP_VARTILDE2) {
5226 reject++;
5227 }
5228 inquotes = 0;
5229 length = 0;
5230 if (flag & EXP_TILDE) {
5231 char *q;
5232
5233 flag &= ~EXP_TILDE;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005234 tilde:
Eric Andersenc470f442003-07-28 09:56:35 +00005235 q = p;
5236 if (*q == CTLESC && (flag & EXP_QWORD))
5237 q++;
5238 if (*q == '~')
5239 p = exptilde(p, q, flag);
5240 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005241 start:
Eric Andersenc470f442003-07-28 09:56:35 +00005242 startloc = expdest - (char *)stackblock();
5243 for (;;) {
5244 length += strcspn(p + length, reject);
5245 c = p[length];
5246 if (c && (!(c & 0x80)
Denis Vlasenko131ae172007-02-18 13:00:19 +00005247#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005248 || c == CTLENDARI
5249#endif
5250 )) {
5251 /* c == '=' || c == ':' || c == CTLENDARI */
5252 length++;
5253 }
5254 if (length > 0) {
5255 int newloc;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00005256 expdest = stack_nputstr(p, length, expdest);
Eric Andersenc470f442003-07-28 09:56:35 +00005257 newloc = expdest - (char *)stackblock();
5258 if (breakall && !inquotes && newloc > startloc) {
5259 recordregion(startloc, newloc, 0);
5260 }
5261 startloc = newloc;
5262 }
5263 p += length + 1;
5264 length = 0;
5265
5266 switch (c) {
5267 case '\0':
5268 goto breakloop;
5269 case '=':
5270 if (flag & EXP_VARTILDE2) {
5271 p--;
5272 continue;
5273 }
5274 flag |= EXP_VARTILDE2;
5275 reject++;
5276 /* fall through */
5277 case ':':
5278 /*
5279 * sort of a hack - expand tildes in variable
5280 * assignments (after the first '=' and after ':'s).
5281 */
5282 if (*--p == '~') {
5283 goto tilde;
5284 }
5285 continue;
5286 }
5287
5288 switch (c) {
5289 case CTLENDVAR: /* ??? */
5290 goto breakloop;
5291 case CTLQUOTEMARK:
5292 /* "$@" syntax adherence hack */
5293 if (
5294 !inquotes &&
5295 !memcmp(p, dolatstr, DOLATSTRLEN) &&
5296 (p[4] == CTLQUOTEMARK || (
5297 p[4] == CTLENDVAR &&
5298 p[5] == CTLQUOTEMARK
5299 ))
5300 ) {
5301 p = evalvar(p + 1, flag) + 1;
5302 goto start;
5303 }
5304 inquotes = !inquotes;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005305 addquote:
Eric Andersenc470f442003-07-28 09:56:35 +00005306 if (quotes) {
5307 p--;
5308 length++;
5309 startloc++;
5310 }
5311 break;
5312 case CTLESC:
5313 startloc++;
5314 length++;
5315 goto addquote;
5316 case CTLVAR:
5317 p = evalvar(p, flag);
5318 goto start;
5319 case CTLBACKQ:
5320 c = 0;
5321 case CTLBACKQ|CTLQUOTE:
5322 expbackq(argbackq->n, c, quotes);
5323 argbackq = argbackq->next;
5324 goto start;
Denis Vlasenko131ae172007-02-18 13:00:19 +00005325#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005326 case CTLENDARI:
5327 p--;
5328 expari(quotes);
5329 goto start;
5330#endif
5331 }
5332 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005333 breakloop:
Eric Andersenc470f442003-07-28 09:56:35 +00005334 ;
5335}
5336
5337static char *
5338exptilde(char *startp, char *p, int flag)
5339{
5340 char c;
5341 char *name;
5342 struct passwd *pw;
5343 const char *home;
5344 int quotes = flag & (EXP_FULL | EXP_CASE);
5345 int startloc;
5346
5347 name = p + 1;
5348
5349 while ((c = *++p) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00005350 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +00005351 case CTLESC:
Denis Vlasenko097c3242006-11-27 16:59:15 +00005352 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005353 case CTLQUOTEMARK:
Denis Vlasenko097c3242006-11-27 16:59:15 +00005354 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005355 case ':':
5356 if (flag & EXP_VARTILDE)
5357 goto done;
5358 break;
5359 case '/':
5360 case CTLENDVAR:
5361 goto done;
5362 }
5363 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005364 done:
Eric Andersenc470f442003-07-28 09:56:35 +00005365 *p = '\0';
5366 if (*name == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005367 home = lookupvar(homestr);
Eric Andersenc470f442003-07-28 09:56:35 +00005368 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005369 pw = getpwnam(name);
5370 if (pw == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00005371 goto lose;
5372 home = pw->pw_dir;
5373 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00005374 if (!home || !*home)
Eric Andersenc470f442003-07-28 09:56:35 +00005375 goto lose;
5376 *p = c;
5377 startloc = expdest - (char *)stackblock();
5378 strtodest(home, SQSYNTAX, quotes);
5379 recordregion(startloc, expdest - (char *)stackblock(), 0);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00005380 return p;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005381 lose:
Eric Andersenc470f442003-07-28 09:56:35 +00005382 *p = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00005383 return startp;
Eric Andersenc470f442003-07-28 09:56:35 +00005384}
5385
5386
5387static void
5388removerecordregions(int endoff)
5389{
5390 if (ifslastp == NULL)
5391 return;
5392
5393 if (ifsfirst.endoff > endoff) {
5394 while (ifsfirst.next != NULL) {
5395 struct ifsregion *ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005396 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005397 ifsp = ifsfirst.next->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005398 free(ifsfirst.next);
Eric Andersenc470f442003-07-28 09:56:35 +00005399 ifsfirst.next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005400 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005401 }
5402 if (ifsfirst.begoff > endoff)
5403 ifslastp = NULL;
5404 else {
5405 ifslastp = &ifsfirst;
5406 ifsfirst.endoff = endoff;
5407 }
5408 return;
5409 }
5410
5411 ifslastp = &ifsfirst;
5412 while (ifslastp->next && ifslastp->next->begoff < endoff)
5413 ifslastp=ifslastp->next;
5414 while (ifslastp->next != NULL) {
5415 struct ifsregion *ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005416 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005417 ifsp = ifslastp->next->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005418 free(ifslastp->next);
Eric Andersenc470f442003-07-28 09:56:35 +00005419 ifslastp->next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005420 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005421 }
5422 if (ifslastp->endoff > endoff)
5423 ifslastp->endoff = endoff;
5424}
5425
5426
Denis Vlasenko131ae172007-02-18 13:00:19 +00005427#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00005428/*
5429 * Expand arithmetic expression. Backup to start of expression,
5430 * evaluate, place result in (backed up) result, adjust string position.
5431 */
5432void
5433expari(int quotes)
5434{
5435 char *p, *start;
5436 int begoff;
5437 int flag;
5438 int len;
5439
5440 /* ifsfree(); */
5441
5442 /*
5443 * This routine is slightly over-complicated for
5444 * efficiency. Next we scan backwards looking for the
5445 * start of arithmetic.
5446 */
5447 start = stackblock();
5448 p = expdest - 1;
5449 *p = '\0';
5450 p--;
5451 do {
5452 int esc;
5453
5454 while (*p != CTLARI) {
5455 p--;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005456#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00005457 if (p < start) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00005458 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
Eric Andersenc470f442003-07-28 09:56:35 +00005459 }
5460#endif
5461 }
5462
5463 esc = esclen(start, p);
5464 if (!(esc % 2)) {
5465 break;
5466 }
5467
5468 p -= esc + 1;
5469 } while (1);
5470
5471 begoff = p - start;
5472
5473 removerecordregions(begoff);
5474
5475 flag = p[1];
5476
5477 expdest = p;
5478
5479 if (quotes)
5480 rmescapes(p + 2);
5481
5482 len = cvtnum(dash_arith(p + 2));
5483
5484 if (flag != '"')
5485 recordregion(begoff, begoff + len, 0);
5486}
5487#endif
5488
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005489
Eric Andersenc470f442003-07-28 09:56:35 +00005490/*
5491 * Expand stuff in backwards quotes.
5492 */
Eric Andersenc470f442003-07-28 09:56:35 +00005493static void
5494expbackq(union node *cmd, int quoted, int quotes)
5495{
5496 struct backcmd in;
5497 int i;
5498 char buf[128];
5499 char *p;
5500 char *dest;
5501 int startloc;
5502 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5503 struct stackmark smark;
5504
Denis Vlasenkob012b102007-02-19 22:43:01 +00005505 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00005506 setstackmark(&smark);
5507 dest = expdest;
5508 startloc = dest - (char *)stackblock();
5509 grabstackstr(dest);
5510 evalbackcmd(cmd, (struct backcmd *) &in);
5511 popstackmark(&smark);
5512
5513 p = in.buf;
5514 i = in.nleft;
5515 if (i == 0)
5516 goto read;
5517 for (;;) {
5518 memtodest(p, i, syntax, quotes);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005519 read:
Eric Andersenc470f442003-07-28 09:56:35 +00005520 if (in.fd < 0)
5521 break;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005522 i = safe_read(in.fd, buf, sizeof(buf));
Eric Andersenc470f442003-07-28 09:56:35 +00005523 TRACE(("expbackq: read returns %d\n", i));
5524 if (i <= 0)
5525 break;
5526 p = buf;
5527 }
5528
5529 if (in.buf)
Denis Vlasenkob012b102007-02-19 22:43:01 +00005530 free(in.buf);
Eric Andersenc470f442003-07-28 09:56:35 +00005531 if (in.fd >= 0) {
5532 close(in.fd);
5533 back_exitstatus = waitforjob(in.jp);
5534 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00005535 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00005536
5537 /* Eat all trailing newlines */
5538 dest = expdest;
5539 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5540 STUNPUTC(dest);
5541 expdest = dest;
5542
5543 if (quoted == 0)
5544 recordregion(startloc, dest - (char *)stackblock(), 0);
5545 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5546 (dest - (char *)stackblock()) - startloc,
5547 (dest - (char *)stackblock()) - startloc,
5548 stackblock() + startloc));
5549}
5550
5551
5552static char *
Eric Andersen90898442003-08-06 11:20:52 +00005553scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5554 int zero)
5555{
Eric Andersenc470f442003-07-28 09:56:35 +00005556 char *loc;
5557 char *loc2;
5558 char c;
5559
5560 loc = startp;
5561 loc2 = rmesc;
5562 do {
5563 int match;
5564 const char *s = loc2;
5565 c = *loc2;
5566 if (zero) {
5567 *loc2 = '\0';
5568 s = rmesc;
5569 }
5570 match = pmatch(str, s);
5571 *loc2 = c;
5572 if (match)
5573 return loc;
5574 if (quotes && *loc == CTLESC)
5575 loc++;
5576 loc++;
5577 loc2++;
5578 } while (c);
5579 return 0;
5580}
5581
5582
5583static char *
Eric Andersen90898442003-08-06 11:20:52 +00005584scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5585 int zero)
5586{
Eric Andersenc470f442003-07-28 09:56:35 +00005587 int esc = 0;
5588 char *loc;
5589 char *loc2;
5590
5591 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5592 int match;
5593 char c = *loc2;
5594 const char *s = loc2;
5595 if (zero) {
5596 *loc2 = '\0';
5597 s = rmesc;
5598 }
5599 match = pmatch(str, s);
5600 *loc2 = c;
5601 if (match)
5602 return loc;
5603 loc--;
5604 if (quotes) {
5605 if (--esc < 0) {
5606 esc = esclen(startp, loc);
5607 }
5608 if (esc % 2) {
5609 esc--;
5610 loc--;
5611 }
5612 }
5613 }
5614 return 0;
5615}
5616
5617static const char *
5618subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5619{
5620 char *startp;
5621 char *loc;
5622 int saveherefd = herefd;
5623 struct nodelist *saveargbackq = argbackq;
5624 int amount;
5625 char *rmesc, *rmescend;
5626 int zero;
5627 char *(*scan)(char *, char *, char *, char *, int , int);
5628
5629 herefd = -1;
5630 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5631 STPUTC('\0', expdest);
5632 herefd = saveherefd;
5633 argbackq = saveargbackq;
5634 startp = stackblock() + startloc;
5635
5636 switch (subtype) {
5637 case VSASSIGN:
5638 setvar(str, startp, 0);
5639 amount = startp - expdest;
5640 STADJUST(amount, expdest);
5641 return startp;
5642
5643 case VSQUESTION:
5644 varunset(p, str, startp, varflags);
5645 /* NOTREACHED */
5646 }
5647
5648 subtype -= VSTRIMRIGHT;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005649#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00005650 if (subtype < 0 || subtype > 3)
5651 abort();
5652#endif
5653
5654 rmesc = startp;
5655 rmescend = stackblock() + strloc;
5656 if (quotes) {
5657 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5658 if (rmesc != startp) {
5659 rmescend = expdest;
5660 startp = stackblock() + startloc;
5661 }
5662 }
5663 rmescend--;
5664 str = stackblock() + strloc;
5665 preglob(str, varflags & VSQUOTE, 0);
5666
5667 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5668 zero = subtype >> 1;
5669 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5670 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5671
5672 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5673 if (loc) {
5674 if (zero) {
5675 memmove(startp, loc, str - loc);
5676 loc = startp + (str - loc) - 1;
5677 }
5678 *loc = '\0';
5679 amount = loc - expdest;
5680 STADJUST(amount, expdest);
5681 }
5682 return loc;
5683}
5684
5685
Eric Andersen62483552001-07-10 06:09:16 +00005686/*
5687 * Expand a variable, and return a pointer to the next character in the
5688 * input string.
5689 */
Eric Andersenc470f442003-07-28 09:56:35 +00005690static char *
5691evalvar(char *p, int flag)
Eric Andersen62483552001-07-10 06:09:16 +00005692{
5693 int subtype;
5694 int varflags;
5695 char *var;
Eric Andersen62483552001-07-10 06:09:16 +00005696 int patloc;
5697 int c;
Eric Andersen62483552001-07-10 06:09:16 +00005698 int startloc;
Glenn L McGrath76620622004-01-13 10:19:37 +00005699 ssize_t varlen;
Eric Andersen62483552001-07-10 06:09:16 +00005700 int easy;
Eric Andersenc470f442003-07-28 09:56:35 +00005701 int quotes;
5702 int quoted;
Eric Andersen62483552001-07-10 06:09:16 +00005703
Eric Andersenc470f442003-07-28 09:56:35 +00005704 quotes = flag & (EXP_FULL | EXP_CASE);
Eric Andersen62483552001-07-10 06:09:16 +00005705 varflags = *p++;
5706 subtype = varflags & VSTYPE;
Eric Andersenc470f442003-07-28 09:56:35 +00005707 quoted = varflags & VSQUOTE;
Eric Andersen62483552001-07-10 06:09:16 +00005708 var = p;
Eric Andersenc470f442003-07-28 09:56:35 +00005709 easy = (!quoted || (*var == '@' && shellparam.nparam));
Eric Andersenc470f442003-07-28 09:56:35 +00005710 startloc = expdest - (char *)stackblock();
5711 p = strchr(p, '=') + 1;
5712
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005713 again:
Glenn L McGrath76620622004-01-13 10:19:37 +00005714 varlen = varvalue(var, varflags, flag);
5715 if (varflags & VSNUL)
5716 varlen--;
Eric Andersen62483552001-07-10 06:09:16 +00005717
Glenn L McGrath76620622004-01-13 10:19:37 +00005718 if (subtype == VSPLUS) {
5719 varlen = -1 - varlen;
5720 goto vsplus;
5721 }
Eric Andersen62483552001-07-10 06:09:16 +00005722
Eric Andersenc470f442003-07-28 09:56:35 +00005723 if (subtype == VSMINUS) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005724 vsplus:
Glenn L McGrath76620622004-01-13 10:19:37 +00005725 if (varlen < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00005726 argstr(
5727 p, flag | EXP_TILDE |
5728 (quoted ? EXP_QWORD : EXP_WORD)
5729 );
5730 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005731 }
5732 if (easy)
5733 goto record;
Eric Andersenc470f442003-07-28 09:56:35 +00005734 goto end;
5735 }
Eric Andersen62483552001-07-10 06:09:16 +00005736
Eric Andersenc470f442003-07-28 09:56:35 +00005737 if (subtype == VSASSIGN || subtype == VSQUESTION) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005738 if (varlen < 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005739 if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
Eric Andersen62483552001-07-10 06:09:16 +00005740 varflags &= ~VSNUL;
5741 /*
5742 * Remove any recorded regions beyond
5743 * start of variable
5744 */
5745 removerecordregions(startloc);
5746 goto again;
5747 }
Eric Andersenc470f442003-07-28 09:56:35 +00005748 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005749 }
5750 if (easy)
5751 goto record;
Eric Andersenc470f442003-07-28 09:56:35 +00005752 goto end;
Eric Andersen62483552001-07-10 06:09:16 +00005753 }
5754
Glenn L McGrath76620622004-01-13 10:19:37 +00005755 if (varlen < 0 && uflag)
Eric Andersenc470f442003-07-28 09:56:35 +00005756 varunset(p, var, 0, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005757
Eric Andersenc470f442003-07-28 09:56:35 +00005758 if (subtype == VSLENGTH) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005759 cvtnum(varlen > 0 ? varlen : 0);
Eric Andersenc470f442003-07-28 09:56:35 +00005760 goto record;
5761 }
5762
5763 if (subtype == VSNORMAL) {
5764 if (!easy)
5765 goto end;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005766 record:
Eric Andersenc470f442003-07-28 09:56:35 +00005767 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5768 goto end;
5769 }
5770
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00005771#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00005772 switch (subtype) {
5773 case VSTRIMLEFT:
5774 case VSTRIMLEFTMAX:
5775 case VSTRIMRIGHT:
5776 case VSTRIMRIGHTMAX:
5777 break;
5778 default:
5779 abort();
5780 }
5781#endif
5782
Glenn L McGrath76620622004-01-13 10:19:37 +00005783 if (varlen >= 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00005784 /*
5785 * Terminate the string and start recording the pattern
5786 * right after it
5787 */
5788 STPUTC('\0', expdest);
5789 patloc = expdest - (char *)stackblock();
5790 if (subevalvar(p, NULL, patloc, subtype,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00005791 startloc, varflags, quotes) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00005792 int amount = expdest - (
5793 (char *)stackblock() + patloc - 1
5794 );
5795 STADJUST(-amount, expdest);
5796 }
5797 /* Remove any recorded regions beyond start of variable */
5798 removerecordregions(startloc);
5799 goto record;
5800 }
5801
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005802 end:
Eric Andersenc470f442003-07-28 09:56:35 +00005803 if (subtype != VSNORMAL) { /* skip to end of alternative */
5804 int nesting = 1;
Eric Andersen62483552001-07-10 06:09:16 +00005805 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005806 c = *p++;
5807 if (c == CTLESC)
Eric Andersen62483552001-07-10 06:09:16 +00005808 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00005809 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005810 if (varlen >= 0)
Eric Andersen62483552001-07-10 06:09:16 +00005811 argbackq = argbackq->next;
5812 } else if (c == CTLVAR) {
5813 if ((*p++ & VSTYPE) != VSNORMAL)
5814 nesting++;
5815 } else if (c == CTLENDVAR) {
5816 if (--nesting == 0)
5817 break;
5818 }
5819 }
5820 }
5821 return p;
5822}
5823
Eric Andersencb57d552001-06-28 07:25:16 +00005824
Eric Andersencb57d552001-06-28 07:25:16 +00005825/*
5826 * Put a string on the stack.
5827 */
Eric Andersenc470f442003-07-28 09:56:35 +00005828static void
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00005829memtodest(const char *p, size_t len, int syntax, int quotes)
5830{
Eric Andersenc470f442003-07-28 09:56:35 +00005831 char *q = expdest;
5832
5833 q = makestrspace(len * 2, q);
5834
5835 while (len--) {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00005836 int c = SC2INT(*p++);
Eric Andersenc470f442003-07-28 09:56:35 +00005837 if (!c)
5838 continue;
5839 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5840 USTPUTC(CTLESC, q);
5841 USTPUTC(c, q);
Eric Andersencb57d552001-06-28 07:25:16 +00005842 }
Eric Andersenc470f442003-07-28 09:56:35 +00005843
5844 expdest = q;
Eric Andersencb57d552001-06-28 07:25:16 +00005845}
5846
Eric Andersenc470f442003-07-28 09:56:35 +00005847
5848static void
5849strtodest(const char *p, int syntax, int quotes)
5850{
5851 memtodest(p, strlen(p), syntax, quotes);
5852}
5853
5854
Eric Andersencb57d552001-06-28 07:25:16 +00005855/*
5856 * Add the value of a specialized variable to the stack string.
5857 */
Glenn L McGrath76620622004-01-13 10:19:37 +00005858static ssize_t
5859varvalue(char *name, int varflags, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00005860{
5861 int num;
5862 char *p;
5863 int i;
Glenn L McGrath76620622004-01-13 10:19:37 +00005864 int sep = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00005865 int sepq = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +00005866 ssize_t len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00005867 char **ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005868 int syntax;
Glenn L McGrath76620622004-01-13 10:19:37 +00005869 int quoted = varflags & VSQUOTE;
5870 int subtype = varflags & VSTYPE;
Eric Andersencb57d552001-06-28 07:25:16 +00005871 int quotes = flags & (EXP_FULL | EXP_CASE);
5872
Glenn L McGrath76620622004-01-13 10:19:37 +00005873 if (quoted && (flags & EXP_FULL))
5874 sep = 1 << CHAR_BIT;
5875
Eric Andersencb57d552001-06-28 07:25:16 +00005876 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5877 switch (*name) {
5878 case '$':
5879 num = rootpid;
5880 goto numvar;
5881 case '?':
Eric Andersenc470f442003-07-28 09:56:35 +00005882 num = exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00005883 goto numvar;
5884 case '#':
5885 num = shellparam.nparam;
5886 goto numvar;
5887 case '!':
5888 num = backgndpid;
Glenn L McGrath76620622004-01-13 10:19:37 +00005889 if (num == 0)
5890 return -1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005891 numvar:
Glenn L McGrath76620622004-01-13 10:19:37 +00005892 len = cvtnum(num);
Eric Andersencb57d552001-06-28 07:25:16 +00005893 break;
5894 case '-':
Glenn L McGrath76620622004-01-13 10:19:37 +00005895 p = makestrspace(NOPTS, expdest);
5896 for (i = NOPTS - 1; i >= 0; i--) {
5897 if (optlist[i]) {
5898 USTPUTC(optletters(i), p);
5899 len++;
5900 }
Eric Andersencb57d552001-06-28 07:25:16 +00005901 }
Glenn L McGrath76620622004-01-13 10:19:37 +00005902 expdest = p;
Eric Andersencb57d552001-06-28 07:25:16 +00005903 break;
5904 case '@':
Glenn L McGrath76620622004-01-13 10:19:37 +00005905 if (sep)
Eric Andersencb57d552001-06-28 07:25:16 +00005906 goto param;
Eric Andersencb57d552001-06-28 07:25:16 +00005907 /* fall through */
5908 case '*':
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00005909 sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
Glenn L McGrath76620622004-01-13 10:19:37 +00005910 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5911 sepq = 1;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005912 param:
5913 ap = shellparam.p;
5914 if (!ap)
Glenn L McGrath76620622004-01-13 10:19:37 +00005915 return -1;
5916 while ((p = *ap++)) {
5917 size_t partlen;
5918
5919 partlen = strlen(p);
Glenn L McGrath76620622004-01-13 10:19:37 +00005920 len += partlen;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00005921
5922 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5923 memtodest(p, partlen, syntax, quotes);
5924
5925 if (*ap && sep) {
Glenn L McGrath76620622004-01-13 10:19:37 +00005926 char *q;
5927
5928 len++;
5929 if (subtype == VSPLUS || subtype == VSLENGTH) {
5930 continue;
5931 }
5932 q = expdest;
Eric Andersencb57d552001-06-28 07:25:16 +00005933 if (sepq)
Glenn L McGrath76620622004-01-13 10:19:37 +00005934 STPUTC(CTLESC, q);
5935 STPUTC(sep, q);
5936 expdest = q;
Eric Andersencb57d552001-06-28 07:25:16 +00005937 }
5938 }
Glenn L McGrath76620622004-01-13 10:19:37 +00005939 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00005940 case '0':
Glenn L McGrath76620622004-01-13 10:19:37 +00005941 case '1':
5942 case '2':
5943 case '3':
5944 case '4':
5945 case '5':
5946 case '6':
5947 case '7':
5948 case '8':
5949 case '9':
Eric Andersencb57d552001-06-28 07:25:16 +00005950 num = atoi(name);
Glenn L McGrath76620622004-01-13 10:19:37 +00005951 if (num < 0 || num > shellparam.nparam)
5952 return -1;
5953 p = num ? shellparam.p[num - 1] : arg0;
5954 goto value;
5955 default:
5956 p = lookupvar(name);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00005957 value:
Glenn L McGrath76620622004-01-13 10:19:37 +00005958 if (!p)
5959 return -1;
5960
5961 len = strlen(p);
5962 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5963 memtodest(p, len, syntax, quotes);
5964 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00005965 }
Glenn L McGrath76620622004-01-13 10:19:37 +00005966
5967 if (subtype == VSPLUS || subtype == VSLENGTH)
5968 STADJUST(-len, expdest);
5969 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00005970}
5971
5972
Eric Andersencb57d552001-06-28 07:25:16 +00005973/*
5974 * Record the fact that we have to scan this region of the
5975 * string for IFS characters.
5976 */
Eric Andersenc470f442003-07-28 09:56:35 +00005977static void
5978recordregion(int start, int end, int nulonly)
Eric Andersencb57d552001-06-28 07:25:16 +00005979{
5980 struct ifsregion *ifsp;
5981
5982 if (ifslastp == NULL) {
5983 ifsp = &ifsfirst;
5984 } else {
Denis Vlasenkob012b102007-02-19 22:43:01 +00005985 INT_OFF;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00005986 ifsp = ckmalloc(sizeof(*ifsp));
Eric Andersencb57d552001-06-28 07:25:16 +00005987 ifsp->next = NULL;
5988 ifslastp->next = ifsp;
Denis Vlasenkob012b102007-02-19 22:43:01 +00005989 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00005990 }
5991 ifslastp = ifsp;
5992 ifslastp->begoff = start;
5993 ifslastp->endoff = end;
5994 ifslastp->nulonly = nulonly;
5995}
5996
5997
Eric Andersencb57d552001-06-28 07:25:16 +00005998/*
5999 * Break the argument string into pieces based upon IFS and add the
6000 * strings to the argument list. The regions of the string to be
6001 * searched for IFS characters have been stored by recordregion.
6002 */
Eric Andersenc470f442003-07-28 09:56:35 +00006003static void
6004ifsbreakup(char *string, struct arglist *arglist)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006005{
Eric Andersencb57d552001-06-28 07:25:16 +00006006 struct ifsregion *ifsp;
6007 struct strlist *sp;
6008 char *start;
6009 char *p;
6010 char *q;
6011 const char *ifs, *realifs;
6012 int ifsspc;
6013 int nulonly;
6014
Eric Andersencb57d552001-06-28 07:25:16 +00006015 start = string;
Eric Andersencb57d552001-06-28 07:25:16 +00006016 if (ifslastp != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00006017 ifsspc = 0;
6018 nulonly = 0;
6019 realifs = ifsset() ? ifsval() : defifs;
Eric Andersencb57d552001-06-28 07:25:16 +00006020 ifsp = &ifsfirst;
6021 do {
6022 p = string + ifsp->begoff;
6023 nulonly = ifsp->nulonly;
6024 ifs = nulonly ? nullstr : realifs;
6025 ifsspc = 0;
6026 while (p < string + ifsp->endoff) {
6027 q = p;
6028 if (*p == CTLESC)
6029 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006030 if (!strchr(ifs, *p)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006031 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006032 continue;
6033 }
6034 if (!nulonly)
6035 ifsspc = (strchr(defifs, *p) != NULL);
6036 /* Ignore IFS whitespace at start */
6037 if (q == start && ifsspc) {
6038 p++;
6039 start = p;
6040 continue;
6041 }
6042 *q = '\0';
6043 sp = stalloc(sizeof(*sp));
6044 sp->text = start;
6045 *arglist->lastp = sp;
6046 arglist->lastp = &sp->next;
6047 p++;
6048 if (!nulonly) {
6049 for (;;) {
6050 if (p >= string + ifsp->endoff) {
6051 break;
6052 }
6053 q = p;
6054 if (*p == CTLESC)
6055 p++;
6056 if (strchr(ifs, *p) == NULL ) {
6057 p = q;
6058 break;
6059 } else if (strchr(defifs, *p) == NULL) {
6060 if (ifsspc) {
Eric Andersencb57d552001-06-28 07:25:16 +00006061 p++;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006062 ifsspc = 0;
6063 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00006064 p = q;
6065 break;
Denis Vlasenko9650f362007-02-23 01:04:37 +00006066 }
6067 } else
6068 p++;
Eric Andersencb57d552001-06-28 07:25:16 +00006069 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00006070 }
6071 start = p;
6072 } /* while */
6073 ifsp = ifsp->next;
6074 } while (ifsp != NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00006075 if (nulonly)
6076 goto add;
Eric Andersencb57d552001-06-28 07:25:16 +00006077 }
6078
Eric Andersenc470f442003-07-28 09:56:35 +00006079 if (!*start)
6080 return;
6081
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006082 add:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006083 sp = stalloc(sizeof(*sp));
Eric Andersencb57d552001-06-28 07:25:16 +00006084 sp->text = start;
6085 *arglist->lastp = sp;
6086 arglist->lastp = &sp->next;
6087}
6088
Eric Andersenc470f442003-07-28 09:56:35 +00006089static void
6090ifsfree(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006091{
Eric Andersenc470f442003-07-28 09:56:35 +00006092 struct ifsregion *p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006093
Denis Vlasenkob012b102007-02-19 22:43:01 +00006094 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00006095 p = ifsfirst.next;
6096 do {
6097 struct ifsregion *ifsp;
6098 ifsp = p->next;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006099 free(p);
Eric Andersenc470f442003-07-28 09:56:35 +00006100 p = ifsp;
6101 } while (p);
Eric Andersencb57d552001-06-28 07:25:16 +00006102 ifslastp = NULL;
6103 ifsfirst.next = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006104 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00006105}
6106
Eric Andersen90898442003-08-06 11:20:52 +00006107static void expmeta(char *, char *);
6108static struct strlist *expsort(struct strlist *);
6109static struct strlist *msort(struct strlist *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00006110
Eric Andersen90898442003-08-06 11:20:52 +00006111static char *expdir;
Eric Andersencb57d552001-06-28 07:25:16 +00006112
Eric Andersencb57d552001-06-28 07:25:16 +00006113
Eric Andersenc470f442003-07-28 09:56:35 +00006114static void
Eric Andersen90898442003-08-06 11:20:52 +00006115expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00006116{
Eric Andersen90898442003-08-06 11:20:52 +00006117 static const char metachars[] = {
6118 '*', '?', '[', 0
6119 };
Eric Andersencb57d552001-06-28 07:25:16 +00006120 /* TODO - EXP_REDIR */
6121
6122 while (str) {
Eric Andersen90898442003-08-06 11:20:52 +00006123 struct strlist **savelastp;
6124 struct strlist *sp;
6125 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +00006126
Eric Andersencb57d552001-06-28 07:25:16 +00006127 if (fflag)
6128 goto nometa;
Eric Andersen90898442003-08-06 11:20:52 +00006129 if (!strpbrk(str->text, metachars))
6130 goto nometa;
6131 savelastp = exparg.lastp;
6132
Denis Vlasenkob012b102007-02-19 22:43:01 +00006133 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00006134 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Eric Andersen90898442003-08-06 11:20:52 +00006135 {
6136 int i = strlen(str->text);
6137 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6138 }
6139
6140 expmeta(expdir, p);
Denis Vlasenkob012b102007-02-19 22:43:01 +00006141 free(expdir);
Eric Andersenc470f442003-07-28 09:56:35 +00006142 if (p != str->text)
Denis Vlasenkob012b102007-02-19 22:43:01 +00006143 free(p);
6144 INT_ON;
Eric Andersen90898442003-08-06 11:20:52 +00006145 if (exparg.lastp == savelastp) {
6146 /*
6147 * no matches
6148 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006149 nometa:
Eric Andersencb57d552001-06-28 07:25:16 +00006150 *exparg.lastp = str;
6151 rmescapes(str->text);
6152 exparg.lastp = &str->next;
Eric Andersen90898442003-08-06 11:20:52 +00006153 } else {
6154 *exparg.lastp = NULL;
6155 *savelastp = sp = expsort(*savelastp);
6156 while (sp->next != NULL)
6157 sp = sp->next;
6158 exparg.lastp = &sp->next;
Eric Andersencb57d552001-06-28 07:25:16 +00006159 }
6160 str = str->next;
6161 }
6162}
6163
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006164
Eric Andersencb57d552001-06-28 07:25:16 +00006165/*
Eric Andersenc470f442003-07-28 09:56:35 +00006166 * Add a file name to the list.
Eric Andersencb57d552001-06-28 07:25:16 +00006167 */
Eric Andersenc470f442003-07-28 09:56:35 +00006168static void
Eric Andersen90898442003-08-06 11:20:52 +00006169addfname(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006170{
Eric Andersencb57d552001-06-28 07:25:16 +00006171 struct strlist *sp;
6172
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006173 sp = stalloc(sizeof(*sp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006174 sp->text = ststrdup(name);
Eric Andersenc470f442003-07-28 09:56:35 +00006175 *exparg.lastp = sp;
6176 exparg.lastp = &sp->next;
Eric Andersencb57d552001-06-28 07:25:16 +00006177}
6178
6179
Eric Andersencb57d552001-06-28 07:25:16 +00006180/*
Eric Andersen90898442003-08-06 11:20:52 +00006181 * Do metacharacter (i.e. *, ?, [...]) expansion.
6182 */
Eric Andersen90898442003-08-06 11:20:52 +00006183static void
6184expmeta(char *enddir, char *name)
6185{
6186 char *p;
6187 const char *cp;
6188 char *start;
6189 char *endname;
6190 int metaflag;
Glenn L McGrath005f83a2003-09-01 08:53:32 +00006191 struct stat statb;
Eric Andersen90898442003-08-06 11:20:52 +00006192 DIR *dirp;
6193 struct dirent *dp;
6194 int atend;
6195 int matchdot;
6196
6197 metaflag = 0;
6198 start = name;
6199 for (p = name; *p; p++) {
6200 if (*p == '*' || *p == '?')
6201 metaflag = 1;
6202 else if (*p == '[') {
6203 char *q = p + 1;
6204 if (*q == '!')
6205 q++;
6206 for (;;) {
6207 if (*q == '\\')
6208 q++;
6209 if (*q == '/' || *q == '\0')
6210 break;
6211 if (*++q == ']') {
6212 metaflag = 1;
6213 break;
6214 }
6215 }
6216 } else if (*p == '\\')
6217 p++;
6218 else if (*p == '/') {
6219 if (metaflag)
6220 goto out;
6221 start = p + 1;
6222 }
6223 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006224 out:
Eric Andersen90898442003-08-06 11:20:52 +00006225 if (metaflag == 0) { /* we've reached the end of the file name */
6226 if (enddir != expdir)
6227 metaflag++;
6228 p = name;
6229 do {
6230 if (*p == '\\')
6231 p++;
6232 *enddir++ = *p;
6233 } while (*p++);
Glenn L McGrath005f83a2003-09-01 08:53:32 +00006234 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
Eric Andersen90898442003-08-06 11:20:52 +00006235 addfname(expdir);
6236 return;
6237 }
6238 endname = p;
6239 if (name < start) {
6240 p = name;
6241 do {
6242 if (*p == '\\')
6243 p++;
6244 *enddir++ = *p++;
6245 } while (p < start);
6246 }
6247 if (enddir == expdir) {
6248 cp = ".";
6249 } else if (enddir == expdir + 1 && *expdir == '/') {
6250 cp = "/";
6251 } else {
6252 cp = expdir;
6253 enddir[-1] = '\0';
6254 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006255 dirp = opendir(cp);
6256 if (dirp == NULL)
Eric Andersen90898442003-08-06 11:20:52 +00006257 return;
6258 if (enddir != expdir)
6259 enddir[-1] = '/';
6260 if (*endname == 0) {
6261 atend = 1;
6262 } else {
6263 atend = 0;
6264 *endname++ = '\0';
6265 }
6266 matchdot = 0;
6267 p = start;
6268 if (*p == '\\')
6269 p++;
6270 if (*p == '.')
6271 matchdot++;
6272 while (! intpending && (dp = readdir(dirp)) != NULL) {
6273 if (dp->d_name[0] == '.' && ! matchdot)
6274 continue;
6275 if (pmatch(start, dp->d_name)) {
6276 if (atend) {
6277 scopy(dp->d_name, enddir);
6278 addfname(expdir);
6279 } else {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00006280 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
Eric Andersen90898442003-08-06 11:20:52 +00006281 continue;
6282 p[-1] = '/';
6283 expmeta(p, endname);
6284 }
6285 }
6286 }
6287 closedir(dirp);
6288 if (! atend)
6289 endname[-1] = '/';
6290}
6291
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006292
Eric Andersen90898442003-08-06 11:20:52 +00006293/*
6294 * Sort the results of file name expansion. It calculates the number of
6295 * strings to sort and then calls msort (short for merge sort) to do the
6296 * work.
6297 */
Eric Andersen90898442003-08-06 11:20:52 +00006298static struct strlist *
6299expsort(struct strlist *str)
6300{
6301 int len;
6302 struct strlist *sp;
6303
6304 len = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006305 for (sp = str; sp; sp = sp->next)
Eric Andersen90898442003-08-06 11:20:52 +00006306 len++;
6307 return msort(str, len);
6308}
6309
6310
6311static struct strlist *
6312msort(struct strlist *list, int len)
6313{
6314 struct strlist *p, *q = NULL;
6315 struct strlist **lpp;
6316 int half;
6317 int n;
6318
6319 if (len <= 1)
6320 return list;
6321 half = len >> 1;
6322 p = list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006323 for (n = half; --n >= 0; ) {
Eric Andersen90898442003-08-06 11:20:52 +00006324 q = p;
6325 p = p->next;
6326 }
6327 q->next = NULL; /* terminate first half of list */
6328 q = msort(list, half); /* sort first half of list */
6329 p = msort(p, len - half); /* sort second half */
6330 lpp = &list;
6331 for (;;) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00006332#if ENABLE_LOCALE_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +00006333 if (strcoll(p->text, q->text) < 0)
6334#else
6335 if (strcmp(p->text, q->text) < 0)
6336#endif
6337 {
6338 *lpp = p;
6339 lpp = &p->next;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006340 p = *lpp;
6341 if (p == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +00006342 *lpp = q;
6343 break;
6344 }
6345 } else {
6346 *lpp = q;
6347 lpp = &q->next;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006348 q = *lpp;
6349 if (q == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +00006350 *lpp = p;
6351 break;
6352 }
6353 }
6354 }
6355 return list;
6356}
6357
6358
6359/*
Eric Andersencb57d552001-06-28 07:25:16 +00006360 * Returns true if the pattern matches the string.
6361 */
Rob Landley88621d72006-08-29 19:41:06 +00006362static int patmatch(char *pattern, const char *string)
Eric Andersen2870d962001-07-02 17:27:21 +00006363{
Eric Andersenc470f442003-07-28 09:56:35 +00006364 return pmatch(preglob(pattern, 0, 0), string);
Eric Andersencb57d552001-06-28 07:25:16 +00006365}
6366
6367
Eric Andersencb57d552001-06-28 07:25:16 +00006368/*
6369 * Remove any CTLESC characters from a string.
6370 */
Eric Andersenc470f442003-07-28 09:56:35 +00006371static char *
6372_rmescapes(char *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00006373{
6374 char *p, *q, *r;
6375 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
Eric Andersenc470f442003-07-28 09:56:35 +00006376 unsigned inquotes;
6377 int notescaped;
6378 int globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006379
6380 p = strpbrk(str, qchars);
6381 if (!p) {
6382 return str;
6383 }
6384 q = p;
6385 r = str;
6386 if (flag & RMESCAPE_ALLOC) {
6387 size_t len = p - str;
Eric Andersenc470f442003-07-28 09:56:35 +00006388 size_t fulllen = len + strlen(p) + 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006389
Eric Andersenc470f442003-07-28 09:56:35 +00006390 if (flag & RMESCAPE_GROW) {
6391 r = makestrspace(fulllen, expdest);
6392 } else if (flag & RMESCAPE_HEAP) {
6393 r = ckmalloc(fulllen);
6394 } else {
6395 r = stalloc(fulllen);
6396 }
6397 q = r;
Eric Andersencb57d552001-06-28 07:25:16 +00006398 if (len > 0) {
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00006399 q = memcpy(q, str, len) + len;
Eric Andersencb57d552001-06-28 07:25:16 +00006400 }
6401 }
Eric Andersenc470f442003-07-28 09:56:35 +00006402 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
6403 globbing = flag & RMESCAPE_GLOB;
6404 notescaped = globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006405 while (*p) {
6406 if (*p == CTLQUOTEMARK) {
Eric Andersenc470f442003-07-28 09:56:35 +00006407 inquotes = ~inquotes;
Eric Andersencb57d552001-06-28 07:25:16 +00006408 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00006409 notescaped = globbing;
Eric Andersencb57d552001-06-28 07:25:16 +00006410 continue;
6411 }
Eric Andersenc470f442003-07-28 09:56:35 +00006412 if (*p == '\\') {
6413 /* naked back slash */
6414 notescaped = 0;
6415 goto copy;
6416 }
Eric Andersencb57d552001-06-28 07:25:16 +00006417 if (*p == CTLESC) {
6418 p++;
Eric Andersenc470f442003-07-28 09:56:35 +00006419 if (notescaped && inquotes && *p != '/') {
Eric Andersencb57d552001-06-28 07:25:16 +00006420 *q++ = '\\';
6421 }
6422 }
Eric Andersenc470f442003-07-28 09:56:35 +00006423 notescaped = globbing;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006424 copy:
Eric Andersencb57d552001-06-28 07:25:16 +00006425 *q++ = *p++;
6426 }
6427 *q = '\0';
Eric Andersenc470f442003-07-28 09:56:35 +00006428 if (flag & RMESCAPE_GROW) {
6429 expdest = r;
6430 STADJUST(q - r + 1, expdest);
6431 }
Eric Andersencb57d552001-06-28 07:25:16 +00006432 return r;
6433}
Eric Andersencb57d552001-06-28 07:25:16 +00006434
6435
Eric Andersencb57d552001-06-28 07:25:16 +00006436/*
6437 * See if a pattern matches in a case statement.
6438 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006439static int
Eric Andersenc470f442003-07-28 09:56:35 +00006440casematch(union node *pattern, char *val)
Eric Andersen2870d962001-07-02 17:27:21 +00006441{
Eric Andersencb57d552001-06-28 07:25:16 +00006442 struct stackmark smark;
6443 int result;
Eric Andersencb57d552001-06-28 07:25:16 +00006444
6445 setstackmark(&smark);
6446 argbackq = pattern->narg.backquote;
6447 STARTSTACKSTR(expdest);
6448 ifslastp = NULL;
6449 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Eric Andersenc470f442003-07-28 09:56:35 +00006450 STACKSTRNUL(expdest);
6451 result = patmatch(stackblock(), val);
Eric Andersencb57d552001-06-28 07:25:16 +00006452 popstackmark(&smark);
6453 return result;
6454}
6455
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006456
Eric Andersencb57d552001-06-28 07:25:16 +00006457/*
6458 * Our own itoa().
6459 */
Eric Andersenc470f442003-07-28 09:56:35 +00006460static int
Eric Andersened9ecf72004-06-22 08:29:45 +00006461cvtnum(arith_t num)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006462{
Eric Andersencb57d552001-06-28 07:25:16 +00006463 int len;
6464
Eric Andersenc470f442003-07-28 09:56:35 +00006465 expdest = makestrspace(32, expdest);
Denis Vlasenko131ae172007-02-18 13:00:19 +00006466#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersened9ecf72004-06-22 08:29:45 +00006467 len = fmtstr(expdest, 32, "%lld", (long long) num);
6468#else
Eric Andersenc470f442003-07-28 09:56:35 +00006469 len = fmtstr(expdest, 32, "%ld", num);
Eric Andersened9ecf72004-06-22 08:29:45 +00006470#endif
Eric Andersenc470f442003-07-28 09:56:35 +00006471 STADJUST(len, expdest);
6472 return len;
Eric Andersencb57d552001-06-28 07:25:16 +00006473}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006474
Denis Vlasenko2da584f2007-02-19 22:44:05 +00006475static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
Eric Andersenc470f442003-07-28 09:56:35 +00006476static void
6477varunset(const char *end, const char *var, const char *umsg, int varflags)
Eric Andersencb57d552001-06-28 07:25:16 +00006478{
Eric Andersenc470f442003-07-28 09:56:35 +00006479 const char *msg;
6480 const char *tail;
6481
6482 tail = nullstr;
6483 msg = "parameter not set";
6484 if (umsg) {
6485 if (*end == CTLENDVAR) {
6486 if (varflags & VSNUL)
6487 tail = " or null";
6488 } else
6489 msg = umsg;
6490 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00006491 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
Eric Andersencb57d552001-06-28 07:25:16 +00006492}
Eric Andersen90898442003-08-06 11:20:52 +00006493
6494
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006495/* input.c */
Eric Andersencb57d552001-06-28 07:25:16 +00006496
Eric Andersencb57d552001-06-28 07:25:16 +00006497/*
Eric Andersen90898442003-08-06 11:20:52 +00006498 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00006499 */
6500
Eric Andersenc470f442003-07-28 09:56:35 +00006501#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00006502
Eric Andersenc470f442003-07-28 09:56:35 +00006503static void pushfile(void);
Eric Andersencb57d552001-06-28 07:25:16 +00006504
Eric Andersencb57d552001-06-28 07:25:16 +00006505/*
Eric Andersenc470f442003-07-28 09:56:35 +00006506 * Read a character from the script, returning PEOF on end of file.
6507 * Nul characters in the input are silently discarded.
6508 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006509static int preadbuffer(void);
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006510#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
Eric Andersenc470f442003-07-28 09:56:35 +00006511
Denis Vlasenko131ae172007-02-18 13:00:19 +00006512#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Eric Andersenc470f442003-07-28 09:56:35 +00006513#define pgetc_macro() pgetc()
6514static int
6515pgetc(void)
6516{
6517 return pgetc_as_macro();
6518}
6519#else
6520#define pgetc_macro() pgetc_as_macro()
6521static int
6522pgetc(void)
6523{
6524 return pgetc_macro();
6525}
6526#endif
6527
Eric Andersenc470f442003-07-28 09:56:35 +00006528/*
6529 * Same as pgetc(), but ignores PEOA.
6530 */
Denis Vlasenko131ae172007-02-18 13:00:19 +00006531#if ENABLE_ASH_ALIAS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006532static int
6533pgetc2(void)
Eric Andersenc470f442003-07-28 09:56:35 +00006534{
6535 int c;
6536
6537 do {
6538 c = pgetc_macro();
6539 } while (c == PEOA);
6540 return c;
6541}
6542#else
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006543static int
6544pgetc2(void)
Eric Andersenc470f442003-07-28 09:56:35 +00006545{
6546 return pgetc_macro();
6547}
6548#endif
6549
Glenn L McGrath28939ad2004-07-21 10:20:19 +00006550/*
6551 * Read a line from the script.
6552 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006553static char *
6554pfgets(char *line, int len)
Glenn L McGrath28939ad2004-07-21 10:20:19 +00006555{
6556 char *p = line;
6557 int nleft = len;
6558 int c;
6559
6560 while (--nleft > 0) {
6561 c = pgetc2();
6562 if (c == PEOF) {
6563 if (p == line)
6564 return NULL;
6565 break;
6566 }
6567 *p++ = c;
6568 if (c == '\n')
6569 break;
6570 }
6571 *p = '\0';
6572 return line;
6573}
6574
Denis Vlasenko38f63192007-01-22 09:03:07 +00006575#if ENABLE_FEATURE_EDITING_VI
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006576#define setvimode(on) do { \
6577 if (on) line_input_state->flags |= VI_MODE; \
6578 else line_input_state->flags &= ~VI_MODE; \
6579} while (0)
6580#else
6581#define setvimode(on) viflag = 0 /* forcibly keep the option off */
6582#endif
6583
Denis Vlasenkoaa744452007-02-23 01:04:22 +00006584static int
6585preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006586{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006587 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00006588 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006589 parsenextc = buf;
6590
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006591 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00006592#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00006593 if (!iflag || parsefile->fd)
6594 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6595 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00006596#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006597 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00006598#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006599 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
6600 if (nr == 0) {
6601 /* Ctrl+C pressed */
6602 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00006603 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006604 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00006605 raise(SIGINT);
6606 return 1;
6607 }
Eric Andersenc470f442003-07-28 09:56:35 +00006608 goto retry;
6609 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00006610 if (nr < 0 && errno == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00006611 /* Ctrl+D presend */
6612 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006613 }
Eric Andersencb57d552001-06-28 07:25:16 +00006614 }
6615#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00006616 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00006617#endif
6618
6619 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006620 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6621 int flags = fcntl(0, F_GETFL, 0);
6622 if (flags >= 0 && flags & O_NONBLOCK) {
Eric Andersenc470f442003-07-28 09:56:35 +00006623 flags &=~ O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00006624 if (fcntl(0, F_SETFL, flags) >= 0) {
6625 out2str("sh: turning off NDELAY mode\n");
6626 goto retry;
6627 }
6628 }
6629 }
6630 }
6631 return nr;
6632}
6633
6634/*
6635 * Refill the input buffer and return the next input character:
6636 *
6637 * 1) If a string was pushed back on the input, pop it;
6638 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6639 * from a string so we can't refill the buffer, return EOF.
6640 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6641 * 4) Process input up to the next newline, deleting nul characters.
6642 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006643static int
Eric Andersenc470f442003-07-28 09:56:35 +00006644preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006645{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006646 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00006647 int more;
6648 char savec;
6649
6650 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00006651#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00006652 if (parsenleft == -1 && parsefile->strpush->ap &&
6653 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00006654 return PEOA;
6655 }
Eric Andersen2870d962001-07-02 17:27:21 +00006656#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006657 popstring();
6658 if (--parsenleft >= 0)
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006659 return SC2INT(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00006660 }
6661 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6662 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006663 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00006664
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006665 more = parselleft;
6666 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006667 again:
6668 more = preadfd();
6669 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006670 parselleft = parsenleft = EOF_NLEFT;
6671 return PEOF;
6672 }
6673 }
6674
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006675 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00006676
6677 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006678 for (;;) {
6679 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00006680
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006681 more--;
6682 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00006683
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006684 if (!c)
6685 memmove(q, q + 1, more);
6686 else {
6687 q++;
6688 if (c == '\n') {
6689 parsenleft = q - parsenextc - 1;
6690 break;
6691 }
Eric Andersencb57d552001-06-28 07:25:16 +00006692 }
6693
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006694 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006695 parsenleft = q - parsenextc - 1;
6696 if (parsenleft < 0)
6697 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006698 break;
Eric Andersencb57d552001-06-28 07:25:16 +00006699 }
6700 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006701 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00006702
6703 savec = *q;
6704 *q = '\0';
6705
6706 if (vflag) {
6707 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00006708 }
6709
6710 *q = savec;
6711
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00006712 return SC2INT(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00006713}
6714
Eric Andersenc470f442003-07-28 09:56:35 +00006715/*
6716 * Undo the last call to pgetc. Only one character may be pushed back.
6717 * PEOF may be pushed back.
6718 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006719static void
Eric Andersenc470f442003-07-28 09:56:35 +00006720pungetc(void)
6721{
6722 parsenleft++;
6723 parsenextc--;
6724}
Eric Andersencb57d552001-06-28 07:25:16 +00006725
6726/*
6727 * Push a string back onto the input at this current parsefile level.
6728 * We handle aliases this way.
6729 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006730static void
Eric Andersenc470f442003-07-28 09:56:35 +00006731pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00006732{
Eric Andersencb57d552001-06-28 07:25:16 +00006733 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00006734 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00006735
Eric Andersenc470f442003-07-28 09:56:35 +00006736 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00006737 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00006738/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6739 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00006740 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00006741 sp->prev = parsefile->strpush;
6742 parsefile->strpush = sp;
6743 } else
6744 sp = parsefile->strpush = &(parsefile->basestrpush);
6745 sp->prevstring = parsenextc;
6746 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00006747#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00006748 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00006749 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00006750 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00006751 sp->string = s;
6752 }
Eric Andersen2870d962001-07-02 17:27:21 +00006753#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006754 parsenextc = s;
6755 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006756 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00006757}
6758
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006759static void
Eric Andersenc470f442003-07-28 09:56:35 +00006760popstring(void)
6761{
6762 struct strpush *sp = parsefile->strpush;
6763
Denis Vlasenkob012b102007-02-19 22:43:01 +00006764 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00006765#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00006766 if (sp->ap) {
6767 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6768 checkkwd |= CHKALIAS;
6769 }
6770 if (sp->string != sp->ap->val) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00006771 free(sp->string);
Eric Andersenc470f442003-07-28 09:56:35 +00006772 }
6773 sp->ap->flag &= ~ALIASINUSE;
6774 if (sp->ap->flag & ALIASDEAD) {
6775 unalias(sp->ap->name);
6776 }
6777 }
6778#endif
6779 parsenextc = sp->prevstring;
6780 parsenleft = sp->prevnleft;
6781/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6782 parsefile->strpush = sp->prev;
6783 if (sp != &(parsefile->basestrpush))
Denis Vlasenkob012b102007-02-19 22:43:01 +00006784 free(sp);
6785 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00006786}
6787
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006788
Eric Andersenc470f442003-07-28 09:56:35 +00006789/*
6790 * Set the input to take input from a file. If push is set, push the
6791 * old input onto the stack first.
6792 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006793static int
6794setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00006795{
6796 int fd;
6797 int fd2;
6798
Denis Vlasenkob012b102007-02-19 22:43:01 +00006799 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006800 fd = open(fname, O_RDONLY);
6801 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006802 if (flags & INPUT_NOFILE_OK)
6803 goto out;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006804 ash_msg_and_raise_error("Can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006805 }
Eric Andersenc470f442003-07-28 09:56:35 +00006806 if (fd < 10) {
6807 fd2 = copyfd(fd, 10);
6808 close(fd);
6809 if (fd2 < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00006810 ash_msg_and_raise_error("Out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00006811 fd = fd2;
6812 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006813 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00006814 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00006815 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006816 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00006817}
6818
6819
6820/*
6821 * Like setinputfile, but takes an open file descriptor. Call this with
6822 * interrupts off.
6823 */
Eric Andersenc470f442003-07-28 09:56:35 +00006824static void
6825setinputfd(int fd, int push)
6826{
6827 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
6828 if (push) {
6829 pushfile();
6830 parsefile->buf = 0;
6831 }
6832 parsefile->fd = fd;
6833 if (parsefile->buf == NULL)
6834 parsefile->buf = ckmalloc(IBUFSIZ);
6835 parselleft = parsenleft = 0;
6836 plinno = 1;
6837}
6838
Eric Andersencb57d552001-06-28 07:25:16 +00006839
Eric Andersencb57d552001-06-28 07:25:16 +00006840/*
6841 * Like setinputfile, but takes input from a string.
6842 */
Eric Andersenc470f442003-07-28 09:56:35 +00006843static void
6844setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00006845{
Denis Vlasenkob012b102007-02-19 22:43:01 +00006846 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00006847 pushfile();
6848 parsenextc = string;
6849 parsenleft = strlen(string);
6850 parsefile->buf = NULL;
6851 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006852 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00006853}
6854
6855
Eric Andersencb57d552001-06-28 07:25:16 +00006856/*
6857 * To handle the "." command, a stack of input files is used. Pushfile
6858 * adds a new entry to the stack and popfile restores the previous level.
6859 */
Eric Andersenc470f442003-07-28 09:56:35 +00006860static void
6861pushfile(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006862{
Eric Andersencb57d552001-06-28 07:25:16 +00006863 struct parsefile *pf;
6864
6865 parsefile->nleft = parsenleft;
6866 parsefile->lleft = parselleft;
6867 parsefile->nextc = parsenextc;
6868 parsefile->linno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00006869 pf = ckmalloc(sizeof(*pf));
Eric Andersencb57d552001-06-28 07:25:16 +00006870 pf->prev = parsefile;
6871 pf->fd = -1;
6872 pf->strpush = NULL;
6873 pf->basestrpush.prev = NULL;
6874 parsefile = pf;
6875}
6876
Eric Andersenc470f442003-07-28 09:56:35 +00006877
6878static void
6879popfile(void)
6880{
6881 struct parsefile *pf = parsefile;
6882
Denis Vlasenkob012b102007-02-19 22:43:01 +00006883 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00006884 if (pf->fd >= 0)
6885 close(pf->fd);
6886 if (pf->buf)
Denis Vlasenkob012b102007-02-19 22:43:01 +00006887 free(pf->buf);
Eric Andersenc470f442003-07-28 09:56:35 +00006888 while (pf->strpush)
6889 popstring();
6890 parsefile = pf->prev;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006891 free(pf);
Eric Andersenc470f442003-07-28 09:56:35 +00006892 parsenleft = parsefile->nleft;
6893 parselleft = parsefile->lleft;
6894 parsenextc = parsefile->nextc;
6895 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00006896 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00006897}
Eric Andersencb57d552001-06-28 07:25:16 +00006898
6899
Eric Andersen2870d962001-07-02 17:27:21 +00006900/*
Eric Andersenc470f442003-07-28 09:56:35 +00006901 * Return to top level.
6902 */
Eric Andersenc470f442003-07-28 09:56:35 +00006903static void
6904popallfiles(void)
Eric Andersen74400cc2001-10-18 04:11:39 +00006905{
Eric Andersenc470f442003-07-28 09:56:35 +00006906 while (parsefile != &basepf)
6907 popfile();
Eric Andersen2870d962001-07-02 17:27:21 +00006908}
6909
Eric Andersen2870d962001-07-02 17:27:21 +00006910
Eric Andersenc470f442003-07-28 09:56:35 +00006911/*
6912 * Close the file(s) that the shell is reading commands from. Called
6913 * after a fork is done.
6914 */
Eric Andersenc470f442003-07-28 09:56:35 +00006915static void
6916closescript(void)
6917{
6918 popallfiles();
6919 if (parsefile->fd > 0) {
6920 close(parsefile->fd);
6921 parsefile->fd = 0;
6922 }
6923}
Eric Andersenc470f442003-07-28 09:56:35 +00006924
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00006925/* jobs.c */
Eric Andersenc470f442003-07-28 09:56:35 +00006926
6927/* mode flags for set_curjob */
6928#define CUR_DELETE 2
6929#define CUR_RUNNING 1
6930#define CUR_STOPPED 0
6931
6932/* mode flags for dowait */
6933#define DOWAIT_NORMAL 0
6934#define DOWAIT_BLOCK 1
6935
6936/* array of jobs */
6937static struct job *jobtab;
6938/* size of array */
6939static unsigned njobs;
6940#if JOBS
6941/* pgrp of shell on invocation */
6942static int initialpgrp;
6943static int ttyfd = -1;
6944#endif
6945/* current job */
6946static struct job *curjob;
6947/* number of presumed living untracked jobs */
6948static int jobless;
6949
6950static void set_curjob(struct job *, unsigned);
6951#if JOBS
6952static int restartjob(struct job *, int);
6953static void xtcsetpgrp(int, pid_t);
6954static char *commandtext(union node *);
6955static void cmdlist(union node *, int);
6956static void cmdtxt(union node *);
6957static void cmdputs(const char *);
6958static void showpipe(struct job *, FILE *);
6959#endif
6960static int sprint_status(char *, int, int);
6961static void freejob(struct job *);
6962static struct job *getjob(const char *, int);
6963static struct job *growjobtab(void);
6964static void forkchild(struct job *, union node *, int);
6965static void forkparent(struct job *, union node *, int, pid_t);
6966static int dowait(int, struct job *);
6967static int getstatus(struct job *);
6968
6969static void
6970set_curjob(struct job *jp, unsigned mode)
6971{
6972 struct job *jp1;
6973 struct job **jpp, **curp;
6974
6975 /* first remove from list */
6976 jpp = curp = &curjob;
6977 do {
6978 jp1 = *jpp;
6979 if (jp1 == jp)
6980 break;
6981 jpp = &jp1->prev_job;
6982 } while (1);
6983 *jpp = jp1->prev_job;
6984
6985 /* Then re-insert in correct position */
6986 jpp = curp;
6987 switch (mode) {
6988 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00006989#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00006990 abort();
6991#endif
6992 case CUR_DELETE:
6993 /* job being deleted */
6994 break;
6995 case CUR_RUNNING:
6996 /* newly created job or backgrounded job,
6997 put after all stopped jobs. */
6998 do {
6999 jp1 = *jpp;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007000#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007001 if (!jp1 || jp1->state != JOBSTOPPED)
7002#endif
7003 break;
7004 jpp = &jp1->prev_job;
7005 } while (1);
7006 /* FALLTHROUGH */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007007#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007008 case CUR_STOPPED:
7009#endif
7010 /* newly stopped job - becomes curjob */
7011 jp->prev_job = *jpp;
7012 *jpp = jp;
7013 break;
7014 }
7015}
7016
7017#if JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007018/*
7019 * Turn job control on and off.
7020 *
7021 * Note: This code assumes that the third arg to ioctl is a character
7022 * pointer, which is true on Berkeley systems but not System V. Since
7023 * System V doesn't have job control yet, this isn't a problem now.
Eric Andersenc470f442003-07-28 09:56:35 +00007024 *
7025 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00007026 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007027static void
Eric Andersenc470f442003-07-28 09:56:35 +00007028setjobctl(int on)
Eric Andersencb57d552001-06-28 07:25:16 +00007029{
Eric Andersenc470f442003-07-28 09:56:35 +00007030 int fd;
7031 int pgrp;
7032
7033 if (on == jobctl || rootshell == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00007034 return;
Eric Andersenc470f442003-07-28 09:56:35 +00007035 if (on) {
7036 int ofd;
7037 ofd = fd = open(_PATH_TTY, O_RDWR);
7038 if (fd < 0) {
Denis Vlasenko2f0c0d02007-01-21 00:41:04 +00007039 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
7040 * That sometimes helps to acquire controlling tty.
7041 * Obviously, a workaround for bugs when someone
7042 * failed to provide a controlling tty to bash! :) */
Eric Andersenc470f442003-07-28 09:56:35 +00007043 fd += 3;
7044 while (!isatty(fd) && --fd >= 0)
7045 ;
7046 }
7047 fd = fcntl(fd, F_DUPFD, 10);
7048 close(ofd);
7049 if (fd < 0)
7050 goto out;
7051 fcntl(fd, F_SETFD, FD_CLOEXEC);
7052 do { /* while we are in the background */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007053 pgrp = tcgetpgrp(fd);
7054 if (pgrp < 0) {
7055 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007056 ash_msg("can't access tty; job control turned off");
Eric Andersenc470f442003-07-28 09:56:35 +00007057 mflag = on = 0;
7058 goto close;
Eric Andersencb57d552001-06-28 07:25:16 +00007059 }
Eric Andersenc470f442003-07-28 09:56:35 +00007060 if (pgrp == getpgrp())
Glenn L McGrath7040ecc2003-01-06 16:27:07 +00007061 break;
7062 killpg(0, SIGTTIN);
7063 } while (1);
Eric Andersenc470f442003-07-28 09:56:35 +00007064 initialpgrp = pgrp;
7065
Eric Andersencb57d552001-06-28 07:25:16 +00007066 setsignal(SIGTSTP);
7067 setsignal(SIGTTOU);
7068 setsignal(SIGTTIN);
Eric Andersenc470f442003-07-28 09:56:35 +00007069 pgrp = rootpid;
7070 setpgid(0, pgrp);
7071 xtcsetpgrp(fd, pgrp);
7072 } else {
7073 /* turning job control off */
7074 fd = ttyfd;
7075 pgrp = initialpgrp;
7076 xtcsetpgrp(fd, pgrp);
7077 setpgid(0, pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00007078 setsignal(SIGTSTP);
7079 setsignal(SIGTTOU);
7080 setsignal(SIGTTIN);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007081 close:
Eric Andersenc470f442003-07-28 09:56:35 +00007082 close(fd);
7083 fd = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00007084 }
Eric Andersenc470f442003-07-28 09:56:35 +00007085 ttyfd = fd;
7086 jobctl = on;
Eric Andersencb57d552001-06-28 07:25:16 +00007087}
Eric Andersencb57d552001-06-28 07:25:16 +00007088
Eric Andersenc470f442003-07-28 09:56:35 +00007089static int
Eric Andersen90898442003-08-06 11:20:52 +00007090killcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007091{
7092 int signo = -1;
7093 int list = 0;
7094 int i;
7095 pid_t pid;
7096 struct job *jp;
7097
7098 if (argc <= 1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007099 usage:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007100 ash_msg_and_raise_error(
Eric Andersenc470f442003-07-28 09:56:35 +00007101"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
7102"kill -l [exitstatus]"
7103 );
Eric Andersencb57d552001-06-28 07:25:16 +00007104 }
7105
Eric Andersenc470f442003-07-28 09:56:35 +00007106 if (**++argv == '-') {
Rob Landleyc9c1a412006-07-12 19:17:55 +00007107 signo = get_signum(*argv + 1);
Eric Andersencb57d552001-06-28 07:25:16 +00007108 if (signo < 0) {
7109 int c;
7110
7111 while ((c = nextopt("ls:")) != '\0')
7112 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +00007113 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007114#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007115 abort();
7116#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007117 case 'l':
7118 list = 1;
7119 break;
7120 case 's':
Rob Landleyc9c1a412006-07-12 19:17:55 +00007121 signo = get_signum(optionarg);
Eric Andersencb57d552001-06-28 07:25:16 +00007122 if (signo < 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007123 ash_msg_and_raise_error(
Eric Andersenc470f442003-07-28 09:56:35 +00007124 "invalid signal number or name: %s",
7125 optionarg
7126 );
Eric Andersencb57d552001-06-28 07:25:16 +00007127 }
Eric Andersen2870d962001-07-02 17:27:21 +00007128 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007129 }
Eric Andersenc470f442003-07-28 09:56:35 +00007130 argv = argptr;
Eric Andersencb57d552001-06-28 07:25:16 +00007131 } else
Eric Andersenc470f442003-07-28 09:56:35 +00007132 argv++;
Eric Andersencb57d552001-06-28 07:25:16 +00007133 }
7134
7135 if (!list && signo < 0)
7136 signo = SIGTERM;
7137
Eric Andersenc470f442003-07-28 09:56:35 +00007138 if ((signo < 0 || !*argv) ^ list) {
Eric Andersencb57d552001-06-28 07:25:16 +00007139 goto usage;
7140 }
7141
7142 if (list) {
Eric Andersen34506362001-08-02 05:02:46 +00007143 const char *name;
7144
Eric Andersenc470f442003-07-28 09:56:35 +00007145 if (!*argv) {
Eric Andersencb57d552001-06-28 07:25:16 +00007146 for (i = 1; i < NSIG; i++) {
Rob Landleyc9c1a412006-07-12 19:17:55 +00007147 name = get_signame(i);
7148 if (isdigit(*name))
Eric Andersenc470f442003-07-28 09:56:35 +00007149 out1fmt(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00007150 }
7151 return 0;
7152 }
Rob Landleyc9c1a412006-07-12 19:17:55 +00007153 name = get_signame(signo);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007154 if (!isdigit(*name))
Denis Vlasenkob012b102007-02-19 22:43:01 +00007155 ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007156 out1fmt(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00007157 return 0;
7158 }
7159
Eric Andersenc470f442003-07-28 09:56:35 +00007160 i = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007161 do {
Eric Andersenc470f442003-07-28 09:56:35 +00007162 if (**argv == '%') {
7163 jp = getjob(*argv, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00007164 pid = -jp->ps[0].pid;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00007165 } else {
7166 pid = **argv == '-' ?
7167 -number(*argv + 1) : number(*argv);
7168 }
Eric Andersenc470f442003-07-28 09:56:35 +00007169 if (kill(pid, signo) != 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007170 ash_msg("(%d) - %m", pid);
Eric Andersenc470f442003-07-28 09:56:35 +00007171 i = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00007172 }
Eric Andersenc470f442003-07-28 09:56:35 +00007173 } while (*++argv);
7174
7175 return i;
7176}
7177#endif /* JOBS */
7178
Denis Vlasenko666da5e2006-12-26 18:17:42 +00007179#if JOBS || DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007180static int
7181jobno(const struct job *jp)
7182{
7183 return jp - jobtab + 1;
Eric Andersencb57d552001-06-28 07:25:16 +00007184}
7185#endif
7186
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007187#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007188static int
7189fgcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007190{
Eric Andersenc470f442003-07-28 09:56:35 +00007191 struct job *jp;
7192 FILE *out;
7193 int mode;
7194 int retval;
7195
7196 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
7197 nextopt(nullstr);
7198 argv = argptr;
7199 out = stdout;
7200 do {
7201 jp = getjob(*argv, 1);
7202 if (mode == FORK_BG) {
7203 set_curjob(jp, CUR_RUNNING);
7204 fprintf(out, "[%d] ", jobno(jp));
7205 }
7206 outstr(jp->ps->cmd, out);
7207 showpipe(jp, out);
7208 retval = restartjob(jp, mode);
7209 } while (*argv && *++argv);
7210 return retval;
7211}
7212
7213static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
7214
7215
7216static int
7217restartjob(struct job *jp, int mode)
7218{
7219 struct procstat *ps;
7220 int i;
7221 int status;
7222 pid_t pgid;
7223
Denis Vlasenkob012b102007-02-19 22:43:01 +00007224 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007225 if (jp->state == JOBDONE)
7226 goto out;
7227 jp->state = JOBRUNNING;
7228 pgid = jp->ps->pid;
7229 if (mode == FORK_FG)
7230 xtcsetpgrp(ttyfd, pgid);
7231 killpg(pgid, SIGCONT);
7232 ps = jp->ps;
7233 i = jp->nprocs;
7234 do {
7235 if (WIFSTOPPED(ps->status)) {
7236 ps->status = -1;
7237 }
7238 } while (ps++, --i);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007239 out:
Eric Andersenc470f442003-07-28 09:56:35 +00007240 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007241 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007242 return status;
7243}
7244#endif
7245
7246static int
7247sprint_status(char *s, int status, int sigonly)
7248{
7249 int col;
7250 int st;
7251
7252 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00007253 if (!WIFEXITED(status)) {
Eric Andersenc470f442003-07-28 09:56:35 +00007254#if JOBS
Glenn L McGratha3822de2003-09-15 14:42:39 +00007255 if (WIFSTOPPED(status))
7256 st = WSTOPSIG(status);
Eric Andersena48b0a32003-10-22 10:56:47 +00007257 else
Eric Andersenc470f442003-07-28 09:56:35 +00007258#endif
Eric Andersena48b0a32003-10-22 10:56:47 +00007259 st = WTERMSIG(status);
Eric Andersenc470f442003-07-28 09:56:35 +00007260 if (sigonly) {
Glenn L McGrath4ddddd12003-11-25 20:45:38 +00007261 if (st == SIGINT || st == SIGPIPE)
Eric Andersena48b0a32003-10-22 10:56:47 +00007262 goto out;
7263#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007264 if (WIFSTOPPED(status))
7265 goto out;
Eric Andersena48b0a32003-10-22 10:56:47 +00007266#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007267 }
7268 st &= 0x7f;
Glenn L McGrath5c2c8ec2003-11-14 21:01:26 +00007269 col = fmtstr(s, 32, strsignal(st));
Eric Andersenc470f442003-07-28 09:56:35 +00007270 if (WCOREDUMP(status)) {
7271 col += fmtstr(s + col, 16, " (core dumped)");
7272 }
7273 } else if (!sigonly) {
Eric Andersena48b0a32003-10-22 10:56:47 +00007274 st = WEXITSTATUS(status);
Eric Andersenc470f442003-07-28 09:56:35 +00007275 if (st)
7276 col = fmtstr(s, 16, "Done(%d)", st);
7277 else
7278 col = fmtstr(s, 16, "Done");
7279 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007280 out:
Eric Andersenc470f442003-07-28 09:56:35 +00007281 return col;
7282}
7283
7284#if JOBS
7285static void
7286showjob(FILE *out, struct job *jp, int mode)
7287{
7288 struct procstat *ps;
7289 struct procstat *psend;
7290 int col;
7291 int indent;
7292 char s[80];
7293
7294 ps = jp->ps;
7295
7296 if (mode & SHOW_PGID) {
7297 /* just output process (group) id of pipeline */
7298 fprintf(out, "%d\n", ps->pid);
7299 return;
7300 }
7301
7302 col = fmtstr(s, 16, "[%d] ", jobno(jp));
7303 indent = col;
7304
7305 if (jp == curjob)
7306 s[col - 2] = '+';
7307 else if (curjob && jp == curjob->prev_job)
7308 s[col - 2] = '-';
7309
7310 if (mode & SHOW_PID)
7311 col += fmtstr(s + col, 16, "%d ", ps->pid);
7312
7313 psend = ps + jp->nprocs;
7314
7315 if (jp->state == JOBRUNNING) {
7316 scopy("Running", s + col);
7317 col += strlen("Running");
7318 } else {
7319 int status = psend[-1].status;
7320#if JOBS
7321 if (jp->state == JOBSTOPPED)
7322 status = jp->stopstatus;
7323#endif
7324 col += sprint_status(s + col, status, 0);
7325 }
7326
7327 goto start;
7328
7329 do {
7330 /* for each process */
7331 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007332 start:
Eric Andersen90898442003-08-06 11:20:52 +00007333 fprintf(out, "%s%*c%s",
Eric Andersenc470f442003-07-28 09:56:35 +00007334 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
7335 );
7336 if (!(mode & SHOW_PID)) {
7337 showpipe(jp, out);
7338 break;
7339 }
7340 if (++ps == psend) {
7341 outcslow('\n', out);
7342 break;
7343 }
7344 } while (1);
7345
7346 jp->changed = 0;
7347
7348 if (jp->state == JOBDONE) {
7349 TRACE(("showjob: freeing job %d\n", jobno(jp)));
7350 freejob(jp);
7351 }
7352}
7353
7354
7355static int
7356jobscmd(int argc, char **argv)
7357{
7358 int mode, m;
7359 FILE *out;
7360
7361 mode = 0;
7362 while ((m = nextopt("lp")))
7363 if (m == 'l')
7364 mode = SHOW_PID;
7365 else
7366 mode = SHOW_PGID;
7367
7368 out = stdout;
7369 argv = argptr;
7370 if (*argv)
7371 do
7372 showjob(out, getjob(*argv,0), mode);
7373 while (*++argv);
7374 else
7375 showjobs(out, mode);
7376
Eric Andersencb57d552001-06-28 07:25:16 +00007377 return 0;
7378}
7379
7380
7381/*
7382 * Print a list of jobs. If "change" is nonzero, only print jobs whose
7383 * statuses have changed since the last call to showjobs.
Eric Andersencb57d552001-06-28 07:25:16 +00007384 */
Eric Andersenc470f442003-07-28 09:56:35 +00007385static void
7386showjobs(FILE *out, int mode)
Eric Andersencb57d552001-06-28 07:25:16 +00007387{
Eric Andersencb57d552001-06-28 07:25:16 +00007388 struct job *jp;
Eric Andersencb57d552001-06-28 07:25:16 +00007389
Eric Andersenc470f442003-07-28 09:56:35 +00007390 TRACE(("showjobs(%x) called\n", mode));
7391
7392 /* If not even one one job changed, there is nothing to do */
7393 while (dowait(DOWAIT_NORMAL, NULL) > 0)
7394 continue;
7395
7396 for (jp = curjob; jp; jp = jp->prev_job) {
7397 if (!(mode & SHOW_CHANGED) || jp->changed)
7398 showjob(out, jp, mode);
Eric Andersencb57d552001-06-28 07:25:16 +00007399 }
7400}
Eric Andersenc470f442003-07-28 09:56:35 +00007401#endif /* JOBS */
Eric Andersencb57d552001-06-28 07:25:16 +00007402
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007403
Eric Andersencb57d552001-06-28 07:25:16 +00007404/*
7405 * Mark a job structure as unused.
7406 */
Eric Andersenc470f442003-07-28 09:56:35 +00007407static void
7408freejob(struct job *jp)
Eric Andersen62483552001-07-10 06:09:16 +00007409{
Eric Andersenc470f442003-07-28 09:56:35 +00007410 struct procstat *ps;
Eric Andersencb57d552001-06-28 07:25:16 +00007411 int i;
7412
Denis Vlasenkob012b102007-02-19 22:43:01 +00007413 INT_OFF;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007414 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Eric Andersencb57d552001-06-28 07:25:16 +00007415 if (ps->cmd != nullstr)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007416 free(ps->cmd);
Eric Andersencb57d552001-06-28 07:25:16 +00007417 }
7418 if (jp->ps != &jp->ps0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007419 free(jp->ps);
Eric Andersencb57d552001-06-28 07:25:16 +00007420 jp->used = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00007421 set_curjob(jp, CUR_DELETE);
Denis Vlasenkob012b102007-02-19 22:43:01 +00007422 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007423}
7424
7425
Eric Andersenc470f442003-07-28 09:56:35 +00007426static int
7427waitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007428{
7429 struct job *job;
Eric Andersenc470f442003-07-28 09:56:35 +00007430 int retval;
Eric Andersencb57d552001-06-28 07:25:16 +00007431 struct job *jp;
7432
Denis Vlasenkob012b102007-02-19 22:43:01 +00007433 EXSIGON;
Eric Andersenc470f442003-07-28 09:56:35 +00007434
7435 nextopt(nullstr);
7436 retval = 0;
7437
7438 argv = argptr;
7439 if (!*argv) {
7440 /* wait for all jobs */
7441 for (;;) {
7442 jp = curjob;
7443 while (1) {
7444 if (!jp) {
7445 /* no running procs */
7446 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00007447 }
Eric Andersenc470f442003-07-28 09:56:35 +00007448 if (jp->state == JOBRUNNING)
Eric Andersencb57d552001-06-28 07:25:16 +00007449 break;
Eric Andersenc470f442003-07-28 09:56:35 +00007450 jp->waited = 1;
7451 jp = jp->prev_job;
Eric Andersencb57d552001-06-28 07:25:16 +00007452 }
Eric Andersenc470f442003-07-28 09:56:35 +00007453 dowait(DOWAIT_BLOCK, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00007454 }
7455 }
Eric Andersenc470f442003-07-28 09:56:35 +00007456
7457 retval = 127;
7458 do {
7459 if (**argv != '%') {
7460 pid_t pid = number(*argv);
7461 job = curjob;
7462 goto start;
7463 do {
7464 if (job->ps[job->nprocs - 1].pid == pid)
7465 break;
7466 job = job->prev_job;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007467 start:
Eric Andersenc470f442003-07-28 09:56:35 +00007468 if (!job)
7469 goto repeat;
7470 } while (1);
7471 } else
7472 job = getjob(*argv, 0);
7473 /* loop until process terminated or stopped */
7474 while (job->state == JOBRUNNING)
7475 dowait(DOWAIT_BLOCK, 0);
7476 job->waited = 1;
7477 retval = getstatus(job);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007478 repeat:
Eric Andersenc470f442003-07-28 09:56:35 +00007479 ;
7480 } while (*++argv);
7481
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007482 out:
Eric Andersenc470f442003-07-28 09:56:35 +00007483 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00007484}
7485
7486
Eric Andersencb57d552001-06-28 07:25:16 +00007487/*
7488 * Convert a job name to a job structure.
7489 */
Eric Andersenc470f442003-07-28 09:56:35 +00007490static struct job *
7491getjob(const char *name, int getctl)
Eric Andersen2870d962001-07-02 17:27:21 +00007492{
Eric Andersencb57d552001-06-28 07:25:16 +00007493 struct job *jp;
Eric Andersenc470f442003-07-28 09:56:35 +00007494 struct job *found;
7495 const char *err_msg = "No such job: %s";
7496 unsigned num;
7497 int c;
7498 const char *p;
7499 char *(*match)(const char *, const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00007500
Eric Andersenc470f442003-07-28 09:56:35 +00007501 jp = curjob;
7502 p = name;
7503 if (!p)
7504 goto currentjob;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007505
Eric Andersenc470f442003-07-28 09:56:35 +00007506 if (*p != '%')
7507 goto err;
7508
7509 c = *++p;
7510 if (!c)
7511 goto currentjob;
7512
7513 if (!p[1]) {
7514 if (c == '+' || c == '%') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007515 currentjob:
Eric Andersenc470f442003-07-28 09:56:35 +00007516 err_msg = "No current job";
7517 goto check;
7518 } else if (c == '-') {
7519 if (jp)
7520 jp = jp->prev_job;
7521 err_msg = "No previous job";
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007522 check:
Eric Andersenc470f442003-07-28 09:56:35 +00007523 if (!jp)
7524 goto err;
7525 goto gotit;
Eric Andersencb57d552001-06-28 07:25:16 +00007526 }
7527 }
Eric Andersenc470f442003-07-28 09:56:35 +00007528
7529 if (is_number(p)) {
7530 num = atoi(p);
7531 if (num < njobs) {
7532 jp = jobtab + num - 1;
7533 if (jp->used)
7534 goto gotit;
7535 goto err;
7536 }
7537 }
7538
7539 match = prefix;
7540 if (*p == '?') {
7541 match = strstr;
7542 p++;
7543 }
7544
7545 found = 0;
7546 while (1) {
7547 if (!jp)
7548 goto err;
7549 if (match(jp->ps[0].cmd, p)) {
7550 if (found)
7551 goto err;
7552 found = jp;
7553 err_msg = "%s: ambiguous";
7554 }
7555 jp = jp->prev_job;
7556 }
7557
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007558 gotit:
Eric Andersenc470f442003-07-28 09:56:35 +00007559#if JOBS
7560 err_msg = "job %s not created under job control";
7561 if (getctl && jp->jobctl == 0)
7562 goto err;
7563#endif
7564 return jp;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007565 err:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007566 ash_msg_and_raise_error(err_msg, name);
Eric Andersencb57d552001-06-28 07:25:16 +00007567}
7568
7569
Eric Andersencb57d552001-06-28 07:25:16 +00007570/*
Eric Andersenc470f442003-07-28 09:56:35 +00007571 * Return a new job structure.
7572 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00007573 */
7574
Eric Andersenc470f442003-07-28 09:56:35 +00007575static struct job *
7576makejob(union node *node, int nprocs)
Eric Andersencb57d552001-06-28 07:25:16 +00007577{
7578 int i;
7579 struct job *jp;
7580
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007581 for (i = njobs, jp = jobtab; ; jp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00007582 if (--i < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00007583 jp = growjobtab();
Eric Andersencb57d552001-06-28 07:25:16 +00007584 break;
7585 }
7586 if (jp->used == 0)
7587 break;
Eric Andersenc470f442003-07-28 09:56:35 +00007588 if (jp->state != JOBDONE || !jp->waited)
7589 continue;
7590#if JOBS
7591 if (jobctl)
7592 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00007593#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007594 freejob(jp);
7595 break;
Eric Andersencb57d552001-06-28 07:25:16 +00007596 }
Eric Andersenc470f442003-07-28 09:56:35 +00007597 memset(jp, 0, sizeof(*jp));
7598#if JOBS
7599 if (jobctl)
7600 jp->jobctl = 1;
7601#endif
7602 jp->prev_job = curjob;
7603 curjob = jp;
7604 jp->used = 1;
7605 jp->ps = &jp->ps0;
7606 if (nprocs > 1) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007607 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
Eric Andersenc470f442003-07-28 09:56:35 +00007608 }
7609 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007610 jobno(jp)));
Eric Andersenc470f442003-07-28 09:56:35 +00007611 return jp;
7612}
7613
7614static struct job *
7615growjobtab(void)
7616{
7617 size_t len;
7618 ptrdiff_t offset;
7619 struct job *jp, *jq;
7620
7621 len = njobs * sizeof(*jp);
7622 jq = jobtab;
7623 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
7624
7625 offset = (char *)jp - (char *)jq;
7626 if (offset) {
7627 /* Relocate pointers */
7628 size_t l = len;
7629
7630 jq = (struct job *)((char *)jq + l);
7631 while (l) {
7632 l -= sizeof(*jp);
7633 jq--;
7634#define joff(p) ((struct job *)((char *)(p) + l))
7635#define jmove(p) (p) = (void *)((char *)(p) + offset)
Glenn L McGrath2f325a02004-08-06 01:49:04 +00007636 if (xlikely(joff(jp)->ps == &jq->ps0))
Eric Andersenc470f442003-07-28 09:56:35 +00007637 jmove(joff(jp)->ps);
7638 if (joff(jp)->prev_job)
7639 jmove(joff(jp)->prev_job);
7640 }
7641 if (curjob)
7642 jmove(curjob);
7643#undef joff
7644#undef jmove
7645 }
7646
7647 njobs += 4;
7648 jobtab = jp;
7649 jp = (struct job *)((char *)jp + len);
7650 jq = jp + 3;
7651 do {
7652 jq->used = 0;
7653 } while (--jq >= jp);
Eric Andersencb57d552001-06-28 07:25:16 +00007654 return jp;
7655}
7656
7657
7658/*
Eric Andersenc470f442003-07-28 09:56:35 +00007659 * Fork off a subshell. If we are doing job control, give the subshell its
Eric Andersencb57d552001-06-28 07:25:16 +00007660 * own process group. Jp is a job structure that the job is to be added to.
7661 * N is the command that will be evaluated by the child. Both jp and n may
7662 * be NULL. The mode parameter can be one of the following:
Eric Andersen2870d962001-07-02 17:27:21 +00007663 * FORK_FG - Fork off a foreground process.
7664 * FORK_BG - Fork off a background process.
7665 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
7666 * process group even if job control is on.
Eric Andersencb57d552001-06-28 07:25:16 +00007667 *
7668 * When job control is turned off, background processes have their standard
7669 * input redirected to /dev/null (except for the second and later processes
7670 * in a pipeline).
Eric Andersenc470f442003-07-28 09:56:35 +00007671 *
7672 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00007673 */
Rob Landley88621d72006-08-29 19:41:06 +00007674static void forkchild(struct job *jp, union node *n, int mode)
Eric Andersencb57d552001-06-28 07:25:16 +00007675{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007676 int oldlvl;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007677
Eric Andersenc470f442003-07-28 09:56:35 +00007678 TRACE(("Child shell %d\n", getpid()));
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007679 oldlvl = shlvl;
7680 shlvl++;
Eric Andersenc470f442003-07-28 09:56:35 +00007681
7682 closescript();
7683 clear_traps();
7684#if JOBS
7685 /* do job control only in root shell */
7686 jobctl = 0;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007687 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
Eric Andersenc470f442003-07-28 09:56:35 +00007688 pid_t pgrp;
7689
7690 if (jp->nprocs == 0)
7691 pgrp = getpid();
7692 else
7693 pgrp = jp->ps[0].pid;
7694 /* This can fail because we are doing it in the parent also */
7695 (void)setpgid(0, pgrp);
7696 if (mode == FORK_FG)
7697 xtcsetpgrp(ttyfd, pgrp);
7698 setsignal(SIGTSTP);
7699 setsignal(SIGTTOU);
7700 } else
Eric Andersen62483552001-07-10 06:09:16 +00007701#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007702 if (mode == FORK_BG) {
7703 ignoresig(SIGINT);
7704 ignoresig(SIGQUIT);
7705 if (jp->nprocs == 0) {
7706 close(0);
Bernhard Reutner-Fischer0a8812b2006-05-19 13:12:21 +00007707 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007708 ash_msg_and_raise_error("Can't open %s", bb_dev_null);
Eric Andersencb57d552001-06-28 07:25:16 +00007709 }
Eric Andersencb57d552001-06-28 07:25:16 +00007710 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007711 if (!oldlvl && iflag) {
Eric Andersenc470f442003-07-28 09:56:35 +00007712 setsignal(SIGINT);
7713 setsignal(SIGQUIT);
7714 setsignal(SIGTERM);
7715 }
7716 for (jp = curjob; jp; jp = jp->prev_job)
7717 freejob(jp);
7718 jobless = 0;
7719}
7720
Rob Landley88621d72006-08-29 19:41:06 +00007721static void forkparent(struct job *jp, union node *n, int mode, pid_t pid)
Eric Andersenc470f442003-07-28 09:56:35 +00007722{
7723 TRACE(("In parent shell: child = %d\n", pid));
7724 if (!jp) {
7725 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
7726 jobless++;
7727 return;
7728 }
7729#if JOBS
7730 if (mode != FORK_NOJOB && jp->jobctl) {
7731 int pgrp;
7732
7733 if (jp->nprocs == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00007734 pgrp = pid;
7735 else
7736 pgrp = jp->ps[0].pid;
Eric Andersenc470f442003-07-28 09:56:35 +00007737 /* This can fail because we are doing it in the child also */
7738 (void)setpgid(pid, pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00007739 }
Eric Andersen62483552001-07-10 06:09:16 +00007740#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007741 if (mode == FORK_BG) {
7742 backgndpid = pid; /* set $! */
7743 set_curjob(jp, CUR_RUNNING);
7744 }
Eric Andersencb57d552001-06-28 07:25:16 +00007745 if (jp) {
7746 struct procstat *ps = &jp->ps[jp->nprocs++];
7747 ps->pid = pid;
7748 ps->status = -1;
7749 ps->cmd = nullstr;
Eric Andersenc470f442003-07-28 09:56:35 +00007750#if JOBS
7751 if (jobctl && n)
Eric Andersencb57d552001-06-28 07:25:16 +00007752 ps->cmd = commandtext(n);
Eric Andersenc470f442003-07-28 09:56:35 +00007753#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007754 }
Eric Andersencb57d552001-06-28 07:25:16 +00007755}
7756
Eric Andersenc470f442003-07-28 09:56:35 +00007757static int
7758forkshell(struct job *jp, union node *n, int mode)
7759{
7760 int pid;
Eric Andersencb57d552001-06-28 07:25:16 +00007761
Eric Andersenc470f442003-07-28 09:56:35 +00007762 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
7763 pid = fork();
7764 if (pid < 0) {
7765 TRACE(("Fork failed, errno=%d", errno));
7766 if (jp)
7767 freejob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00007768 ash_msg_and_raise_error("Cannot fork");
Eric Andersenc470f442003-07-28 09:56:35 +00007769 }
7770 if (pid == 0)
7771 forkchild(jp, n, mode);
7772 else
7773 forkparent(jp, n, mode, pid);
7774 return pid;
7775}
Eric Andersencb57d552001-06-28 07:25:16 +00007776
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007777
Eric Andersencb57d552001-06-28 07:25:16 +00007778/*
7779 * Wait for job to finish.
7780 *
7781 * Under job control we have the problem that while a child process is
7782 * running interrupts generated by the user are sent to the child but not
7783 * to the shell. This means that an infinite loop started by an inter-
7784 * active user may be hard to kill. With job control turned off, an
7785 * interactive user may place an interactive program inside a loop. If
7786 * the interactive program catches interrupts, the user doesn't want
7787 * these interrupts to also abort the loop. The approach we take here
7788 * is to have the shell ignore interrupt signals while waiting for a
Eric Andersenaff114c2004-04-14 17:51:38 +00007789 * foreground process to terminate, and then send itself an interrupt
Eric Andersencb57d552001-06-28 07:25:16 +00007790 * signal if the child process was terminated by an interrupt signal.
7791 * Unfortunately, some programs want to do a bit of cleanup and then
7792 * exit on interrupt; unless these processes terminate themselves by
7793 * sending a signal to themselves (instead of calling exit) they will
7794 * confuse this approach.
Eric Andersenc470f442003-07-28 09:56:35 +00007795 *
7796 * Called with interrupts off.
Eric Andersencb57d552001-06-28 07:25:16 +00007797 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007798static int
Eric Andersenc470f442003-07-28 09:56:35 +00007799waitforjob(struct job *jp)
Eric Andersen62483552001-07-10 06:09:16 +00007800{
Eric Andersencb57d552001-06-28 07:25:16 +00007801 int st;
Eric Andersencb57d552001-06-28 07:25:16 +00007802
Eric Andersenc470f442003-07-28 09:56:35 +00007803 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
7804 while (jp->state == JOBRUNNING) {
7805 dowait(DOWAIT_BLOCK, jp);
Eric Andersencb57d552001-06-28 07:25:16 +00007806 }
Eric Andersenc470f442003-07-28 09:56:35 +00007807 st = getstatus(jp);
7808#if JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007809 if (jp->jobctl) {
Eric Andersenc470f442003-07-28 09:56:35 +00007810 xtcsetpgrp(ttyfd, rootpid);
Eric Andersencb57d552001-06-28 07:25:16 +00007811 /*
7812 * This is truly gross.
7813 * If we're doing job control, then we did a TIOCSPGRP which
7814 * caused us (the shell) to no longer be in the controlling
7815 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
7816 * intuit from the subprocess exit status whether a SIGINT
Eric Andersenc470f442003-07-28 09:56:35 +00007817 * occurred, and if so interrupt ourselves. Yuck. - mycroft
Eric Andersencb57d552001-06-28 07:25:16 +00007818 */
Eric Andersenc470f442003-07-28 09:56:35 +00007819 if (jp->sigint)
Eric Andersencb57d552001-06-28 07:25:16 +00007820 raise(SIGINT);
7821 }
Eric Andersen2870d962001-07-02 17:27:21 +00007822 if (jp->state == JOBDONE)
Eric Andersencb57d552001-06-28 07:25:16 +00007823#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007824 freejob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00007825 return st;
7826}
7827
7828
Eric Andersen62483552001-07-10 06:09:16 +00007829/*
7830 * Do a wait system call. If job control is compiled in, we accept
7831 * stopped processes. If block is zero, we return a value of zero
7832 * rather than blocking.
7833 *
7834 * System V doesn't have a non-blocking wait system call. It does
7835 * have a SIGCLD signal that is sent to a process when one of it's
7836 * children dies. The obvious way to use SIGCLD would be to install
7837 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7838 * was received, and have waitproc bump another counter when it got
7839 * the status of a process. Waitproc would then know that a wait
7840 * system call would not block if the two counters were different.
7841 * This approach doesn't work because if a process has children that
7842 * have not been waited for, System V will send it a SIGCLD when it
7843 * installs a signal handler for SIGCLD. What this means is that when
7844 * a child exits, the shell will be sent SIGCLD signals continuously
7845 * until is runs out of stack space, unless it does a wait call before
7846 * restoring the signal handler. The code below takes advantage of
7847 * this (mis)feature by installing a signal handler for SIGCLD and
7848 * then checking to see whether it was called. If there are any
7849 * children to be waited for, it will be.
7850 *
Eric Andersenc470f442003-07-28 09:56:35 +00007851 * If neither SYSV nor BSD is defined, we don't implement nonblocking
7852 * waits at all. In this case, the user will not be informed when
7853 * a background process until the next time she runs a real program
7854 * (as opposed to running a builtin command or just typing return),
7855 * and the jobs command may give out of date information.
Eric Andersen62483552001-07-10 06:09:16 +00007856 */
Rob Landley88621d72006-08-29 19:41:06 +00007857static int waitproc(int block, int *status)
Eric Andersen62483552001-07-10 06:09:16 +00007858{
Eric Andersenc470f442003-07-28 09:56:35 +00007859 int flags = 0;
Eric Andersen62483552001-07-10 06:09:16 +00007860
Eric Andersenc470f442003-07-28 09:56:35 +00007861#if JOBS
Eric Andersen62483552001-07-10 06:09:16 +00007862 if (jobctl)
7863 flags |= WUNTRACED;
7864#endif
7865 if (block == 0)
7866 flags |= WNOHANG;
Eric Andersenc470f442003-07-28 09:56:35 +00007867 return wait3(status, flags, (struct rusage *)NULL);
Eric Andersen62483552001-07-10 06:09:16 +00007868}
7869
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007870
Eric Andersenc470f442003-07-28 09:56:35 +00007871/*
7872 * Wait for a process to terminate.
7873 */
Eric Andersenc470f442003-07-28 09:56:35 +00007874static int
7875dowait(int block, struct job *job)
Eric Andersencb57d552001-06-28 07:25:16 +00007876{
7877 int pid;
7878 int status;
Eric Andersencb57d552001-06-28 07:25:16 +00007879 struct job *jp;
7880 struct job *thisjob;
Eric Andersenc470f442003-07-28 09:56:35 +00007881 int state;
Eric Andersencb57d552001-06-28 07:25:16 +00007882
7883 TRACE(("dowait(%d) called\n", block));
Eric Andersenc470f442003-07-28 09:56:35 +00007884 pid = waitproc(block, &status);
7885 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Eric Andersencb57d552001-06-28 07:25:16 +00007886 if (pid <= 0)
7887 return pid;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007888 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007889 thisjob = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00007890 for (jp = curjob; jp; jp = jp->prev_job) {
7891 struct procstat *sp;
7892 struct procstat *spend;
7893 if (jp->state == JOBDONE)
7894 continue;
7895 state = JOBDONE;
7896 spend = jp->ps + jp->nprocs;
7897 sp = jp->ps;
7898 do {
7899 if (sp->pid == pid) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007900 TRACE(("Job %d: changing status of proc %d "
7901 "from 0x%x to 0x%x\n",
7902 jobno(jp), pid, sp->status, status));
Eric Andersenc470f442003-07-28 09:56:35 +00007903 sp->status = status;
7904 thisjob = jp;
Eric Andersencb57d552001-06-28 07:25:16 +00007905 }
Eric Andersenc470f442003-07-28 09:56:35 +00007906 if (sp->status == -1)
7907 state = JOBRUNNING;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007908#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007909 if (state == JOBRUNNING)
7910 continue;
7911 if (WIFSTOPPED(sp->status)) {
7912 jp->stopstatus = sp->status;
7913 state = JOBSTOPPED;
7914 }
Eric Andersencb57d552001-06-28 07:25:16 +00007915#endif
Eric Andersenc470f442003-07-28 09:56:35 +00007916 } while (++sp < spend);
7917 if (thisjob)
7918 goto gotjob;
7919 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007920#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007921 if (!WIFSTOPPED(status))
7922#endif
7923
7924 jobless--;
7925 goto out;
7926
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007927 gotjob:
Eric Andersenc470f442003-07-28 09:56:35 +00007928 if (state != JOBRUNNING) {
7929 thisjob->changed = 1;
7930
7931 if (thisjob->state != state) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007932 TRACE(("Job %d: changing state from %d to %d\n",
7933 jobno(thisjob), thisjob->state, state));
Eric Andersenc470f442003-07-28 09:56:35 +00007934 thisjob->state = state;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007935#if JOBS
Eric Andersenc470f442003-07-28 09:56:35 +00007936 if (state == JOBSTOPPED) {
7937 set_curjob(thisjob, CUR_STOPPED);
Eric Andersencb57d552001-06-28 07:25:16 +00007938 }
Eric Andersenc470f442003-07-28 09:56:35 +00007939#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007940 }
7941 }
Eric Andersencb57d552001-06-28 07:25:16 +00007942
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007943 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007944 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007945
7946 if (thisjob && thisjob == job) {
7947 char s[48 + 1];
7948 int len;
7949
7950 len = sprint_status(s, status, 1);
7951 if (len) {
7952 s[len] = '\n';
7953 s[len + 1] = 0;
7954 out2str(s);
Eric Andersencb57d552001-06-28 07:25:16 +00007955 }
Eric Andersencb57d552001-06-28 07:25:16 +00007956 }
7957 return pid;
7958}
7959
7960
Eric Andersencb57d552001-06-28 07:25:16 +00007961/*
7962 * return 1 if there are stopped jobs, otherwise 0
7963 */
Eric Andersenc470f442003-07-28 09:56:35 +00007964int
7965stoppedjobs(void)
Eric Andersencb57d552001-06-28 07:25:16 +00007966{
Eric Andersencb57d552001-06-28 07:25:16 +00007967 struct job *jp;
Eric Andersenc470f442003-07-28 09:56:35 +00007968 int retval;
Eric Andersencb57d552001-06-28 07:25:16 +00007969
Eric Andersenc470f442003-07-28 09:56:35 +00007970 retval = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007971 if (job_warning)
Eric Andersenc470f442003-07-28 09:56:35 +00007972 goto out;
7973 jp = curjob;
7974 if (jp && jp->state == JOBSTOPPED) {
7975 out2str("You have stopped jobs.\n");
7976 job_warning = 2;
7977 retval++;
Eric Andersencb57d552001-06-28 07:25:16 +00007978 }
7979
Eric Andersenc470f442003-07-28 09:56:35 +00007980out:
7981 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00007982}
7983
7984/*
7985 * Return a string identifying a command (to be printed by the
Eric Andersenc470f442003-07-28 09:56:35 +00007986 * jobs command).
Eric Andersencb57d552001-06-28 07:25:16 +00007987 */
Eric Andersenc470f442003-07-28 09:56:35 +00007988#if JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007989static char *cmdnextc;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007990
Eric Andersenc470f442003-07-28 09:56:35 +00007991static char *
7992commandtext(union node *n)
Eric Andersen2870d962001-07-02 17:27:21 +00007993{
Eric Andersenc470f442003-07-28 09:56:35 +00007994 char *name;
Eric Andersencb57d552001-06-28 07:25:16 +00007995
Eric Andersenc470f442003-07-28 09:56:35 +00007996 STARTSTACKSTR(cmdnextc);
7997 cmdtxt(n);
7998 name = stackblock();
7999 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
8000 name, cmdnextc, cmdnextc));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008001 return ckstrdup(name);
Eric Andersencb57d552001-06-28 07:25:16 +00008002}
8003
Eric Andersenc470f442003-07-28 09:56:35 +00008004static void
8005cmdtxt(union node *n)
Eric Andersen2870d962001-07-02 17:27:21 +00008006{
Eric Andersencb57d552001-06-28 07:25:16 +00008007 union node *np;
8008 struct nodelist *lp;
8009 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +00008010 char s[2];
8011
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008012 if (!n)
8013 return;
Eric Andersencb57d552001-06-28 07:25:16 +00008014 switch (n->type) {
Eric Andersenc470f442003-07-28 09:56:35 +00008015 default:
8016#if DEBUG
8017 abort();
8018#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008019 case NPIPE:
Eric Andersenc470f442003-07-28 09:56:35 +00008020 lp = n->npipe.cmdlist;
8021 for (;;) {
Eric Andersencb57d552001-06-28 07:25:16 +00008022 cmdtxt(lp->n);
Eric Andersenc470f442003-07-28 09:56:35 +00008023 lp = lp->next;
8024 if (!lp)
8025 break;
8026 cmdputs(" | ");
Eric Andersencb57d552001-06-28 07:25:16 +00008027 }
8028 break;
Eric Andersenc470f442003-07-28 09:56:35 +00008029 case NSEMI:
8030 p = "; ";
8031 goto binop;
8032 case NAND:
8033 p = " && ";
8034 goto binop;
8035 case NOR:
8036 p = " || ";
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008037 binop:
Eric Andersenc470f442003-07-28 09:56:35 +00008038 cmdtxt(n->nbinary.ch1);
8039 cmdputs(p);
8040 n = n->nbinary.ch2;
8041 goto donode;
Eric Andersencb57d552001-06-28 07:25:16 +00008042 case NREDIR:
8043 case NBACKGND:
Eric Andersenc470f442003-07-28 09:56:35 +00008044 n = n->nredir.n;
8045 goto donode;
8046 case NNOT:
8047 cmdputs("!");
8048 n = n->nnot.com;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008049 donode:
Eric Andersenc470f442003-07-28 09:56:35 +00008050 cmdtxt(n);
Eric Andersencb57d552001-06-28 07:25:16 +00008051 break;
8052 case NIF:
8053 cmdputs("if ");
8054 cmdtxt(n->nif.test);
8055 cmdputs("; then ");
Eric Andersenc470f442003-07-28 09:56:35 +00008056 n = n->nif.ifpart;
8057 if (n->nif.elsepart) {
8058 cmdtxt(n);
8059 cmdputs("; else ");
8060 n = n->nif.elsepart;
8061 }
8062 p = "; fi";
8063 goto dotail;
8064 case NSUBSHELL:
8065 cmdputs("(");
8066 n = n->nredir.n;
8067 p = ")";
8068 goto dotail;
Eric Andersencb57d552001-06-28 07:25:16 +00008069 case NWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00008070 p = "while ";
Eric Andersencb57d552001-06-28 07:25:16 +00008071 goto until;
8072 case NUNTIL:
Eric Andersenc470f442003-07-28 09:56:35 +00008073 p = "until ";
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008074 until:
Eric Andersenc470f442003-07-28 09:56:35 +00008075 cmdputs(p);
Eric Andersencb57d552001-06-28 07:25:16 +00008076 cmdtxt(n->nbinary.ch1);
Eric Andersenc470f442003-07-28 09:56:35 +00008077 n = n->nbinary.ch2;
8078 p = "; done";
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008079 dodo:
Eric Andersencb57d552001-06-28 07:25:16 +00008080 cmdputs("; do ");
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008081 dotail:
Eric Andersenc470f442003-07-28 09:56:35 +00008082 cmdtxt(n);
8083 goto dotail2;
Eric Andersencb57d552001-06-28 07:25:16 +00008084 case NFOR:
8085 cmdputs("for ");
8086 cmdputs(n->nfor.var);
Eric Andersenc470f442003-07-28 09:56:35 +00008087 cmdputs(" in ");
8088 cmdlist(n->nfor.args, 1);
8089 n = n->nfor.body;
8090 p = "; done";
8091 goto dodo;
Eric Andersencb57d552001-06-28 07:25:16 +00008092 case NDEFUN:
8093 cmdputs(n->narg.text);
Eric Andersenc470f442003-07-28 09:56:35 +00008094 p = "() { ... }";
8095 goto dotail2;
Eric Andersencb57d552001-06-28 07:25:16 +00008096 case NCMD:
Eric Andersenc470f442003-07-28 09:56:35 +00008097 cmdlist(n->ncmd.args, 1);
8098 cmdlist(n->ncmd.redirect, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00008099 break;
8100 case NARG:
Eric Andersenc470f442003-07-28 09:56:35 +00008101 p = n->narg.text;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008102 dotail2:
Eric Andersencb57d552001-06-28 07:25:16 +00008103 cmdputs(p);
Eric Andersencb57d552001-06-28 07:25:16 +00008104 break;
8105 case NHERE:
8106 case NXHERE:
Eric Andersenc470f442003-07-28 09:56:35 +00008107 p = "<<...";
8108 goto dotail2;
8109 case NCASE:
8110 cmdputs("case ");
8111 cmdputs(n->ncase.expr->narg.text);
8112 cmdputs(" in ");
8113 for (np = n->ncase.cases; np; np = np->nclist.next) {
8114 cmdtxt(np->nclist.pattern);
8115 cmdputs(") ");
8116 cmdtxt(np->nclist.body);
8117 cmdputs(";; ");
8118 }
8119 p = "esac";
8120 goto dotail2;
8121 case NTO:
8122 p = ">";
8123 goto redir;
8124 case NCLOBBER:
8125 p = ">|";
8126 goto redir;
8127 case NAPPEND:
8128 p = ">>";
8129 goto redir;
8130 case NTOFD:
8131 p = ">&";
8132 goto redir;
8133 case NFROM:
8134 p = "<";
8135 goto redir;
8136 case NFROMFD:
8137 p = "<&";
8138 goto redir;
8139 case NFROMTO:
8140 p = "<>";
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008141 redir:
Eric Andersenc470f442003-07-28 09:56:35 +00008142 s[0] = n->nfile.fd + '0';
8143 s[1] = '\0';
8144 cmdputs(s);
8145 cmdputs(p);
8146 if (n->type == NTOFD || n->type == NFROMFD) {
8147 s[0] = n->ndup.dupfd + '0';
8148 p = s;
8149 goto dotail2;
8150 } else {
8151 n = n->nfile.fname;
8152 goto donode;
8153 }
Eric Andersencb57d552001-06-28 07:25:16 +00008154 }
8155}
Eric Andersencb57d552001-06-28 07:25:16 +00008156
Eric Andersenc470f442003-07-28 09:56:35 +00008157static void
8158cmdlist(union node *np, int sep)
Eric Andersen2870d962001-07-02 17:27:21 +00008159{
Eric Andersenc470f442003-07-28 09:56:35 +00008160 for (; np; np = np->narg.next) {
8161 if (!sep)
8162 cmdputs(spcstr);
8163 cmdtxt(np);
8164 if (sep && np->narg.next)
8165 cmdputs(spcstr);
8166 }
Eric Andersencb57d552001-06-28 07:25:16 +00008167}
8168
Eric Andersenc470f442003-07-28 09:56:35 +00008169static void
8170cmdputs(const char *s)
8171{
8172 const char *p, *str;
8173 char c, cc[2] = " ";
8174 char *nextc;
8175 int subtype = 0;
8176 int quoted = 0;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008177 static const char vstype[VSTYPE + 1][4] = {
8178 "", "}", "-", "+", "?", "=",
8179 "%", "%%", "#", "##"
Eric Andersenc470f442003-07-28 09:56:35 +00008180 };
Eric Andersenc470f442003-07-28 09:56:35 +00008181 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
8182 p = s;
8183 while ((c = *p++) != 0) {
8184 str = 0;
8185 switch (c) {
8186 case CTLESC:
8187 c = *p++;
8188 break;
8189 case CTLVAR:
8190 subtype = *p++;
8191 if ((subtype & VSTYPE) == VSLENGTH)
8192 str = "${#";
8193 else
8194 str = "${";
8195 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
8196 quoted ^= 1;
8197 c = '"';
8198 } else
8199 goto dostr;
8200 break;
8201 case CTLENDVAR:
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008202 str = "\"}" + !(quoted & 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008203 quoted >>= 1;
8204 subtype = 0;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008205 goto dostr;
Eric Andersenc470f442003-07-28 09:56:35 +00008206 case CTLBACKQ:
8207 str = "$(...)";
8208 goto dostr;
8209 case CTLBACKQ+CTLQUOTE:
8210 str = "\"$(...)\"";
8211 goto dostr;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008212#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00008213 case CTLARI:
8214 str = "$((";
8215 goto dostr;
8216 case CTLENDARI:
8217 str = "))";
8218 goto dostr;
8219#endif
8220 case CTLQUOTEMARK:
8221 quoted ^= 1;
8222 c = '"';
8223 break;
8224 case '=':
8225 if (subtype == 0)
8226 break;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008227 if ((subtype & VSTYPE) != VSNORMAL)
8228 quoted <<= 1;
Eric Andersenc470f442003-07-28 09:56:35 +00008229 str = vstype[subtype & VSTYPE];
8230 if (subtype & VSNUL)
8231 c = ':';
8232 else
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008233 goto checkstr;
Eric Andersenc470f442003-07-28 09:56:35 +00008234 break;
8235 case '\'':
8236 case '\\':
8237 case '"':
8238 case '$':
8239 /* These can only happen inside quotes */
8240 cc[0] = c;
8241 str = cc;
8242 c = '\\';
8243 break;
8244 default:
8245 break;
8246 }
8247 USTPUTC(c, nextc);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008248 checkstr:
Eric Andersenc470f442003-07-28 09:56:35 +00008249 if (!str)
8250 continue;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008251 dostr:
Eric Andersenc470f442003-07-28 09:56:35 +00008252 while ((c = *str++)) {
8253 USTPUTC(c, nextc);
8254 }
8255 }
8256 if (quoted & 1) {
8257 USTPUTC('"', nextc);
8258 }
8259 *nextc = 0;
8260 cmdnextc = nextc;
8261}
8262
8263
8264static void
8265showpipe(struct job *jp, FILE *out)
8266{
8267 struct procstat *sp;
8268 struct procstat *spend;
8269
8270 spend = jp->ps + jp->nprocs;
8271 for (sp = jp->ps + 1; sp < spend; sp++)
8272 fprintf(out, " | %s", sp->cmd);
8273 outcslow('\n', out);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008274 flush_stdout_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008275}
8276
8277static void
8278xtcsetpgrp(int fd, pid_t pgrp)
8279{
8280 if (tcsetpgrp(fd, pgrp))
Denis Vlasenkob012b102007-02-19 22:43:01 +00008281 ash_msg_and_raise_error("Cannot set tty process group (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +00008282}
8283#endif /* JOBS */
8284
8285static int
Denis Vlasenkoe5570da2007-02-19 22:41:55 +00008286getstatus(struct job *job)
8287{
Eric Andersenc470f442003-07-28 09:56:35 +00008288 int status;
8289 int retval;
8290
8291 status = job->ps[job->nprocs - 1].status;
8292 retval = WEXITSTATUS(status);
8293 if (!WIFEXITED(status)) {
8294#if JOBS
8295 retval = WSTOPSIG(status);
8296 if (!WIFSTOPPED(status))
8297#endif
8298 {
8299 /* XXX: limits number of signals */
8300 retval = WTERMSIG(status);
8301#if JOBS
8302 if (retval == SIGINT)
8303 job->sigint = 1;
8304#endif
8305 }
8306 retval += 128;
8307 }
8308 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
8309 jobno(job), job->nprocs, status, retval));
8310 return retval;
8311}
8312
Denis Vlasenko131ae172007-02-18 13:00:19 +00008313#if ENABLE_ASH_MAIL
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008314/* mail.c */
Eric Andersenec074692001-10-31 11:05:49 +00008315
Eric Andersencb57d552001-06-28 07:25:16 +00008316/*
Eric Andersenc470f442003-07-28 09:56:35 +00008317 * Routines to check for mail. (Perhaps make part of main.c?)
Eric Andersencb57d552001-06-28 07:25:16 +00008318 */
8319
Eric Andersencb57d552001-06-28 07:25:16 +00008320#define MAXMBOXES 10
8321
Eric Andersenc470f442003-07-28 09:56:35 +00008322/* times of mailboxes */
8323static time_t mailtime[MAXMBOXES];
8324/* Set if MAIL or MAILPATH is changed. */
8325static int mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00008326
8327
Eric Andersencb57d552001-06-28 07:25:16 +00008328/*
Eric Andersenc470f442003-07-28 09:56:35 +00008329 * Print appropriate message(s) if mail has arrived.
8330 * If mail_var_path_changed is set,
8331 * then the value of MAIL has mail_var_path_changed,
8332 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00008333 */
Eric Andersenc470f442003-07-28 09:56:35 +00008334static void
8335chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008336{
Eric Andersencb57d552001-06-28 07:25:16 +00008337 const char *mpath;
8338 char *p;
8339 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008340 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00008341 struct stackmark smark;
8342 struct stat statb;
8343
Eric Andersencb57d552001-06-28 07:25:16 +00008344 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00008345 mpath = mpathset() ? mpathval() : mailval();
8346 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008347 p = padvance(&mpath, nullstr);
8348 if (p == NULL)
8349 break;
8350 if (*p == '\0')
8351 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008352 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008353#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00008354 if (q[-1] != '/')
8355 abort();
8356#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008357 q[-1] = '\0'; /* delete trailing '/' */
8358 if (stat(p, &statb) < 0) {
8359 *mtp = 0;
8360 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00008361 }
Eric Andersenc470f442003-07-28 09:56:35 +00008362 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
8363 fprintf(
8364 stderr, snlfmt,
8365 pathopt ? pathopt : "you have mail"
8366 );
8367 }
8368 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00008369 }
Eric Andersenc470f442003-07-28 09:56:35 +00008370 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008371 popstackmark(&smark);
8372}
Eric Andersencb57d552001-06-28 07:25:16 +00008373
Eric Andersenec074692001-10-31 11:05:49 +00008374
Eric Andersenc470f442003-07-28 09:56:35 +00008375static void
8376changemail(const char *val)
8377{
8378 mail_var_path_changed++;
8379}
8380
Denis Vlasenko131ae172007-02-18 13:00:19 +00008381#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00008382
Eric Andersencb57d552001-06-28 07:25:16 +00008383/*
Eric Andersenc470f442003-07-28 09:56:35 +00008384 * Take commands from a file. To be compatible we should do a path
Eric Andersencb57d552001-06-28 07:25:16 +00008385 * search for the file, which is necessary to find sub-commands.
8386 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008387static char *
8388find_dot_file(char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00008389{
8390 char *fullname;
8391 const char *path = pathval();
8392 struct stat statb;
8393
8394 /* don't try this for absolute or relative paths */
Eric Andersenc470f442003-07-28 09:56:35 +00008395 if (strchr(name, '/'))
8396 return name;
Eric Andersencb57d552001-06-28 07:25:16 +00008397
Eric Andersenc470f442003-07-28 09:56:35 +00008398 while ((fullname = padvance(&path, name)) != NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00008399 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
8400 /*
8401 * Don't bother freeing here, since it will
8402 * be freed by the caller.
8403 */
8404 return fullname;
8405 }
8406 stunalloc(fullname);
8407 }
8408
8409 /* not found in the PATH */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008410 ash_msg_and_raise_error("%s: not found", name);
Eric Andersencb57d552001-06-28 07:25:16 +00008411 /* NOTREACHED */
8412}
8413
Eric Andersenc470f442003-07-28 09:56:35 +00008414static void
8415calcsize(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008416{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008417 if (n == NULL)
8418 return;
8419 funcblocksize += nodesize[n->type];
8420 switch (n->type) {
8421 case NCMD:
8422 calcsize(n->ncmd.redirect);
8423 calcsize(n->ncmd.args);
8424 calcsize(n->ncmd.assign);
8425 break;
8426 case NPIPE:
8427 sizenodelist(n->npipe.cmdlist);
8428 break;
8429 case NREDIR:
8430 case NBACKGND:
8431 case NSUBSHELL:
8432 calcsize(n->nredir.redirect);
8433 calcsize(n->nredir.n);
8434 break;
8435 case NAND:
8436 case NOR:
8437 case NSEMI:
8438 case NWHILE:
8439 case NUNTIL:
8440 calcsize(n->nbinary.ch2);
8441 calcsize(n->nbinary.ch1);
8442 break;
8443 case NIF:
8444 calcsize(n->nif.elsepart);
8445 calcsize(n->nif.ifpart);
8446 calcsize(n->nif.test);
8447 break;
8448 case NFOR:
8449 funcstringsize += strlen(n->nfor.var) + 1;
8450 calcsize(n->nfor.body);
8451 calcsize(n->nfor.args);
8452 break;
8453 case NCASE:
8454 calcsize(n->ncase.cases);
8455 calcsize(n->ncase.expr);
8456 break;
8457 case NCLIST:
8458 calcsize(n->nclist.body);
8459 calcsize(n->nclist.pattern);
8460 calcsize(n->nclist.next);
8461 break;
8462 case NDEFUN:
8463 case NARG:
8464 sizenodelist(n->narg.backquote);
8465 funcstringsize += strlen(n->narg.text) + 1;
8466 calcsize(n->narg.next);
8467 break;
8468 case NTO:
8469 case NCLOBBER:
8470 case NFROM:
8471 case NFROMTO:
8472 case NAPPEND:
8473 calcsize(n->nfile.fname);
8474 calcsize(n->nfile.next);
8475 break;
8476 case NTOFD:
8477 case NFROMFD:
8478 calcsize(n->ndup.vname);
8479 calcsize(n->ndup.next);
8480 break;
8481 case NHERE:
8482 case NXHERE:
8483 calcsize(n->nhere.doc);
8484 calcsize(n->nhere.next);
8485 break;
8486 case NNOT:
8487 calcsize(n->nnot.com);
8488 break;
8489 };
Eric Andersencb57d552001-06-28 07:25:16 +00008490}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008491
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008492
Eric Andersenc470f442003-07-28 09:56:35 +00008493static void
8494sizenodelist(struct nodelist *lp)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008495{
8496 while (lp) {
Eric Andersenc470f442003-07-28 09:56:35 +00008497 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008498 calcsize(lp->n);
8499 lp = lp->next;
8500 }
8501}
Eric Andersencb57d552001-06-28 07:25:16 +00008502
8503
Eric Andersenc470f442003-07-28 09:56:35 +00008504static union node *
8505copynode(union node *n)
8506{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008507 union node *new;
Eric Andersenc470f442003-07-28 09:56:35 +00008508
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008509 if (n == NULL)
8510 return NULL;
8511 new = funcblock;
8512 funcblock = (char *) funcblock + nodesize[n->type];
8513
8514 switch (n->type) {
8515 case NCMD:
8516 new->ncmd.redirect = copynode(n->ncmd.redirect);
8517 new->ncmd.args = copynode(n->ncmd.args);
8518 new->ncmd.assign = copynode(n->ncmd.assign);
8519 break;
8520 case NPIPE:
8521 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8522 new->npipe.backgnd = n->npipe.backgnd;
8523 break;
8524 case NREDIR:
8525 case NBACKGND:
8526 case NSUBSHELL:
8527 new->nredir.redirect = copynode(n->nredir.redirect);
8528 new->nredir.n = copynode(n->nredir.n);
8529 break;
8530 case NAND:
8531 case NOR:
8532 case NSEMI:
8533 case NWHILE:
8534 case NUNTIL:
8535 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8536 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8537 break;
8538 case NIF:
8539 new->nif.elsepart = copynode(n->nif.elsepart);
8540 new->nif.ifpart = copynode(n->nif.ifpart);
8541 new->nif.test = copynode(n->nif.test);
8542 break;
8543 case NFOR:
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008544 new->nfor.var = nodeckstrdup(n->nfor.var);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008545 new->nfor.body = copynode(n->nfor.body);
8546 new->nfor.args = copynode(n->nfor.args);
8547 break;
8548 case NCASE:
8549 new->ncase.cases = copynode(n->ncase.cases);
8550 new->ncase.expr = copynode(n->ncase.expr);
8551 break;
8552 case NCLIST:
8553 new->nclist.body = copynode(n->nclist.body);
8554 new->nclist.pattern = copynode(n->nclist.pattern);
8555 new->nclist.next = copynode(n->nclist.next);
8556 break;
8557 case NDEFUN:
8558 case NARG:
8559 new->narg.backquote = copynodelist(n->narg.backquote);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008560 new->narg.text = nodeckstrdup(n->narg.text);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008561 new->narg.next = copynode(n->narg.next);
8562 break;
8563 case NTO:
8564 case NCLOBBER:
8565 case NFROM:
8566 case NFROMTO:
8567 case NAPPEND:
8568 new->nfile.fname = copynode(n->nfile.fname);
8569 new->nfile.fd = n->nfile.fd;
8570 new->nfile.next = copynode(n->nfile.next);
8571 break;
8572 case NTOFD:
8573 case NFROMFD:
8574 new->ndup.vname = copynode(n->ndup.vname);
8575 new->ndup.dupfd = n->ndup.dupfd;
8576 new->ndup.fd = n->ndup.fd;
8577 new->ndup.next = copynode(n->ndup.next);
8578 break;
8579 case NHERE:
8580 case NXHERE:
8581 new->nhere.doc = copynode(n->nhere.doc);
8582 new->nhere.fd = n->nhere.fd;
8583 new->nhere.next = copynode(n->nhere.next);
8584 break;
8585 case NNOT:
8586 new->nnot.com = copynode(n->nnot.com);
8587 break;
8588 };
8589 new->type = n->type;
Eric Andersenc470f442003-07-28 09:56:35 +00008590 return new;
8591}
8592
8593
8594static struct nodelist *
8595copynodelist(struct nodelist *lp)
Eric Andersencb57d552001-06-28 07:25:16 +00008596{
8597 struct nodelist *start;
8598 struct nodelist **lpp;
8599
8600 lpp = &start;
8601 while (lp) {
8602 *lpp = funcblock;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008603 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00008604 (*lpp)->n = copynode(lp->n);
8605 lp = lp->next;
8606 lpp = &(*lpp)->next;
8607 }
8608 *lpp = NULL;
8609 return start;
8610}
8611
8612
Eric Andersenc470f442003-07-28 09:56:35 +00008613static char *
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008614nodeckstrdup(char *s)
Eric Andersenc470f442003-07-28 09:56:35 +00008615{
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00008616 char *rtn = funcstring;
Eric Andersenc470f442003-07-28 09:56:35 +00008617
Denis Vlasenko7cfecc42006-12-18 22:32:45 +00008618 strcpy(funcstring, s);
8619 funcstring += strlen(s) + 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008620 return rtn;
Eric Andersencb57d552001-06-28 07:25:16 +00008621}
8622
Eric Andersenc470f442003-07-28 09:56:35 +00008623
Eric Andersenc470f442003-07-28 09:56:35 +00008624/*
8625 * Free a parse tree.
8626 */
Eric Andersenc470f442003-07-28 09:56:35 +00008627static void
8628freefunc(struct funcnode *f)
8629{
8630 if (f && --f->count < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008631 free(f);
Eric Andersenc470f442003-07-28 09:56:35 +00008632}
8633
8634
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008635static void
Eric Andersenc470f442003-07-28 09:56:35 +00008636optschanged(void)
8637{
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008638#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008639 opentrace();
8640#endif
8641 setinteractive(iflag);
8642 setjobctl(mflag);
Paul Fox3f11b1b2005-08-04 19:04:46 +00008643 setvimode(viflag);
Eric Andersenc470f442003-07-28 09:56:35 +00008644}
Eric Andersencb57d552001-06-28 07:25:16 +00008645
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008646static void
8647minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008648{
8649 int i;
8650
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008651 if (name) {
8652 for (i = 0; i < NOPTS; i++) {
Eric Andersenc470f442003-07-28 09:56:35 +00008653 if (equal(name, optnames(i))) {
8654 optlist[i] = val;
Eric Andersen62483552001-07-10 06:09:16 +00008655 return;
8656 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008657 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008658 ash_msg_and_raise_error("Illegal option -o %s", name);
Eric Andersen62483552001-07-10 06:09:16 +00008659 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008660 out1str("Current option settings\n");
8661 for (i = 0; i < NOPTS; i++)
8662 out1fmt("%-16s%s\n", optnames(i),
8663 optlist[i] ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00008664}
8665
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008666
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008667static void
8668setoption(int flag, int val)
8669{
8670 int i;
8671
8672 for (i = 0; i < NOPTS; i++) {
8673 if (optletters(i) == flag) {
8674 optlist[i] = val;
8675 return;
8676 }
8677 }
8678 ash_msg_and_raise_error("Illegal option -%c", flag);
8679 /* NOTREACHED */
8680}
8681
8682
Eric Andersenc470f442003-07-28 09:56:35 +00008683/*
8684 * Process shell options. The global variable argptr contains a pointer
8685 * to the argument list; we advance it past the options.
8686 */
Eric Andersenc470f442003-07-28 09:56:35 +00008687static void
8688options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00008689{
8690 char *p;
8691 int val;
8692 int c;
8693
8694 if (cmdline)
8695 minusc = NULL;
8696 while ((p = *argptr) != NULL) {
8697 argptr++;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008698 c = *p++;
8699 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00008700 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00008701 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00008702 if (!cmdline) {
8703 /* "-" means turn off -x and -v */
8704 if (p[0] == '\0')
8705 xflag = vflag = 0;
8706 /* "--" means reset params */
8707 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00008708 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00008709 }
Eric Andersenc470f442003-07-28 09:56:35 +00008710 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00008711 }
8712 } else if (c == '+') {
8713 val = 0;
8714 } else {
8715 argptr--;
8716 break;
8717 }
8718 while ((c = *p++) != '\0') {
8719 if (c == 'c' && cmdline) {
Eric Andersenc470f442003-07-28 09:56:35 +00008720 minusc = p; /* command is after shell args*/
Eric Andersencb57d552001-06-28 07:25:16 +00008721 } else if (c == 'o') {
8722 minus_o(*argptr, val);
8723 if (*argptr)
8724 argptr++;
Eric Andersenc470f442003-07-28 09:56:35 +00008725 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008726 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00008727 isloginsh = 1;
8728 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008729 } else {
8730 setoption(c, val);
8731 }
8732 }
8733 }
8734}
8735
Eric Andersencb57d552001-06-28 07:25:16 +00008736
Eric Andersencb57d552001-06-28 07:25:16 +00008737/*
8738 * Set the shell parameters.
8739 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008740static void
Eric Andersenc470f442003-07-28 09:56:35 +00008741setparam(char **argv)
Eric Andersen2870d962001-07-02 17:27:21 +00008742{
Eric Andersencb57d552001-06-28 07:25:16 +00008743 char **newparam;
8744 char **ap;
8745 int nparam;
8746
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008747 for (nparam = 0; argv[nparam]; nparam++);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008748 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
Eric Andersencb57d552001-06-28 07:25:16 +00008749 while (*argv) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00008750 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008751 }
8752 *ap = NULL;
8753 freeparam(&shellparam);
8754 shellparam.malloc = 1;
8755 shellparam.nparam = nparam;
8756 shellparam.p = newparam;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008757#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008758 shellparam.optind = 1;
8759 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00008760#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008761}
8762
8763
8764/*
8765 * Free the list of positional parameters.
8766 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008767static void
Eric Andersenc470f442003-07-28 09:56:35 +00008768freeparam(volatile struct shparam *param)
Eric Andersen2870d962001-07-02 17:27:21 +00008769{
Eric Andersencb57d552001-06-28 07:25:16 +00008770 char **ap;
8771
8772 if (param->malloc) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008773 for (ap = param->p; *ap; ap++)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008774 free(*ap);
8775 free(param->p);
Eric Andersencb57d552001-06-28 07:25:16 +00008776 }
8777}
8778
8779
Eric Andersencb57d552001-06-28 07:25:16 +00008780/*
8781 * The shift builtin command.
8782 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008783static int
Eric Andersenc470f442003-07-28 09:56:35 +00008784shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008785{
8786 int n;
8787 char **ap1, **ap2;
8788
8789 n = 1;
8790 if (argc > 1)
8791 n = number(argv[1]);
8792 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008793 ash_msg_and_raise_error("can't shift that many");
8794 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008795 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008796 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008797 if (shellparam.malloc)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008798 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00008799 }
8800 ap2 = shellparam.p;
8801 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00008802#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008803 shellparam.optind = 1;
8804 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00008805#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00008806 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008807 return 0;
8808}
8809
8810
Eric Andersencb57d552001-06-28 07:25:16 +00008811/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008812 * POSIX requires that 'set' (but not export or readonly) output the
8813 * variables in lexicographic order - by the locale's collating order (sigh).
8814 * Maybe we could keep them in an ordered balanced binary tree
8815 * instead of hashed lists.
8816 * For now just roll 'em through qsort for printing...
8817 */
8818static int
8819showvars(const char *sep_prefix, int on, int off)
8820{
8821 const char *sep;
8822 char **ep, **epend;
8823
8824 ep = listvars(on, off, &epend);
8825 qsort(ep, epend - ep, sizeof(char *), vpcmp);
8826
8827 sep = *sep_prefix ? spcstr : sep_prefix;
8828
8829 for (; ep < epend; ep++) {
8830 const char *p;
8831 const char *q;
8832
8833 p = strchrnul(*ep, '=');
8834 q = nullstr;
8835 if (*p)
8836 q = single_quote(++p);
8837 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
8838 }
8839 return 0;
8840}
8841
8842/*
Eric Andersencb57d552001-06-28 07:25:16 +00008843 * The set command builtin.
8844 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008845static int
Eric Andersenc470f442003-07-28 09:56:35 +00008846setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008847{
8848 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00008849 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008850 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008851 options(0);
8852 optschanged();
8853 if (*argptr != NULL) {
8854 setparam(argptr);
8855 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008856 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008857 return 0;
8858}
8859
8860
Denis Vlasenko131ae172007-02-18 13:00:19 +00008861#if ENABLE_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00008862static void change_lc_all(const char *value)
8863{
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008864 if (value && *value != '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00008865 setlocale(LC_ALL, value);
8866}
8867
8868static void change_lc_ctype(const char *value)
8869{
Denis Vlasenkoa624c112007-02-19 22:45:43 +00008870 if (value && *value != '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00008871 setlocale(LC_CTYPE, value);
8872}
Eric Andersen2870d962001-07-02 17:27:21 +00008873#endif
8874
Denis Vlasenko131ae172007-02-18 13:00:19 +00008875#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00008876/* Roughly copied from bash.. */
Eric Andersenef02f822004-03-11 13:34:24 +00008877static void change_random(const char *value)
8878{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008879 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00008880 /* "get", generate */
8881 char buf[16];
8882
8883 rseed = rseed * 1103515245 + 12345;
8884 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
8885 /* set without recursion */
8886 setvar(vrandom.text, buf, VNOFUNC);
8887 vrandom.flags &= ~VNOFUNC;
8888 } else {
8889 /* set/reset */
8890 rseed = strtoul(value, (char **)NULL, 10);
8891 }
Eric Andersenef02f822004-03-11 13:34:24 +00008892}
Eric Andersen16767e22004-03-16 05:14:10 +00008893#endif
8894
Eric Andersenef02f822004-03-11 13:34:24 +00008895
Denis Vlasenko131ae172007-02-18 13:00:19 +00008896#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008897static int
Eric Andersenc470f442003-07-28 09:56:35 +00008898getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00008899{
8900 char *p, *q;
8901 char c = '?';
8902 int done = 0;
8903 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00008904 char s[12];
8905 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00008906
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008907 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00008908 return 1;
8909 optnext = optfirst + *param_optind - 1;
8910
8911 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00008912 p = NULL;
8913 else
Eric Andersena48b0a32003-10-22 10:56:47 +00008914 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00008915 if (p == NULL || *p == '\0') {
8916 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00008917 p = *optnext;
8918 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008919 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00008920 p = NULL;
8921 done = 1;
8922 goto out;
8923 }
8924 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00008925 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00008926 goto atend;
8927 }
8928
8929 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00008930 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00008931 if (*q == '\0') {
8932 if (optstr[0] == ':') {
8933 s[0] = c;
8934 s[1] = '\0';
8935 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008936 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00008937 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008938 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00008939 }
8940 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00008941 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00008942 }
8943 if (*++q == ':')
8944 q++;
8945 }
8946
8947 if (*++q == ':') {
8948 if (*p == '\0' && (p = *optnext) == NULL) {
8949 if (optstr[0] == ':') {
8950 s[0] = c;
8951 s[1] = '\0';
8952 err |= setvarsafe("OPTARG", s, 0);
8953 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008954 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00008955 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008956 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00008957 c = '?';
8958 }
Eric Andersenc470f442003-07-28 09:56:35 +00008959 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00008960 }
8961
8962 if (p == *optnext)
8963 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00008964 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00008965 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008966 } else
Eric Andersenc470f442003-07-28 09:56:35 +00008967 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008968 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008969 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00008970 *param_optind = optnext - optfirst + 1;
8971 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00008972 err |= setvarsafe("OPTIND", s, VNOFUNC);
8973 s[0] = c;
8974 s[1] = '\0';
8975 err |= setvarsafe(optvar, s, 0);
8976 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00008977 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008978 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008979 flush_stdout_stderr();
8980 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00008981 }
8982 return done;
8983}
Eric Andersenc470f442003-07-28 09:56:35 +00008984
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008985
Eric Andersenc470f442003-07-28 09:56:35 +00008986/*
8987 * The getopts builtin. Shellparam.optnext points to the next argument
8988 * to be processed. Shellparam.optptr points to the next character to
8989 * be processed in the current argument. If shellparam.optnext is NULL,
8990 * then it's the first time getopts has been called.
8991 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008992static int
Eric Andersenc470f442003-07-28 09:56:35 +00008993getoptscmd(int argc, char **argv)
8994{
8995 char **optbase;
8996
8997 if (argc < 3)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008998 ash_msg_and_raise_error("Usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008999 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009000 optbase = shellparam.p;
9001 if (shellparam.optind > shellparam.nparam + 1) {
9002 shellparam.optind = 1;
9003 shellparam.optoff = -1;
9004 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009005 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009006 optbase = &argv[3];
9007 if (shellparam.optind > argc - 2) {
9008 shellparam.optind = 1;
9009 shellparam.optoff = -1;
9010 }
9011 }
9012
9013 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009014 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009015}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009016#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009017
Eric Andersencb57d552001-06-28 07:25:16 +00009018
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009019/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009020
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009021static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9022static void
9023raise_error_syntax(const char *msg)
9024{
9025 ash_msg_and_raise_error("Syntax error: %s", msg);
9026 /* NOTREACHED */
9027}
9028
9029/*
9030 * Called when an unexpected token is read during the parse. The argument
9031 * is the token that is expected, or -1 if more than one type of token can
9032 * occur at this point.
9033 */
9034static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9035static void
9036raise_error_unexpected_syntax(int token)
9037{
9038 char msg[64];
9039 int l;
9040
9041 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9042 if (token >= 0)
9043 sprintf(msg + l, " (expecting %s)", tokname(token));
9044 raise_error_syntax(msg);
9045 /* NOTREACHED */
9046}
Eric Andersencb57d552001-06-28 07:25:16 +00009047
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009048#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009049
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009050struct heredoc {
9051 struct heredoc *next; /* next here document in list */
9052 union node *here; /* redirection node */
9053 char *eofmark; /* string indicating end of input */
9054 int striptabs; /* if set, strip leading tabs */
9055};
Eric Andersencb57d552001-06-28 07:25:16 +00009056
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009057static struct heredoc *heredoclist; /* list of here documents to read */
9058
9059/* parsing is heavily cross-recursive, need these forward decls */
9060static union node *andor(void);
9061static union node *pipeline(void);
9062static union node *parse_command(void);
9063static void parseheredoc(void);
9064static char peektoken(void);
9065static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009066
Eric Andersenc470f442003-07-28 09:56:35 +00009067static union node *
9068list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009069{
9070 union node *n1, *n2, *n3;
9071 int tok;
9072
Eric Andersenc470f442003-07-28 09:56:35 +00009073 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9074 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009075 return NULL;
9076 n1 = NULL;
9077 for (;;) {
9078 n2 = andor();
9079 tok = readtoken();
9080 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009081 if (n2->type == NPIPE) {
9082 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009083 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009084 if (n2->type != NREDIR) {
9085 n3 = stalloc(sizeof(struct nredir));
9086 n3->nredir.n = n2;
9087 n3->nredir.redirect = NULL;
9088 n2 = n3;
9089 }
9090 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009091 }
9092 }
9093 if (n1 == NULL) {
9094 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009095 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009096 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009097 n3->type = NSEMI;
9098 n3->nbinary.ch1 = n1;
9099 n3->nbinary.ch2 = n2;
9100 n1 = n3;
9101 }
9102 switch (tok) {
9103 case TBACKGND:
9104 case TSEMI:
9105 tok = readtoken();
9106 /* fall through */
9107 case TNL:
9108 if (tok == TNL) {
9109 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009110 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009111 return n1;
9112 } else {
9113 tokpushback++;
9114 }
Eric Andersenc470f442003-07-28 09:56:35 +00009115 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009116 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009117 return n1;
9118 break;
9119 case TEOF:
9120 if (heredoclist)
9121 parseheredoc();
9122 else
Eric Andersenc470f442003-07-28 09:56:35 +00009123 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009124 return n1;
9125 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009126 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009127 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009128 tokpushback++;
9129 return n1;
9130 }
9131 }
9132}
9133
Eric Andersenc470f442003-07-28 09:56:35 +00009134static union node *
9135andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009136{
Eric Andersencb57d552001-06-28 07:25:16 +00009137 union node *n1, *n2, *n3;
9138 int t;
9139
Eric Andersencb57d552001-06-28 07:25:16 +00009140 n1 = pipeline();
9141 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009142 t = readtoken();
9143 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009144 t = NAND;
9145 } else if (t == TOR) {
9146 t = NOR;
9147 } else {
9148 tokpushback++;
9149 return n1;
9150 }
Eric Andersenc470f442003-07-28 09:56:35 +00009151 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009152 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009153 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009154 n3->type = t;
9155 n3->nbinary.ch1 = n1;
9156 n3->nbinary.ch2 = n2;
9157 n1 = n3;
9158 }
9159}
9160
Eric Andersenc470f442003-07-28 09:56:35 +00009161static union node *
9162pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009163{
Eric Andersencb57d552001-06-28 07:25:16 +00009164 union node *n1, *n2, *pipenode;
9165 struct nodelist *lp, *prev;
9166 int negate;
9167
9168 negate = 0;
9169 TRACE(("pipeline: entered\n"));
9170 if (readtoken() == TNOT) {
9171 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009172 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009173 } else
9174 tokpushback++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009175 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009176 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009177 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009178 pipenode->type = NPIPE;
9179 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009180 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009181 pipenode->npipe.cmdlist = lp;
9182 lp->n = n1;
9183 do {
9184 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009185 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009186 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009187 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009188 prev->next = lp;
9189 } while (readtoken() == TPIPE);
9190 lp->next = NULL;
9191 n1 = pipenode;
9192 }
9193 tokpushback++;
9194 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009195 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009196 n2->type = NNOT;
9197 n2->nnot.com = n1;
9198 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009199 }
9200 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009201}
9202
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009203static union node *
9204makename(void)
9205{
9206 union node *n;
9207
9208 n = stalloc(sizeof(struct narg));
9209 n->type = NARG;
9210 n->narg.next = NULL;
9211 n->narg.text = wordtext;
9212 n->narg.backquote = backquotelist;
9213 return n;
9214}
9215
9216static void
9217fixredir(union node *n, const char *text, int err)
9218{
9219 TRACE(("Fix redir %s %d\n", text, err));
9220 if (!err)
9221 n->ndup.vname = NULL;
9222
9223 if (isdigit(text[0]) && text[1] == '\0')
9224 n->ndup.dupfd = digit_val(text[0]);
9225 else if (LONE_DASH(text))
9226 n->ndup.dupfd = -1;
9227 else {
9228 if (err)
9229 raise_error_syntax("Bad fd number");
9230 n->ndup.vname = makename();
9231 }
9232}
9233
9234/*
9235 * Returns true if the text contains nothing to expand (no dollar signs
9236 * or backquotes).
9237 */
9238static int
9239noexpand(char *text)
9240{
9241 char *p;
9242 char c;
9243
9244 p = text;
9245 while ((c = *p++) != '\0') {
9246 if (c == CTLQUOTEMARK)
9247 continue;
9248 if (c == CTLESC)
9249 p++;
9250 else if (SIT(c, BASESYNTAX) == CCTL)
9251 return 0;
9252 }
9253 return 1;
9254}
9255
9256static void
9257parsefname(void)
9258{
9259 union node *n = redirnode;
9260
9261 if (readtoken() != TWORD)
9262 raise_error_unexpected_syntax(-1);
9263 if (n->type == NHERE) {
9264 struct heredoc *here = heredoc;
9265 struct heredoc *p;
9266 int i;
9267
9268 if (quoteflag == 0)
9269 n->type = NXHERE;
9270 TRACE(("Here document %d\n", n->type));
9271 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9272 raise_error_syntax("Illegal eof marker for << redirection");
9273 rmescapes(wordtext);
9274 here->eofmark = wordtext;
9275 here->next = NULL;
9276 if (heredoclist == NULL)
9277 heredoclist = here;
9278 else {
9279 for (p = heredoclist; p->next; p = p->next);
9280 p->next = here;
9281 }
9282 } else if (n->type == NTOFD || n->type == NFROMFD) {
9283 fixredir(n, wordtext, 0);
9284 } else {
9285 n->nfile.fname = makename();
9286 }
9287}
Eric Andersencb57d552001-06-28 07:25:16 +00009288
Eric Andersenc470f442003-07-28 09:56:35 +00009289static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009290simplecmd(void)
9291{
9292 union node *args, **app;
9293 union node *n = NULL;
9294 union node *vars, **vpp;
9295 union node **rpp, *redir;
9296 int savecheckkwd;
9297
9298 args = NULL;
9299 app = &args;
9300 vars = NULL;
9301 vpp = &vars;
9302 redir = NULL;
9303 rpp = &redir;
9304
9305 savecheckkwd = CHKALIAS;
9306 for (;;) {
9307 checkkwd = savecheckkwd;
9308 switch (readtoken()) {
9309 case TWORD:
9310 n = stalloc(sizeof(struct narg));
9311 n->type = NARG;
9312 n->narg.text = wordtext;
9313 n->narg.backquote = backquotelist;
9314 if (savecheckkwd && isassignment(wordtext)) {
9315 *vpp = n;
9316 vpp = &n->narg.next;
9317 } else {
9318 *app = n;
9319 app = &n->narg.next;
9320 savecheckkwd = 0;
9321 }
9322 break;
9323 case TREDIR:
9324 *rpp = n = redirnode;
9325 rpp = &n->nfile.next;
9326 parsefname(); /* read name of redirection file */
9327 break;
9328 case TLP:
9329 if (args && app == &args->narg.next
9330 && !vars && !redir
9331 ) {
9332 struct builtincmd *bcmd;
9333 const char *name;
9334
9335 /* We have a function */
9336 if (readtoken() != TRP)
9337 raise_error_unexpected_syntax(TRP);
9338 name = n->narg.text;
9339 if (!goodname(name)
9340 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9341 ) {
9342 raise_error_syntax("Bad function name");
9343 }
9344 n->type = NDEFUN;
9345 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9346 n->narg.next = parse_command();
9347 return n;
9348 }
9349 /* fall through */
9350 default:
9351 tokpushback++;
9352 goto out;
9353 }
9354 }
9355 out:
9356 *app = NULL;
9357 *vpp = NULL;
9358 *rpp = NULL;
9359 n = stalloc(sizeof(struct ncmd));
9360 n->type = NCMD;
9361 n->ncmd.args = args;
9362 n->ncmd.assign = vars;
9363 n->ncmd.redirect = redir;
9364 return n;
9365}
9366
9367static union node *
9368parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009369{
Eric Andersencb57d552001-06-28 07:25:16 +00009370 union node *n1, *n2;
9371 union node *ap, **app;
9372 union node *cp, **cpp;
9373 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009374 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009375 int t;
9376
9377 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009378 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009379
Eric Andersencb57d552001-06-28 07:25:16 +00009380 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009381 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009382 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009383 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009384 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009385 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009386 n1->type = NIF;
9387 n1->nif.test = list(0);
9388 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009389 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009390 n1->nif.ifpart = list(0);
9391 n2 = n1;
9392 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009393 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009394 n2 = n2->nif.elsepart;
9395 n2->type = NIF;
9396 n2->nif.test = list(0);
9397 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009398 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009399 n2->nif.ifpart = list(0);
9400 }
9401 if (lasttoken == TELSE)
9402 n2->nif.elsepart = list(0);
9403 else {
9404 n2->nif.elsepart = NULL;
9405 tokpushback++;
9406 }
Eric Andersenc470f442003-07-28 09:56:35 +00009407 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009408 break;
9409 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009410 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009411 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009412 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009413 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009414 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009415 got = readtoken();
9416 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009417 TRACE(("expecting DO got %s %s\n", tokname(got),
9418 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009419 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009420 }
9421 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009422 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009423 break;
9424 }
9425 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009426 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009427 raise_error_syntax("Bad for loop variable");
9428 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009429 n1->type = NFOR;
9430 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009431 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009432 if (readtoken() == TIN) {
9433 app = &ap;
9434 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009435 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009436 n2->type = NARG;
9437 n2->narg.text = wordtext;
9438 n2->narg.backquote = backquotelist;
9439 *app = n2;
9440 app = &n2->narg.next;
9441 }
9442 *app = NULL;
9443 n1->nfor.args = ap;
9444 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009445 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009446 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009447 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009448 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009449 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009450 n2->narg.backquote = NULL;
9451 n2->narg.next = NULL;
9452 n1->nfor.args = n2;
9453 /*
9454 * Newline or semicolon here is optional (but note
9455 * that the original Bourne shell only allowed NL).
9456 */
9457 if (lasttoken != TNL && lasttoken != TSEMI)
9458 tokpushback++;
9459 }
Eric Andersenc470f442003-07-28 09:56:35 +00009460 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009461 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009462 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009463 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009464 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009465 break;
9466 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009467 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009468 n1->type = NCASE;
9469 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009470 raise_error_unexpected_syntax(TWORD);
9471 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009472 n2->type = NARG;
9473 n2->narg.text = wordtext;
9474 n2->narg.backquote = backquotelist;
9475 n2->narg.next = NULL;
9476 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009477 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009478 } while (readtoken() == TNL);
9479 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009480 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009481 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009482 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009483 checkkwd = CHKNL | CHKKWD;
9484 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009485 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009486 if (lasttoken == TLP)
9487 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009488 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009489 cp->type = NCLIST;
9490 app = &cp->nclist.pattern;
9491 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009492 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009493 ap->type = NARG;
9494 ap->narg.text = wordtext;
9495 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009496 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009497 break;
9498 app = &ap->narg.next;
9499 readtoken();
9500 }
9501 ap->narg.next = NULL;
9502 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009503 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009504 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009505
Eric Andersenc470f442003-07-28 09:56:35 +00009506 cpp = &cp->nclist.next;
9507
9508 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009509 t = readtoken();
9510 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009511 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009512 raise_error_unexpected_syntax(TENDCASE);
9513 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009514 }
Eric Andersenc470f442003-07-28 09:56:35 +00009515 }
Eric Andersencb57d552001-06-28 07:25:16 +00009516 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009517 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009518 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009519 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009520 n1->type = NSUBSHELL;
9521 n1->nredir.n = list(0);
9522 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009523 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009524 break;
9525 case TBEGIN:
9526 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009527 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009528 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009529 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009530 case TREDIR:
Eric Andersencb57d552001-06-28 07:25:16 +00009531 tokpushback++;
Eric Andersenc470f442003-07-28 09:56:35 +00009532 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009533 }
9534
Eric Andersenc470f442003-07-28 09:56:35 +00009535 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009536 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009537
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009538 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009539 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009540 checkkwd = CHKKWD | CHKALIAS;
9541 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009542 while (readtoken() == TREDIR) {
9543 *rpp = n2 = redirnode;
9544 rpp = &n2->nfile.next;
9545 parsefname();
9546 }
9547 tokpushback++;
9548 *rpp = NULL;
9549 if (redir) {
9550 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009551 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009552 n2->type = NREDIR;
9553 n2->nredir.n = n1;
9554 n1 = n2;
9555 }
9556 n1->nredir.redirect = redir;
9557 }
Eric Andersencb57d552001-06-28 07:25:16 +00009558 return n1;
9559}
9560
Eric Andersencb57d552001-06-28 07:25:16 +00009561/*
9562 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9563 * is not NULL, read a here document. In the latter case, eofmark is the
9564 * word which marks the end of the document and striptabs is true if
9565 * leading tabs should be stripped from the document. The argument firstc
9566 * is the first character of the input token or document.
9567 *
9568 * Because C does not have internal subroutines, I have simulated them
9569 * using goto's to implement the subroutine linkage. The following macros
9570 * will run code that appears at the end of readtoken1.
9571 */
9572
Eric Andersen2870d962001-07-02 17:27:21 +00009573#define CHECKEND() {goto checkend; checkend_return:;}
9574#define PARSEREDIR() {goto parseredir; parseredir_return:;}
9575#define PARSESUB() {goto parsesub; parsesub_return:;}
9576#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
9577#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
9578#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +00009579
9580static int
Eric Andersenc470f442003-07-28 09:56:35 +00009581readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009582{
Eric Andersencb57d552001-06-28 07:25:16 +00009583 int c = firstc;
9584 char *out;
9585 int len;
9586 char line[EOFMARKLEN + 1];
Eric Andersena68ea1c2006-01-30 22:48:39 +00009587 struct nodelist *bqlist = 0;
9588 int quotef = 0;
9589 int dblquote = 0;
9590 int varnest = 0; /* levels of variables expansion */
9591 int arinest = 0; /* levels of arithmetic expansion */
9592 int parenlevel = 0; /* levels of parens in arithmetic */
9593 int dqvarnest = 0; /* levels of variables expansion within double quotes */
9594 int oldstyle = 0;
9595 int prevsyntax = 0; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009596#if __GNUC__
9597 /* Avoid longjmp clobbering */
9598 (void) &out;
9599 (void) &quotef;
9600 (void) &dblquote;
9601 (void) &varnest;
9602 (void) &arinest;
9603 (void) &parenlevel;
9604 (void) &dqvarnest;
9605 (void) &oldstyle;
9606 (void) &prevsyntax;
9607 (void) &syntax;
9608#endif
9609
9610 startlinno = plinno;
9611 dblquote = 0;
9612 if (syntax == DQSYNTAX)
9613 dblquote = 1;
9614 quotef = 0;
9615 bqlist = NULL;
9616 varnest = 0;
9617 arinest = 0;
9618 parenlevel = 0;
9619 dqvarnest = 0;
9620
9621 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +00009622 loop: { /* for each line, until end of word */
9623 CHECKEND(); /* set c to PEOF if at end of here document */
9624 for (;;) { /* until end of line or end of word */
9625 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00009626 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009627 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +00009628 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +00009629 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009630 USTPUTC(c, out);
9631 plinno++;
9632 if (doprompt)
9633 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009634 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009635 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009636 case CWORD:
9637 USTPUTC(c, out);
9638 break;
9639 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +00009640 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +00009641 USTPUTC(CTLESC, out);
9642 USTPUTC(c, out);
9643 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009644 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +00009645 c = pgetc2();
9646 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +00009647 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009648 USTPUTC('\\', out);
9649 pungetc();
9650 } else if (c == '\n') {
9651 if (doprompt)
9652 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009653 } else {
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009654 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +00009655 c != '\\' && c != '`' &&
9656 c != '$' && (
9657 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +00009658 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +00009659 ) {
9660 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009661 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009662 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009663 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00009664 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009665 USTPUTC(c, out);
9666 quotef++;
9667 }
9668 break;
9669 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009670 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009671 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +00009672 if (eofmark == NULL) {
9673 USTPUTC(CTLQUOTEMARK, out);
9674 }
Eric Andersencb57d552001-06-28 07:25:16 +00009675 break;
9676 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00009677 syntax = DQSYNTAX;
9678 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009679 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009680 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009681 if (eofmark != NULL && arinest == 0
9682 && varnest == 0
9683 ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009684 USTPUTC(c, out);
9685 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009686 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009687 syntax = BASESYNTAX;
9688 dblquote = 0;
9689 }
9690 quotef++;
Eric Andersenc470f442003-07-28 09:56:35 +00009691 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +00009692 }
9693 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009694 case CVAR: /* '$' */
9695 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +00009696 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009697 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +00009698 if (varnest > 0) {
9699 varnest--;
9700 if (dqvarnest > 0) {
9701 dqvarnest--;
9702 }
9703 USTPUTC(CTLENDVAR, out);
9704 } else {
9705 USTPUTC(c, out);
9706 }
9707 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009708#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00009709 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009710 parenlevel++;
9711 USTPUTC(c, out);
9712 break;
Eric Andersenc470f442003-07-28 09:56:35 +00009713 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +00009714 if (parenlevel > 0) {
9715 USTPUTC(c, out);
9716 --parenlevel;
9717 } else {
9718 if (pgetc() == ')') {
9719 if (--arinest == 0) {
9720 USTPUTC(CTLENDARI, out);
9721 syntax = prevsyntax;
9722 if (syntax == DQSYNTAX)
9723 dblquote = 1;
9724 else
9725 dblquote = 0;
9726 } else
9727 USTPUTC(')', out);
9728 } else {
9729 /*
9730 * unbalanced parens
9731 * (don't 2nd guess - no error)
9732 */
9733 pungetc();
9734 USTPUTC(')', out);
9735 }
9736 }
9737 break;
9738#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009739 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +00009740 PARSEBACKQOLD();
9741 break;
Eric Andersen2870d962001-07-02 17:27:21 +00009742 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009743 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +00009744 case CIGN:
9745 break;
9746 default:
9747 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009748 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +00009749#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +00009750 if (c != PEOA)
9751#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009752 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +00009753
Eric Andersencb57d552001-06-28 07:25:16 +00009754 }
9755 c = pgetc_macro();
9756 }
9757 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009758 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +00009759#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00009760 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009761 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +00009762#endif
9763 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009764 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +00009765 if (varnest != 0) {
9766 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +00009767 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009768 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +00009769 }
9770 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +00009771 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +00009772 out = stackblock();
9773 if (eofmark == NULL) {
9774 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +00009775 && quotef == 0
9776 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009777 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +00009778 PARSEREDIR();
9779 return lasttoken = TREDIR;
9780 } else {
9781 pungetc();
9782 }
9783 }
9784 quoteflag = quotef;
9785 backquotelist = bqlist;
9786 grabstackblock(len);
9787 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009788 lasttoken = TWORD;
9789 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +00009790/* end of readtoken routine */
9791
9792
Eric Andersencb57d552001-06-28 07:25:16 +00009793/*
9794 * Check to see whether we are at the end of the here document. When this
9795 * is called, c is set to the first character of the next input line. If
9796 * we are at the end of the here document, this routine sets the c to PEOF.
9797 */
Eric Andersenc470f442003-07-28 09:56:35 +00009798checkend: {
9799 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009800#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00009801 if (c == PEOA) {
9802 c = pgetc2();
9803 }
9804#endif
9805 if (striptabs) {
9806 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009807 c = pgetc2();
9808 }
Eric Andersenc470f442003-07-28 09:56:35 +00009809 }
9810 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009811 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00009812 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009813
Eric Andersenc470f442003-07-28 09:56:35 +00009814 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009815 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +00009816 if (*p == '\n' && *q == '\0') {
9817 c = PEOF;
9818 plinno++;
9819 needprompt = doprompt;
9820 } else {
9821 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +00009822 }
9823 }
9824 }
9825 }
Eric Andersenc470f442003-07-28 09:56:35 +00009826 goto checkend_return;
9827}
Eric Andersencb57d552001-06-28 07:25:16 +00009828
9829
9830/*
9831 * Parse a redirection operator. The variable "out" points to a string
9832 * specifying the fd to be redirected. The variable "c" contains the
9833 * first character of the redirection operator.
9834 */
Eric Andersenc470f442003-07-28 09:56:35 +00009835parseredir: {
9836 char fd = *out;
9837 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +00009838
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009839 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +00009840 if (c == '>') {
9841 np->nfile.fd = 1;
9842 c = pgetc();
9843 if (c == '>')
9844 np->type = NAPPEND;
9845 else if (c == '|')
9846 np->type = NCLOBBER;
9847 else if (c == '&')
9848 np->type = NTOFD;
9849 else {
9850 np->type = NTO;
9851 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +00009852 }
Eric Andersenc470f442003-07-28 09:56:35 +00009853 } else { /* c == '<' */
9854 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009855 c = pgetc();
9856 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +00009857 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009858 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009859 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +00009860 np->nfile.fd = 0;
9861 }
9862 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009863 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +00009864 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009865 c = pgetc();
9866 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +00009867 heredoc->striptabs = 1;
9868 } else {
9869 heredoc->striptabs = 0;
9870 pungetc();
9871 }
9872 break;
9873
9874 case '&':
9875 np->type = NFROMFD;
9876 break;
9877
9878 case '>':
9879 np->type = NFROMTO;
9880 break;
9881
9882 default:
9883 np->type = NFROM;
9884 pungetc();
9885 break;
9886 }
Eric Andersencb57d552001-06-28 07:25:16 +00009887 }
Eric Andersenc470f442003-07-28 09:56:35 +00009888 if (fd != '\0')
9889 np->nfile.fd = digit_val(fd);
9890 redirnode = np;
9891 goto parseredir_return;
9892}
Eric Andersencb57d552001-06-28 07:25:16 +00009893
9894
9895/*
9896 * Parse a substitution. At this point, we have read the dollar sign
9897 * and nothing else.
9898 */
Eric Andersenc470f442003-07-28 09:56:35 +00009899parsesub: {
9900 int subtype;
9901 int typeloc;
9902 int flags;
9903 char *p;
9904 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +00009905
Eric Andersenc470f442003-07-28 09:56:35 +00009906 c = pgetc();
9907 if (
9908 c <= PEOA_OR_PEOF ||
9909 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
9910 ) {
9911 USTPUTC('$', out);
9912 pungetc();
9913 } else if (c == '(') { /* $(command) or $((arith)) */
9914 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009915#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +00009916 PARSEARITH();
9917#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009918 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +00009919#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009920 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009921 pungetc();
9922 PARSEBACKQNEW();
9923 }
9924 } else {
9925 USTPUTC(CTLVAR, out);
9926 typeloc = out - (char *)stackblock();
9927 USTPUTC(VSNORMAL, out);
9928 subtype = VSNORMAL;
9929 if (c == '{') {
9930 c = pgetc();
9931 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009932 c = pgetc();
9933 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +00009934 c = '#';
9935 else
9936 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009937 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009938 subtype = 0;
9939 }
9940 if (c > PEOA_OR_PEOF && is_name(c)) {
9941 do {
9942 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +00009943 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +00009944 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009945 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009946 do {
9947 STPUTC(c, out);
9948 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009949 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009950 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +00009951 USTPUTC(c, out);
9952 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009953 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009954 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +00009955
Eric Andersenc470f442003-07-28 09:56:35 +00009956 STPUTC('=', out);
9957 flags = 0;
9958 if (subtype == 0) {
9959 switch (c) {
9960 case ':':
9961 flags = VSNUL;
9962 c = pgetc();
9963 /*FALLTHROUGH*/
9964 default:
9965 p = strchr(types, c);
9966 if (p == NULL)
9967 goto badsub;
9968 subtype = p - types + VSNORMAL;
9969 break;
9970 case '%':
9971 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +00009972 {
9973 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +00009974 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009975 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +00009976 c = pgetc();
9977 if (c == cc)
9978 subtype++;
9979 else
9980 pungetc();
9981 break;
9982 }
9983 }
Eric Andersenc470f442003-07-28 09:56:35 +00009984 } else {
9985 pungetc();
9986 }
9987 if (dblquote || arinest)
9988 flags |= VSQUOTE;
9989 *((char *)stackblock() + typeloc) = subtype | flags;
9990 if (subtype != VSNORMAL) {
9991 varnest++;
9992 if (dblquote || arinest) {
9993 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +00009994 }
9995 }
9996 }
Eric Andersenc470f442003-07-28 09:56:35 +00009997 goto parsesub_return;
9998}
Eric Andersencb57d552001-06-28 07:25:16 +00009999
10000
10001/*
10002 * Called to parse command substitutions. Newstyle is set if the command
10003 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10004 * list of commands (passed by reference), and savelen is the number of
10005 * characters on the top of the stack which must be preserved.
10006 */
Eric Andersenc470f442003-07-28 09:56:35 +000010007parsebackq: {
10008 struct nodelist **nlpp;
10009 int savepbq;
10010 union node *n;
10011 char *volatile str;
10012 struct jmploc jmploc;
10013 struct jmploc *volatile savehandler;
10014 size_t savelen;
Eric Andersena68ea1c2006-01-30 22:48:39 +000010015 int saveprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010016#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010017 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010018#endif
10019
Eric Andersenc470f442003-07-28 09:56:35 +000010020 savepbq = parsebackquote;
10021 if (setjmp(jmploc.loc)) {
10022 if (str)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010023 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010024 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010025 exception_handler = savehandler;
10026 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010027 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010028 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010029 str = NULL;
10030 savelen = out - (char *)stackblock();
10031 if (savelen > 0) {
10032 str = ckmalloc(savelen);
10033 memcpy(str, stackblock(), savelen);
10034 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010035 savehandler = exception_handler;
10036 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010037 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010038 if (oldstyle) {
10039 /* We must read until the closing backquote, giving special
10040 treatment to some slashes, and then push the string and
10041 reread it as input, interpreting it normally. */
10042 char *pout;
10043 int pc;
10044 size_t psavelen;
10045 char *pstr;
10046
10047
10048 STARTSTACKSTR(pout);
10049 for (;;) {
10050 if (needprompt) {
10051 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010052 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010053 pc = pgetc();
10054 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010055 case '`':
10056 goto done;
10057
10058 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010059 pc = pgetc();
10060 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010061 plinno++;
10062 if (doprompt)
10063 setprompt(2);
10064 /*
10065 * If eating a newline, avoid putting
10066 * the newline into the new character
10067 * stream (via the STPUTC after the
10068 * switch).
10069 */
10070 continue;
10071 }
10072 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010073 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010074 STPUTC('\\', pout);
10075 if (pc > PEOA_OR_PEOF) {
10076 break;
10077 }
10078 /* fall through */
10079
10080 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010081#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010082 case PEOA:
10083#endif
10084 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010085 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010086
10087 case '\n':
10088 plinno++;
10089 needprompt = doprompt;
10090 break;
10091
10092 default:
10093 break;
10094 }
10095 STPUTC(pc, pout);
10096 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010097 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010098 STPUTC('\0', pout);
10099 psavelen = pout - (char *)stackblock();
10100 if (psavelen > 0) {
10101 pstr = grabstackstr(pout);
10102 setinputstring(pstr);
10103 }
10104 }
10105 nlpp = &bqlist;
10106 while (*nlpp)
10107 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010108 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010109 (*nlpp)->next = NULL;
10110 parsebackquote = oldstyle;
10111
10112 if (oldstyle) {
10113 saveprompt = doprompt;
10114 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010115 }
10116
Eric Andersenc470f442003-07-28 09:56:35 +000010117 n = list(2);
10118
10119 if (oldstyle)
10120 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010121 else if (readtoken() != TRP)
10122 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010123
10124 (*nlpp)->n = n;
10125 if (oldstyle) {
10126 /*
10127 * Start reading from old file again, ignoring any pushed back
10128 * tokens left from the backquote parsing
10129 */
10130 popfile();
10131 tokpushback = 0;
10132 }
10133 while (stackblocksize() <= savelen)
10134 growstackblock();
10135 STARTSTACKSTR(out);
10136 if (str) {
10137 memcpy(out, str, savelen);
10138 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010139 INT_OFF;
10140 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010141 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010142 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010143 }
10144 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010145 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010146 if (arinest || dblquote)
10147 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10148 else
10149 USTPUTC(CTLBACKQ, out);
10150 if (oldstyle)
10151 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010152 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010153}
10154
Denis Vlasenko131ae172007-02-18 13:00:19 +000010155#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010156/*
10157 * Parse an arithmetic expansion (indicate start of one and set state)
10158 */
Eric Andersenc470f442003-07-28 09:56:35 +000010159parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010160 if (++arinest == 1) {
10161 prevsyntax = syntax;
10162 syntax = ARISYNTAX;
10163 USTPUTC(CTLARI, out);
10164 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010165 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010166 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010167 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010168 } else {
10169 /*
10170 * we collapse embedded arithmetic expansion to
10171 * parenthesis, which should be equivalent
10172 */
10173 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010174 }
Eric Andersenc470f442003-07-28 09:56:35 +000010175 goto parsearith_return;
10176}
10177#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010178
Eric Andersenc470f442003-07-28 09:56:35 +000010179} /* end of readtoken */
10180
Eric Andersencb57d552001-06-28 07:25:16 +000010181/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010182 * Read the next input token.
10183 * If the token is a word, we set backquotelist to the list of cmds in
10184 * backquotes. We set quoteflag to true if any part of the word was
10185 * quoted.
10186 * If the token is TREDIR, then we set redirnode to a structure containing
10187 * the redirection.
10188 * In all cases, the variable startlinno is set to the number of the line
10189 * on which the token starts.
10190 *
10191 * [Change comment: here documents and internal procedures]
10192 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10193 * word parsing code into a separate routine. In this case, readtoken
10194 * doesn't need to have any internal procedures, but parseword does.
10195 * We could also make parseoperator in essence the main routine, and
10196 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010197 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010198#define NEW_xxreadtoken
10199#ifdef NEW_xxreadtoken
10200/* singles must be first! */
10201static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
Eric Andersencb57d552001-06-28 07:25:16 +000010202
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010203static const char xxreadtoken_tokens[] = {
10204 TNL, TLP, TRP, /* only single occurrence allowed */
10205 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10206 TEOF, /* corresponds to trailing nul */
10207 TAND, TOR, TENDCASE, /* if double occurrence */
10208};
10209
10210#define xxreadtoken_doubles \
10211 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10212#define xxreadtoken_singles \
10213 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10214
10215static int
10216xxreadtoken(void)
10217{
10218 int c;
10219
10220 if (tokpushback) {
10221 tokpushback = 0;
10222 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010223 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010224 if (needprompt) {
10225 setprompt(2);
10226 }
10227 startlinno = plinno;
10228 for (;;) { /* until token or start of word found */
10229 c = pgetc_macro();
10230
10231 if ((c != ' ') && (c != '\t')
10232#if ENABLE_ASH_ALIAS
10233 && (c != PEOA)
10234#endif
10235 ) {
10236 if (c == '#') {
10237 while ((c = pgetc()) != '\n' && c != PEOF);
10238 pungetc();
10239 } else if (c == '\\') {
10240 if (pgetc() != '\n') {
10241 pungetc();
10242 goto READTOKEN1;
10243 }
10244 startlinno = ++plinno;
10245 if (doprompt)
10246 setprompt(2);
10247 } else {
10248 const char *p
10249 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10250
10251 if (c != PEOF) {
10252 if (c == '\n') {
10253 plinno++;
10254 needprompt = doprompt;
10255 }
10256
10257 p = strchr(xxreadtoken_chars, c);
10258 if (p == NULL) {
10259 READTOKEN1:
10260 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10261 }
10262
10263 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10264 if (pgetc() == *p) { /* double occurrence? */
10265 p += xxreadtoken_doubles + 1;
10266 } else {
10267 pungetc();
10268 }
10269 }
10270 }
10271 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10272 }
10273 }
10274 } /* for */
10275}
10276#else
10277#define RETURN(token) return lasttoken = token
10278static int
10279xxreadtoken(void)
10280{
10281 int c;
10282
10283 if (tokpushback) {
10284 tokpushback = 0;
10285 return lasttoken;
10286 }
10287 if (needprompt) {
10288 setprompt(2);
10289 }
10290 startlinno = plinno;
10291 for (;;) { /* until token or start of word found */
10292 c = pgetc_macro();
10293 switch (c) {
10294 case ' ': case '\t':
10295#if ENABLE_ASH_ALIAS
10296 case PEOA:
10297#endif
10298 continue;
10299 case '#':
10300 while ((c = pgetc()) != '\n' && c != PEOF);
10301 pungetc();
10302 continue;
10303 case '\\':
10304 if (pgetc() == '\n') {
10305 startlinno = ++plinno;
10306 if (doprompt)
10307 setprompt(2);
10308 continue;
10309 }
10310 pungetc();
10311 goto breakloop;
10312 case '\n':
10313 plinno++;
10314 needprompt = doprompt;
10315 RETURN(TNL);
10316 case PEOF:
10317 RETURN(TEOF);
10318 case '&':
10319 if (pgetc() == '&')
10320 RETURN(TAND);
10321 pungetc();
10322 RETURN(TBACKGND);
10323 case '|':
10324 if (pgetc() == '|')
10325 RETURN(TOR);
10326 pungetc();
10327 RETURN(TPIPE);
10328 case ';':
10329 if (pgetc() == ';')
10330 RETURN(TENDCASE);
10331 pungetc();
10332 RETURN(TSEMI);
10333 case '(':
10334 RETURN(TLP);
10335 case ')':
10336 RETURN(TRP);
10337 default:
10338 goto breakloop;
10339 }
10340 }
10341 breakloop:
10342 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10343#undef RETURN
10344}
10345#endif /* NEW_xxreadtoken */
10346
10347static int
10348readtoken(void)
10349{
10350 int t;
10351#if DEBUG
10352 int alreadyseen = tokpushback;
10353#endif
10354
10355#if ENABLE_ASH_ALIAS
10356 top:
10357#endif
10358
10359 t = xxreadtoken();
10360
10361 /*
10362 * eat newlines
10363 */
10364 if (checkkwd & CHKNL) {
10365 while (t == TNL) {
10366 parseheredoc();
10367 t = xxreadtoken();
10368 }
10369 }
10370
10371 if (t != TWORD || quoteflag) {
10372 goto out;
10373 }
10374
10375 /*
10376 * check for keywords
10377 */
10378 if (checkkwd & CHKKWD) {
10379 const char *const *pp;
10380
10381 pp = findkwd(wordtext);
10382 if (pp) {
10383 lasttoken = t = pp - tokname_array;
10384 TRACE(("keyword %s recognized\n", tokname(t)));
10385 goto out;
10386 }
10387 }
10388
10389 if (checkkwd & CHKALIAS) {
10390#if ENABLE_ASH_ALIAS
10391 struct alias *ap;
10392 ap = lookupalias(wordtext, 1);
10393 if (ap != NULL) {
10394 if (*ap->val) {
10395 pushstring(ap->val, ap);
10396 }
10397 goto top;
10398 }
10399#endif
10400 }
10401 out:
10402 checkkwd = 0;
10403#if DEBUG
10404 if (!alreadyseen)
10405 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10406 else
10407 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10408#endif
10409 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010410}
10411
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010412static char
10413peektoken(void)
10414{
10415 int t;
10416
10417 t = readtoken();
10418 tokpushback++;
10419 return tokname_array[t][0];
10420}
Eric Andersencb57d552001-06-28 07:25:16 +000010421
10422/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010423 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10424 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010425 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010426static union node *
10427parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010428{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010429 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010430
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010431 tokpushback = 0;
10432 doprompt = interact;
10433 if (doprompt)
10434 setprompt(doprompt);
10435 needprompt = 0;
10436 t = readtoken();
10437 if (t == TEOF)
10438 return NEOF;
10439 if (t == TNL)
10440 return NULL;
10441 tokpushback++;
10442 return list(1);
10443}
10444
10445/*
10446 * Input any here documents.
10447 */
10448static void
10449parseheredoc(void)
10450{
10451 struct heredoc *here;
10452 union node *n;
10453
10454 here = heredoclist;
10455 heredoclist = 0;
10456
10457 while (here) {
10458 if (needprompt) {
10459 setprompt(2);
10460 }
10461 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10462 here->eofmark, here->striptabs);
10463 n = stalloc(sizeof(struct narg));
10464 n->narg.type = NARG;
10465 n->narg.next = NULL;
10466 n->narg.text = wordtext;
10467 n->narg.backquote = backquotelist;
10468 here->here->nhere.doc = n;
10469 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010470 }
Eric Andersencb57d552001-06-28 07:25:16 +000010471}
10472
10473
10474/*
Eric Andersencb57d552001-06-28 07:25:16 +000010475 * called by editline -- any expansions to the prompt
10476 * should be added here.
10477 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010478#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010479static const char *
10480expandstr(const char *ps)
10481{
10482 union node n;
10483
10484 /* XXX Fix (char *) cast. */
10485 setinputstring((char *)ps);
10486 readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
10487 popfile();
10488
10489 n.narg.type = NARG;
10490 n.narg.next = NULL;
10491 n.narg.text = wordtext;
10492 n.narg.backquote = backquotelist;
10493
10494 expandarg(&n, NULL, 0);
10495 return stackblock();
10496}
10497#endif
10498
Eric Andersencb57d552001-06-28 07:25:16 +000010499
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010500/*
10501 * Execute a command or commands contained in a string.
10502 */
10503static int
10504evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010505{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010506 union node *n;
10507 struct stackmark smark;
10508 int skip;
10509
10510 setinputstring(s);
10511 setstackmark(&smark);
10512
10513 skip = 0;
10514 while ((n = parsecmd(0)) != NEOF) {
10515 evaltree(n, 0);
10516 popstackmark(&smark);
10517 skip = evalskip;
10518 if (skip)
10519 break;
10520 }
10521 popfile();
10522
10523 skip &= mask;
10524 evalskip = skip;
10525 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010526}
10527
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010528/*
10529 * The eval command.
10530 */
10531static int
10532evalcmd(int argc, char **argv)
10533{
10534 char *p;
10535 char *concat;
10536 char **ap;
10537
10538 if (argc > 1) {
10539 p = argv[1];
10540 if (argc > 2) {
10541 STARTSTACKSTR(concat);
10542 ap = argv + 2;
10543 for (;;) {
10544 concat = stack_putstr(p, concat);
10545 p = *ap++;
10546 if (p == NULL)
10547 break;
10548 STPUTC(' ', concat);
10549 }
10550 STPUTC('\0', concat);
10551 p = grabstackstr(concat);
10552 }
10553 evalstring(p, ~SKIPEVAL);
10554
10555 }
10556 return exitstatus;
10557}
10558
10559/*
10560 * Read and execute commands. "Top" is nonzero for the top level command
10561 * loop; it turns on prompting if the shell is interactive.
10562 */
10563static int
10564cmdloop(int top)
10565{
10566 union node *n;
10567 struct stackmark smark;
10568 int inter;
10569 int numeof = 0;
10570
10571 TRACE(("cmdloop(%d) called\n", top));
10572 for (;;) {
10573 int skip;
10574
10575 setstackmark(&smark);
10576#if JOBS
10577 if (jobctl)
10578 showjobs(stderr, SHOW_CHANGED);
10579#endif
10580 inter = 0;
10581 if (iflag && top) {
10582 inter++;
10583#if ENABLE_ASH_MAIL
10584 chkmail();
10585#endif
10586 }
10587 n = parsecmd(inter);
10588 /* showtree(n); DEBUG */
10589 if (n == NEOF) {
10590 if (!top || numeof >= 50)
10591 break;
10592 if (!stoppedjobs()) {
10593 if (!Iflag)
10594 break;
10595 out2str("\nUse \"exit\" to leave shell.\n");
10596 }
10597 numeof++;
10598 } else if (nflag == 0) {
10599 job_warning = (job_warning == 2) ? 1 : 0;
10600 numeof = 0;
10601 evaltree(n, 0);
10602 }
10603 popstackmark(&smark);
10604 skip = evalskip;
10605
10606 if (skip) {
10607 evalskip = 0;
10608 return skip & SKIPEVAL;
10609 }
10610 }
10611 return 0;
10612}
10613
10614static int
10615dotcmd(int argc, char **argv)
10616{
10617 struct strlist *sp;
10618 volatile struct shparam saveparam;
10619 int status = 0;
10620
10621 for (sp = cmdenviron; sp; sp = sp->next)
10622 setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
10623
10624 if (argc >= 2) { /* That's what SVR2 does */
10625 char *fullname;
10626
10627 fullname = find_dot_file(argv[1]);
10628
10629 if (argc > 2) {
10630 saveparam = shellparam;
10631 shellparam.malloc = 0;
10632 shellparam.nparam = argc - 2;
10633 shellparam.p = argv + 2;
10634 };
10635
10636 setinputfile(fullname, INPUT_PUSH_FILE);
10637 commandname = fullname;
10638 cmdloop(0);
10639 popfile();
10640
10641 if (argc > 2) {
10642 freeparam(&shellparam);
10643 shellparam = saveparam;
10644 };
10645 status = exitstatus;
10646 }
10647 return status;
10648}
10649
10650static int
10651exitcmd(int argc, char **argv)
10652{
10653 if (stoppedjobs())
10654 return 0;
10655 if (argc > 1)
10656 exitstatus = number(argv[1]);
10657 raise_exception(EXEXIT);
10658 /* NOTREACHED */
10659}
10660
10661#if ENABLE_ASH_BUILTIN_ECHO
10662static int
10663echocmd(int argc, char **argv)
10664{
10665 return bb_echo(argv);
10666}
10667#endif
10668
10669#if ENABLE_ASH_BUILTIN_TEST
10670static int
10671testcmd(int argc, char **argv)
10672{
10673 return bb_test(argc, argv);
10674}
10675#endif
10676
10677/*
10678 * Read a file containing shell functions.
10679 */
10680static void
10681readcmdfile(char *name)
10682{
10683 setinputfile(name, INPUT_PUSH_FILE);
10684 cmdloop(0);
10685 popfile();
10686}
10687
10688
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010689/* redir.c */
Eric Andersenc470f442003-07-28 09:56:35 +000010690
Eric Andersencb57d552001-06-28 07:25:16 +000010691/*
10692 * Code for dealing with input/output redirection.
10693 */
10694
Eric Andersenc470f442003-07-28 09:56:35 +000010695#define EMPTY -2 /* marks an unused slot in redirtab */
Eric Andersencb57d552001-06-28 07:25:16 +000010696#ifndef PIPE_BUF
Eric Andersenc470f442003-07-28 09:56:35 +000010697# define PIPESIZE 4096 /* amount of buffering in a pipe */
Eric Andersencb57d552001-06-28 07:25:16 +000010698#else
10699# define PIPESIZE PIPE_BUF
10700#endif
10701
Eric Andersen62483552001-07-10 06:09:16 +000010702/*
10703 * Open a file in noclobber mode.
10704 * The code was copied from bash.
10705 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010706static int
10707noclobberopen(const char *fname)
Eric Andersen62483552001-07-10 06:09:16 +000010708{
10709 int r, fd;
10710 struct stat finfo, finfo2;
10711
10712 /*
10713 * If the file exists and is a regular file, return an error
10714 * immediately.
10715 */
10716 r = stat(fname, &finfo);
10717 if (r == 0 && S_ISREG(finfo.st_mode)) {
10718 errno = EEXIST;
10719 return -1;
10720 }
10721
10722 /*
10723 * If the file was not present (r != 0), make sure we open it
10724 * exclusively so that if it is created before we open it, our open
10725 * will fail. Make sure that we do not truncate an existing file.
10726 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10727 * file was not a regular file, we leave O_EXCL off.
10728 */
10729 if (r != 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010730 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10731 fd = open(fname, O_WRONLY|O_CREAT, 0666);
Eric Andersen62483552001-07-10 06:09:16 +000010732
10733 /* If the open failed, return the file descriptor right away. */
10734 if (fd < 0)
10735 return fd;
10736
10737 /*
10738 * OK, the open succeeded, but the file may have been changed from a
10739 * non-regular file to a regular file between the stat and the open.
10740 * We are assuming that the O_EXCL open handles the case where FILENAME
10741 * did not exist and is symlinked to an existing file between the stat
10742 * and open.
10743 */
10744
10745 /*
10746 * If we can open it and fstat the file descriptor, and neither check
10747 * revealed that it was a regular file, and the file has not been
10748 * replaced, return the file descriptor.
10749 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010750 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
10751 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
Eric Andersen62483552001-07-10 06:09:16 +000010752 return fd;
10753
10754 /* The file has been replaced. badness. */
10755 close(fd);
10756 errno = EEXIST;
10757 return -1;
10758}
Eric Andersencb57d552001-06-28 07:25:16 +000010759
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010760
Eric Andersencb57d552001-06-28 07:25:16 +000010761/*
Eric Andersen62483552001-07-10 06:09:16 +000010762 * Handle here documents. Normally we fork off a process to write the
10763 * data to a pipe. If the document is short, we can stuff the data in
10764 * the pipe without forking.
Eric Andersencb57d552001-06-28 07:25:16 +000010765 */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010766static int
10767openhere(union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010768{
10769 int pip[2];
Eric Andersenc470f442003-07-28 09:56:35 +000010770 size_t len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010771
Eric Andersen62483552001-07-10 06:09:16 +000010772 if (pipe(pip) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010773 ash_msg_and_raise_error("Pipe call failed");
Eric Andersen62483552001-07-10 06:09:16 +000010774 if (redir->type == NHERE) {
10775 len = strlen(redir->nhere.doc->narg.text);
10776 if (len <= PIPESIZE) {
Rob Landley53437472006-07-16 08:14:35 +000010777 full_write(pip[1], redir->nhere.doc->narg.text, len);
Eric Andersen62483552001-07-10 06:09:16 +000010778 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010779 }
Eric Andersencb57d552001-06-28 07:25:16 +000010780 }
Eric Andersenc470f442003-07-28 09:56:35 +000010781 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Eric Andersen62483552001-07-10 06:09:16 +000010782 close(pip[0]);
10783 signal(SIGINT, SIG_IGN);
10784 signal(SIGQUIT, SIG_IGN);
10785 signal(SIGHUP, SIG_IGN);
10786#ifdef SIGTSTP
10787 signal(SIGTSTP, SIG_IGN);
10788#endif
10789 signal(SIGPIPE, SIG_DFL);
10790 if (redir->type == NHERE)
Rob Landley53437472006-07-16 08:14:35 +000010791 full_write(pip[1], redir->nhere.doc->narg.text, len);
Eric Andersen62483552001-07-10 06:09:16 +000010792 else
10793 expandhere(redir->nhere.doc, pip[1]);
10794 _exit(0);
10795 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010796 out:
Eric Andersen62483552001-07-10 06:09:16 +000010797 close(pip[1]);
10798 return pip[0];
Eric Andersencb57d552001-06-28 07:25:16 +000010799}
10800
Eric Andersenc470f442003-07-28 09:56:35 +000010801static int
10802openredirect(union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010803{
Eric Andersencb57d552001-06-28 07:25:16 +000010804 char *fname;
10805 int f;
10806
10807 switch (redir->nfile.type) {
10808 case NFROM:
10809 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010810 f = open(fname, O_RDONLY);
10811 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010812 goto eopen;
10813 break;
10814 case NFROMTO:
10815 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010816 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
10817 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010818 goto ecreate;
10819 break;
10820 case NTO:
10821 /* Take care of noclobber mode. */
10822 if (Cflag) {
10823 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010824 f = noclobberopen(fname);
10825 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010826 goto ecreate;
10827 break;
10828 }
Eric Andersenc470f442003-07-28 09:56:35 +000010829 /* FALLTHROUGH */
10830 case NCLOBBER:
Eric Andersencb57d552001-06-28 07:25:16 +000010831 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010832 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
10833 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010834 goto ecreate;
Eric Andersencb57d552001-06-28 07:25:16 +000010835 break;
10836 case NAPPEND:
10837 fname = redir->nfile.expfname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010838 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
10839 if (f < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010840 goto ecreate;
Eric Andersencb57d552001-06-28 07:25:16 +000010841 break;
10842 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010843#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010844 abort();
10845#endif
10846 /* Fall through to eliminate warning. */
10847 case NTOFD:
10848 case NFROMFD:
10849 f = -1;
10850 break;
10851 case NHERE:
10852 case NXHERE:
10853 f = openhere(redir);
10854 break;
10855 }
10856
10857 return f;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010858 ecreate:
Denis Vlasenkob012b102007-02-19 22:43:01 +000010859 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010860 eopen:
Denis Vlasenkob012b102007-02-19 22:43:01 +000010861 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
Eric Andersencb57d552001-06-28 07:25:16 +000010862}
10863
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010864static void
10865dupredirect(union node *redir, int f)
Eric Andersenc470f442003-07-28 09:56:35 +000010866{
10867 int fd = redir->nfile.fd;
10868
10869 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
10870 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010871 copyfd(redir->ndup.dupfd, fd);
Eric Andersenc470f442003-07-28 09:56:35 +000010872 }
10873 return;
10874 }
10875
10876 if (f != fd) {
10877 copyfd(f, fd);
10878 close(f);
10879 }
Eric Andersenc470f442003-07-28 09:56:35 +000010880}
Eric Andersencb57d552001-06-28 07:25:16 +000010881
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010882
Eric Andersen62483552001-07-10 06:09:16 +000010883/*
10884 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
10885 * old file descriptors are stashed away so that the redirection can be
10886 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
10887 * standard output, and the standard error if it becomes a duplicate of
Eric Andersenc470f442003-07-28 09:56:35 +000010888 * stdout, is saved in memory.
Eric Andersen62483552001-07-10 06:09:16 +000010889 */
Eric Andersenc470f442003-07-28 09:56:35 +000010890static void
10891redirect(union node *redir, int flags)
Eric Andersen62483552001-07-10 06:09:16 +000010892{
10893 union node *n;
Eric Andersenc470f442003-07-28 09:56:35 +000010894 struct redirtab *sv;
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010895 int i;
Eric Andersen62483552001-07-10 06:09:16 +000010896 int fd;
10897 int newfd;
Eric Andersenc470f442003-07-28 09:56:35 +000010898 int *p;
10899 nullredirs++;
10900 if (!redir) {
10901 return;
Eric Andersen62483552001-07-10 06:09:16 +000010902 }
Eric Andersenc470f442003-07-28 09:56:35 +000010903 sv = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010904 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010905 if (flags & REDIR_PUSH) {
10906 struct redirtab *q;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010907 q = ckmalloc(sizeof(struct redirtab));
Eric Andersenc470f442003-07-28 09:56:35 +000010908 q->next = redirlist;
10909 redirlist = q;
10910 q->nullredirs = nullredirs - 1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010911 for (i = 0; i < 10; i++)
Eric Andersenc470f442003-07-28 09:56:35 +000010912 q->renamed[i] = EMPTY;
10913 nullredirs = 0;
10914 sv = q;
10915 }
10916 n = redir;
10917 do {
Eric Andersen62483552001-07-10 06:09:16 +000010918 fd = n->nfile.fd;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010919 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
10920 && n->ndup.dupfd == fd)
Eric Andersenc470f442003-07-28 09:56:35 +000010921 continue; /* redirect from/to same file descriptor */
Eric Andersen62483552001-07-10 06:09:16 +000010922
Eric Andersen62483552001-07-10 06:09:16 +000010923 newfd = openredirect(n);
Eric Andersenc470f442003-07-28 09:56:35 +000010924 if (fd == newfd)
10925 continue;
10926 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
10927 i = fcntl(fd, F_DUPFD, 10);
10928
10929 if (i == -1) {
10930 i = errno;
10931 if (i != EBADF) {
10932 close(newfd);
10933 errno = i;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010934 ash_msg_and_raise_error("%d: %m", fd);
Eric Andersen62483552001-07-10 06:09:16 +000010935 /* NOTREACHED */
10936 }
Eric Andersenc470f442003-07-28 09:56:35 +000010937 } else {
10938 *p = i;
Eric Andersen62483552001-07-10 06:09:16 +000010939 close(fd);
Eric Andersen62483552001-07-10 06:09:16 +000010940 }
Eric Andersenc470f442003-07-28 09:56:35 +000010941 } else {
Eric Andersen62483552001-07-10 06:09:16 +000010942 close(fd);
10943 }
Eric Andersenc470f442003-07-28 09:56:35 +000010944 dupredirect(n, newfd);
10945 } while ((n = n->nfile.next));
Denis Vlasenkob012b102007-02-19 22:43:01 +000010946 INT_ON;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010947 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
10948 preverrout_fd = sv->renamed[2];
Eric Andersen62483552001-07-10 06:09:16 +000010949}
10950
10951
Eric Andersencb57d552001-06-28 07:25:16 +000010952/*
10953 * Undo the effects of the last redirection.
10954 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010955static void
Eric Andersenc470f442003-07-28 09:56:35 +000010956popredir(int drop)
Eric Andersen2870d962001-07-02 17:27:21 +000010957{
Eric Andersenc470f442003-07-28 09:56:35 +000010958 struct redirtab *rp;
Eric Andersencb57d552001-06-28 07:25:16 +000010959 int i;
10960
Eric Andersenc470f442003-07-28 09:56:35 +000010961 if (--nullredirs >= 0)
10962 return;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010963 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010964 rp = redirlist;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010965 for (i = 0; i < 10; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000010966 if (rp->renamed[i] != EMPTY) {
Eric Andersenc470f442003-07-28 09:56:35 +000010967 if (!drop) {
10968 close(i);
10969 copyfd(rp->renamed[i], i);
Eric Andersencb57d552001-06-28 07:25:16 +000010970 }
Eric Andersenc470f442003-07-28 09:56:35 +000010971 close(rp->renamed[i]);
Eric Andersencb57d552001-06-28 07:25:16 +000010972 }
10973 }
10974 redirlist = rp->next;
Eric Andersenc470f442003-07-28 09:56:35 +000010975 nullredirs = rp->nullredirs;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010976 free(rp);
10977 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010978}
10979
10980/*
Eric Andersenc470f442003-07-28 09:56:35 +000010981 * Undo all redirections. Called on error or interrupt.
10982 */
10983
10984/*
Eric Andersencb57d552001-06-28 07:25:16 +000010985 * Discard all saved file descriptors.
10986 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010987static void
Eric Andersenc470f442003-07-28 09:56:35 +000010988clearredir(int drop)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010989{
Eric Andersenc470f442003-07-28 09:56:35 +000010990 for (;;) {
10991 nullredirs = 0;
10992 if (!redirlist)
10993 break;
10994 popredir(drop);
Eric Andersencb57d552001-06-28 07:25:16 +000010995 }
Eric Andersencb57d552001-06-28 07:25:16 +000010996}
10997
10998
Eric Andersencb57d552001-06-28 07:25:16 +000010999/*
11000 * Copy a file descriptor to be >= to. Returns -1
11001 * if the source file descriptor is closed, EMPTY if there are no unused
11002 * file descriptors left.
11003 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011004static int
Eric Andersenc470f442003-07-28 09:56:35 +000011005copyfd(int from, int to)
Eric Andersencb57d552001-06-28 07:25:16 +000011006{
11007 int newfd;
11008
11009 newfd = fcntl(from, F_DUPFD, to);
11010 if (newfd < 0) {
11011 if (errno == EMFILE)
11012 return EMPTY;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011013 ash_msg_and_raise_error("%d: %m", from);
Eric Andersencb57d552001-06-28 07:25:16 +000011014 }
11015 return newfd;
11016}
11017
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011018static int
Eric Andersenc470f442003-07-28 09:56:35 +000011019redirectsafe(union node *redir, int flags)
11020{
11021 int err;
11022 volatile int saveint;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011023 struct jmploc *volatile savehandler = exception_handler;
Eric Andersenc470f442003-07-28 09:56:35 +000011024 struct jmploc jmploc;
11025
Denis Vlasenkob012b102007-02-19 22:43:01 +000011026 SAVE_INT(saveint);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011027 err = setjmp(jmploc.loc) * 2;
11028 if (!err) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011029 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000011030 redirect(redir, flags);
11031 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011032 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011033 if (err && exception != EXERROR)
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011034 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011035 RESTORE_INT(saveint);
Eric Andersenc470f442003-07-28 09:56:35 +000011036 return err;
11037}
11038
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011039/* show.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011040
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011041#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +000011042static void shtree(union node *, int, char *, FILE*);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011043static void shcmd(union node *, FILE *);
11044static void sharg(union node *, FILE *);
11045static void indent(int, char *, FILE *);
11046static void trstring(char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011047
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011048static void
Eric Andersenc470f442003-07-28 09:56:35 +000011049showtree(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +000011050{
11051 trputs("showtree called\n");
11052 shtree(n, 1, NULL, stdout);
11053}
Eric Andersencb57d552001-06-28 07:25:16 +000011054
Eric Andersenc470f442003-07-28 09:56:35 +000011055static void
11056shtree(union node *n, int ind, char *pfx, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011057{
11058 struct nodelist *lp;
11059 const char *s;
11060
11061 if (n == NULL)
11062 return;
11063
11064 indent(ind, pfx, fp);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011065 switch (n->type) {
Eric Andersencb57d552001-06-28 07:25:16 +000011066 case NSEMI:
11067 s = "; ";
11068 goto binop;
11069 case NAND:
11070 s = " && ";
11071 goto binop;
11072 case NOR:
11073 s = " || ";
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011074 binop:
Eric Andersencb57d552001-06-28 07:25:16 +000011075 shtree(n->nbinary.ch1, ind, NULL, fp);
Eric Andersenc470f442003-07-28 09:56:35 +000011076 /* if (ind < 0) */
11077 fputs(s, fp);
Eric Andersencb57d552001-06-28 07:25:16 +000011078 shtree(n->nbinary.ch2, ind, NULL, fp);
11079 break;
11080 case NCMD:
11081 shcmd(n, fp);
11082 if (ind >= 0)
11083 putc('\n', fp);
11084 break;
11085 case NPIPE:
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011086 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +000011087 shcmd(lp->n, fp);
11088 if (lp->next)
11089 fputs(" | ", fp);
11090 }
11091 if (n->npipe.backgnd)
11092 fputs(" &", fp);
11093 if (ind >= 0)
11094 putc('\n', fp);
11095 break;
11096 default:
11097 fprintf(fp, "<node type %d>", n->type);
11098 if (ind >= 0)
11099 putc('\n', fp);
11100 break;
11101 }
11102}
11103
Eric Andersenc470f442003-07-28 09:56:35 +000011104static void
11105shcmd(union node *cmd, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011106{
11107 union node *np;
11108 int first;
11109 const char *s;
11110 int dftfd;
11111
11112 first = 1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011113 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Eric Andersenc470f442003-07-28 09:56:35 +000011114 if (! first)
Eric Andersencb57d552001-06-28 07:25:16 +000011115 putchar(' ');
11116 sharg(np, fp);
11117 first = 0;
11118 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011119 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +000011120 if (! first)
Eric Andersencb57d552001-06-28 07:25:16 +000011121 putchar(' ');
11122 switch (np->nfile.type) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011123 case NTO: s = ">"; dftfd = 1; break;
11124 case NCLOBBER: s = ">|"; dftfd = 1; break;
11125 case NAPPEND: s = ">>"; dftfd = 1; break;
11126 case NTOFD: s = ">&"; dftfd = 1; break;
11127 case NFROM: s = "<"; dftfd = 0; break;
11128 case NFROMFD: s = "<&"; dftfd = 0; break;
11129 case NFROMTO: s = "<>"; dftfd = 0; break;
11130 default: s = "*error*"; dftfd = 0; break;
Eric Andersencb57d552001-06-28 07:25:16 +000011131 }
11132 if (np->nfile.fd != dftfd)
11133 fprintf(fp, "%d", np->nfile.fd);
11134 fputs(s, fp);
11135 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11136 fprintf(fp, "%d", np->ndup.dupfd);
11137 } else {
11138 sharg(np->nfile.fname, fp);
11139 }
11140 first = 0;
11141 }
11142}
11143
Eric Andersenc470f442003-07-28 09:56:35 +000011144static void
11145sharg(union node *arg, FILE *fp)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011146{
Eric Andersencb57d552001-06-28 07:25:16 +000011147 char *p;
11148 struct nodelist *bqlist;
11149 int subtype;
11150
11151 if (arg->type != NARG) {
Eric Andersenc470f442003-07-28 09:56:35 +000011152 out1fmt("<node type %d>\n", arg->type);
Eric Andersencb57d552001-06-28 07:25:16 +000011153 abort();
11154 }
11155 bqlist = arg->narg.backquote;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011156 for (p = arg->narg.text; *p; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011157 switch (*p) {
11158 case CTLESC:
11159 putc(*++p, fp);
11160 break;
11161 case CTLVAR:
11162 putc('$', fp);
11163 putc('{', fp);
11164 subtype = *++p;
11165 if (subtype == VSLENGTH)
11166 putc('#', fp);
11167
11168 while (*p != '=')
11169 putc(*p++, fp);
11170
11171 if (subtype & VSNUL)
11172 putc(':', fp);
11173
11174 switch (subtype & VSTYPE) {
11175 case VSNORMAL:
11176 putc('}', fp);
11177 break;
11178 case VSMINUS:
11179 putc('-', fp);
11180 break;
11181 case VSPLUS:
11182 putc('+', fp);
11183 break;
11184 case VSQUESTION:
11185 putc('?', fp);
11186 break;
11187 case VSASSIGN:
11188 putc('=', fp);
11189 break;
11190 case VSTRIMLEFT:
11191 putc('#', fp);
11192 break;
11193 case VSTRIMLEFTMAX:
11194 putc('#', fp);
11195 putc('#', fp);
11196 break;
11197 case VSTRIMRIGHT:
11198 putc('%', fp);
11199 break;
11200 case VSTRIMRIGHTMAX:
11201 putc('%', fp);
11202 putc('%', fp);
11203 break;
11204 case VSLENGTH:
11205 break;
11206 default:
Eric Andersenc470f442003-07-28 09:56:35 +000011207 out1fmt("<subtype %d>", subtype);
Eric Andersencb57d552001-06-28 07:25:16 +000011208 }
11209 break;
11210 case CTLENDVAR:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011211 putc('}', fp);
11212 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011213 case CTLBACKQ:
Eric Andersenc470f442003-07-28 09:56:35 +000011214 case CTLBACKQ|CTLQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011215 putc('$', fp);
11216 putc('(', fp);
11217 shtree(bqlist->n, -1, NULL, fp);
11218 putc(')', fp);
11219 break;
11220 default:
11221 putc(*p, fp);
11222 break;
11223 }
11224 }
11225}
11226
11227
Eric Andersenc470f442003-07-28 09:56:35 +000011228static void
11229indent(int amount, char *pfx, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011230{
11231 int i;
11232
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011233 for (i = 0; i < amount; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011234 if (pfx && i == amount - 1)
11235 fputs(pfx, fp);
11236 putc('\t', fp);
11237 }
11238}
Eric Andersencb57d552001-06-28 07:25:16 +000011239
Eric Andersenc470f442003-07-28 09:56:35 +000011240
Eric Andersenc470f442003-07-28 09:56:35 +000011241/*
11242 * Debugging stuff.
11243 */
11244
11245
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011246static FILE *tracefile;
Eric Andersencb57d552001-06-28 07:25:16 +000011247
Eric Andersencb57d552001-06-28 07:25:16 +000011248
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011249static void
Eric Andersenc470f442003-07-28 09:56:35 +000011250trputc(int c)
Eric Andersencb57d552001-06-28 07:25:16 +000011251{
Eric Andersenc470f442003-07-28 09:56:35 +000011252 if (debug != 1)
Eric Andersencb57d552001-06-28 07:25:16 +000011253 return;
11254 putc(c, tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011255}
11256
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011257static void
Eric Andersenc470f442003-07-28 09:56:35 +000011258trace(const char *fmt, ...)
Eric Andersencb57d552001-06-28 07:25:16 +000011259{
11260 va_list va;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011261
Eric Andersenc470f442003-07-28 09:56:35 +000011262 if (debug != 1)
11263 return;
Eric Andersencb57d552001-06-28 07:25:16 +000011264 va_start(va, fmt);
Eric Andersenc470f442003-07-28 09:56:35 +000011265 (void) vfprintf(tracefile, fmt, va);
Eric Andersencb57d552001-06-28 07:25:16 +000011266 va_end(va);
11267}
11268
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011269static void
Eric Andersenc470f442003-07-28 09:56:35 +000011270tracev(const char *fmt, va_list va)
Eric Andersencb57d552001-06-28 07:25:16 +000011271{
Eric Andersenc470f442003-07-28 09:56:35 +000011272 if (debug != 1)
Eric Andersencb57d552001-06-28 07:25:16 +000011273 return;
Eric Andersenc470f442003-07-28 09:56:35 +000011274 (void) vfprintf(tracefile, fmt, va);
Eric Andersencb57d552001-06-28 07:25:16 +000011275}
11276
11277
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011278static void
Eric Andersenc470f442003-07-28 09:56:35 +000011279trputs(const char *s)
11280{
11281 if (debug != 1)
11282 return;
11283 fputs(s, tracefile);
11284}
11285
11286
11287static void
11288trstring(char *s)
Eric Andersencb57d552001-06-28 07:25:16 +000011289{
11290 char *p;
11291 char c;
11292
Eric Andersenc470f442003-07-28 09:56:35 +000011293 if (debug != 1)
Eric Andersencb57d552001-06-28 07:25:16 +000011294 return;
11295 putc('"', tracefile);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011296 for (p = s; *p; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011297 switch (*p) {
Eric Andersenc470f442003-07-28 09:56:35 +000011298 case '\n': c = 'n'; goto backslash;
11299 case '\t': c = 't'; goto backslash;
11300 case '\r': c = 'r'; goto backslash;
11301 case '"': c = '"'; goto backslash;
11302 case '\\': c = '\\'; goto backslash;
11303 case CTLESC: c = 'e'; goto backslash;
11304 case CTLVAR: c = 'v'; goto backslash;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011305 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
Eric Andersenc470f442003-07-28 09:56:35 +000011306 case CTLBACKQ: c = 'q'; goto backslash;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011307 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
11308 backslash:
11309 putc('\\', tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011310 putc(c, tracefile);
11311 break;
11312 default:
11313 if (*p >= ' ' && *p <= '~')
11314 putc(*p, tracefile);
11315 else {
11316 putc('\\', tracefile);
11317 putc(*p >> 6 & 03, tracefile);
11318 putc(*p >> 3 & 07, tracefile);
11319 putc(*p & 07, tracefile);
11320 }
11321 break;
11322 }
11323 }
11324 putc('"', tracefile);
11325}
11326
11327
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011328static void
Eric Andersenc470f442003-07-28 09:56:35 +000011329trargs(char **ap)
Eric Andersencb57d552001-06-28 07:25:16 +000011330{
Eric Andersenc470f442003-07-28 09:56:35 +000011331 if (debug != 1)
Eric Andersencb57d552001-06-28 07:25:16 +000011332 return;
11333 while (*ap) {
11334 trstring(*ap++);
11335 if (*ap)
11336 putc(' ', tracefile);
11337 else
11338 putc('\n', tracefile);
11339 }
Eric Andersencb57d552001-06-28 07:25:16 +000011340}
11341
11342
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011343static void
Eric Andersenc470f442003-07-28 09:56:35 +000011344opentrace(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011345{
Eric Andersencb57d552001-06-28 07:25:16 +000011346 char s[100];
11347#ifdef O_APPEND
11348 int flags;
11349#endif
11350
Eric Andersenc470f442003-07-28 09:56:35 +000011351 if (debug != 1) {
11352 if (tracefile)
11353 fflush(tracefile);
11354 /* leave open because libedit might be using it */
Eric Andersencb57d552001-06-28 07:25:16 +000011355 return;
Eric Andersencb57d552001-06-28 07:25:16 +000011356 }
Eric Andersenc470f442003-07-28 09:56:35 +000011357 scopy("./trace", s);
11358 if (tracefile) {
11359 if (!freopen(s, "a", tracefile)) {
11360 fprintf(stderr, "Can't re-open %s\n", s);
11361 debug = 0;
11362 return;
11363 }
11364 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011365 tracefile = fopen(s, "a");
11366 if (tracefile == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011367 fprintf(stderr, "Can't open %s\n", s);
11368 debug = 0;
11369 return;
11370 }
11371 }
Eric Andersencb57d552001-06-28 07:25:16 +000011372#ifdef O_APPEND
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011373 flags = fcntl(fileno(tracefile), F_GETFL, 0);
11374 if (flags >= 0)
Eric Andersencb57d552001-06-28 07:25:16 +000011375 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11376#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011377 setlinebuf(tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011378 fputs("\nTracing started.\n", tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011379}
Eric Andersenc470f442003-07-28 09:56:35 +000011380#endif /* DEBUG */
11381
11382
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011383/* trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011384
11385/*
11386 * Sigmode records the current value of the signal handlers for the various
11387 * modes. A value of zero means that the current handler is not known.
11388 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
11389 */
11390
11391#define S_DFL 1 /* default signal handling (SIG_DFL) */
11392#define S_CATCH 2 /* signal is caught */
11393#define S_IGN 3 /* signal is ignored (SIG_IGN) */
11394#define S_HARD_IGN 4 /* signal is ignored permenantly */
11395#define S_RESET 5 /* temporary - to reset a hard ignored sig */
11396
Eric Andersencb57d552001-06-28 07:25:16 +000011397
Eric Andersencb57d552001-06-28 07:25:16 +000011398/*
Eric Andersencb57d552001-06-28 07:25:16 +000011399 * The trap builtin.
11400 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011401static int
Eric Andersenc470f442003-07-28 09:56:35 +000011402trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011403{
11404 char *action;
11405 char **ap;
11406 int signo;
11407
Eric Andersenc470f442003-07-28 09:56:35 +000011408 nextopt(nullstr);
11409 ap = argptr;
11410 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011411 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011412 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011413 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011414
Rob Landleyc9c1a412006-07-12 19:17:55 +000011415 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011416 out1fmt("trap -- %s %s\n",
11417 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011418 }
11419 }
11420 return 0;
11421 }
Eric Andersenc470f442003-07-28 09:56:35 +000011422 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011423 action = NULL;
11424 else
11425 action = *ap++;
11426 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011427 signo = get_signum(*ap);
11428 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011429 ash_msg_and_raise_error("%s: bad trap", *ap);
11430 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011431 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011432 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011433 action = NULL;
11434 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011435 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011436 }
Eric Andersenc470f442003-07-28 09:56:35 +000011437 if (trap[signo])
Denis Vlasenkob012b102007-02-19 22:43:01 +000011438 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011439 trap[signo] = action;
11440 if (signo != 0)
11441 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011442 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011443 ap++;
11444 }
11445 return 0;
11446}
11447
11448
Eric Andersenc470f442003-07-28 09:56:35 +000011449/*
11450 * Clear traps on a fork.
11451 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011452static void
Eric Andersenc470f442003-07-28 09:56:35 +000011453clear_traps(void)
11454{
11455 char **tp;
11456
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011457 for (tp = trap; tp < &trap[NSIG]; tp++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011458 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011459 INT_OFF;
11460 free(*tp);
Eric Andersenc470f442003-07-28 09:56:35 +000011461 *tp = NULL;
11462 if (tp != &trap[0])
11463 setsignal(tp - trap);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011464 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011465 }
11466 }
11467}
11468
11469
Eric Andersencb57d552001-06-28 07:25:16 +000011470/*
11471 * Set the signal handler for the specified signal. The routine figures
11472 * out what it should be set to.
11473 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011474static void
Eric Andersenc470f442003-07-28 09:56:35 +000011475setsignal(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011476{
11477 int action;
Eric Andersenc470f442003-07-28 09:56:35 +000011478 char *t, tsig;
Eric Andersencb57d552001-06-28 07:25:16 +000011479 struct sigaction act;
11480
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011481 t = trap[signo];
11482 if (t == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011483 action = S_DFL;
11484 else if (*t != '\0')
11485 action = S_CATCH;
11486 else
11487 action = S_IGN;
11488 if (rootshell && action == S_DFL) {
11489 switch (signo) {
11490 case SIGINT:
11491 if (iflag || minusc || sflag == 0)
11492 action = S_CATCH;
11493 break;
11494 case SIGQUIT:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011495#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011496 if (debug)
11497 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011498#endif
11499 /* FALLTHROUGH */
11500 case SIGTERM:
11501 if (iflag)
11502 action = S_IGN;
11503 break;
Eric Andersenc470f442003-07-28 09:56:35 +000011504#if JOBS
Eric Andersencb57d552001-06-28 07:25:16 +000011505 case SIGTSTP:
11506 case SIGTTOU:
11507 if (mflag)
11508 action = S_IGN;
11509 break;
11510#endif
11511 }
11512 }
11513
11514 t = &sigmode[signo - 1];
Eric Andersenc470f442003-07-28 09:56:35 +000011515 tsig = *t;
11516 if (tsig == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011517 /*
11518 * current setting unknown
11519 */
11520 if (sigaction(signo, 0, &act) == -1) {
11521 /*
11522 * Pretend it worked; maybe we should give a warning
11523 * here, but other shells don't. We don't alter
11524 * sigmode, so that we retry every time.
11525 */
11526 return;
11527 }
11528 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011529 if (mflag
11530 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011531 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011532 tsig = S_IGN; /* don't hard ignore these */
Eric Andersencb57d552001-06-28 07:25:16 +000011533 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011534 tsig = S_HARD_IGN;
Eric Andersencb57d552001-06-28 07:25:16 +000011535 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011536 tsig = S_RESET; /* force to be set */
Eric Andersencb57d552001-06-28 07:25:16 +000011537 }
11538 }
Eric Andersenc470f442003-07-28 09:56:35 +000011539 if (tsig == S_HARD_IGN || tsig == action)
Eric Andersencb57d552001-06-28 07:25:16 +000011540 return;
Eric Andersenc470f442003-07-28 09:56:35 +000011541 switch (action) {
11542 case S_CATCH:
11543 act.sa_handler = onsig;
11544 break;
11545 case S_IGN:
11546 act.sa_handler = SIG_IGN;
11547 break;
11548 default:
11549 act.sa_handler = SIG_DFL;
11550 }
Eric Andersencb57d552001-06-28 07:25:16 +000011551 *t = action;
11552 act.sa_flags = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011553 sigfillset(&act.sa_mask);
Eric Andersencb57d552001-06-28 07:25:16 +000011554 sigaction(signo, &act, 0);
11555}
11556
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011557
Eric Andersencb57d552001-06-28 07:25:16 +000011558/*
11559 * Ignore a signal.
11560 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011561static void
Eric Andersenc470f442003-07-28 09:56:35 +000011562ignoresig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011563{
11564 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11565 signal(signo, SIG_IGN);
11566 }
11567 sigmode[signo - 1] = S_HARD_IGN;
11568}
11569
11570
Eric Andersencb57d552001-06-28 07:25:16 +000011571/*
11572 * Signal handler.
11573 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011574static void
Eric Andersenc470f442003-07-28 09:56:35 +000011575onsig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011576{
Eric Andersencb57d552001-06-28 07:25:16 +000011577 gotsig[signo - 1] = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011578 pendingsigs = signo;
11579
11580 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
11581 if (!suppressint)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011582 raise_interrupt();
Eric Andersenc470f442003-07-28 09:56:35 +000011583 intpending = 1;
11584 }
Eric Andersencb57d552001-06-28 07:25:16 +000011585}
11586
11587
Eric Andersencb57d552001-06-28 07:25:16 +000011588/*
11589 * Called to execute a trap. Perhaps we should avoid entering new trap
11590 * handlers while we are executing a trap handler.
11591 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011592static int
Eric Andersenc470f442003-07-28 09:56:35 +000011593dotrap(void)
Eric Andersen2870d962001-07-02 17:27:21 +000011594{
Eric Andersenc470f442003-07-28 09:56:35 +000011595 char *p;
11596 char *q;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011597 int i;
Eric Andersencb57d552001-06-28 07:25:16 +000011598 int savestatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011599 int skip = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011600
Eric Andersenc470f442003-07-28 09:56:35 +000011601 savestatus = exitstatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011602 pendingsigs = 0;
11603 xbarrier();
11604
11605 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
11606 if (!*q)
11607 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011608 *q = '\0';
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011609
11610 p = trap[i + 1];
Eric Andersenc470f442003-07-28 09:56:35 +000011611 if (!p)
11612 continue;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011613 skip = evalstring(p, SKIPEVAL);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011614 exitstatus = savestatus;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011615 if (skip)
11616 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011617 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011618
11619 return skip;
Eric Andersencb57d552001-06-28 07:25:16 +000011620}
11621
Eric Andersenc470f442003-07-28 09:56:35 +000011622/*
11623 * Controls whether the shell is interactive or not.
11624 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011625static void
Eric Andersenc470f442003-07-28 09:56:35 +000011626setinteractive(int on)
11627{
11628 static int is_interactive;
11629
11630 if (++on == is_interactive)
11631 return;
11632 is_interactive = on;
11633 setsignal(SIGINT);
11634 setsignal(SIGQUIT);
11635 setsignal(SIGTERM);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011636#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011637 if (is_interactive > 1) {
11638 /* Looks like they want an interactive shell */
11639 static int do_banner;
Eric Andersenc470f442003-07-28 09:56:35 +000011640
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011641 if (!do_banner) {
11642 out1fmt(
11643 "\n\n"
11644 "%s Built-in shell (ash)\n"
11645 "Enter 'help' for a list of built-in commands."
11646 "\n\n",
11647 BB_BANNER);
11648 do_banner++;
Eric Andersenc470f442003-07-28 09:56:35 +000011649 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011650 }
Eric Andersenc470f442003-07-28 09:56:35 +000011651#endif
11652}
11653
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011654#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Eric Andersenc470f442003-07-28 09:56:35 +000011655/*** List the available builtins ***/
11656
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011657static int
11658helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011659{
11660 int col, i;
11661
11662 out1fmt("\nBuilt-in commands:\n-------------------\n");
11663 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11664 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
11665 builtincmd[i].name + 1);
11666 if (col > 60) {
11667 out1fmt("\n");
11668 col = 0;
11669 }
11670 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011671#if ENABLE_FEATURE_SH_STANDALONE_SHELL
Denis Vlasenko0ee39992006-12-24 15:23:28 +000011672 for (i = 0; i < NUM_APPLETS; i++) {
11673 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11674 if (col > 60) {
11675 out1fmt("\n");
11676 col = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011677 }
11678 }
11679#endif
11680 out1fmt("\n\n");
11681 return EXIT_SUCCESS;
11682}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011683#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011684
Eric Andersencb57d552001-06-28 07:25:16 +000011685/*
11686 * Called to exit the shell.
11687 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011688static void
Eric Andersenc470f442003-07-28 09:56:35 +000011689exitshell(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011690{
Eric Andersenc470f442003-07-28 09:56:35 +000011691 struct jmploc loc;
Eric Andersencb57d552001-06-28 07:25:16 +000011692 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011693 int status;
Eric Andersencb57d552001-06-28 07:25:16 +000011694
Eric Andersenc470f442003-07-28 09:56:35 +000011695 status = exitstatus;
11696 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011697 if (setjmp(loc.loc)) {
11698 if (exception == EXEXIT)
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011699/* dash bug: it just does _exit(exitstatus) here
11700 * but we have to do setjobctl(0) first!
11701 * (bug is still not fixed in dash-0.5.3 - if you run dash
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011702 * under Midnight Commander, on exit from dash MC is backgrounded) */
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011703 status = exitstatus;
Eric Andersenc470f442003-07-28 09:56:35 +000011704 goto out;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011705 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011706 exception_handler = &loc;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011707 p = trap[0];
11708 if (p) {
Eric Andersencb57d552001-06-28 07:25:16 +000011709 trap[0] = NULL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011710 evalstring(p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011711 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011712 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011713 out:
Denis Vlasenko7f0d7ae2007-01-18 01:12:57 +000011714 setjobctl(0);
Glenn L McGrathfdbbb042002-12-09 11:10:40 +000011715 _exit(status);
Eric Andersencb57d552001-06-28 07:25:16 +000011716 /* NOTREACHED */
11717}
11718
Eric Andersencb57d552001-06-28 07:25:16 +000011719
Eric Andersencb57d552001-06-28 07:25:16 +000011720/*
11721 * The export and readonly commands.
11722 */
Eric Andersenc470f442003-07-28 09:56:35 +000011723static int
11724exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011725{
11726 struct var *vp;
11727 char *name;
11728 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011729 char **aptr;
11730 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011731
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011732 if (nextopt("p") != 'p') {
11733 aptr = argptr;
11734 name = *aptr;
11735 if (name) {
11736 do {
11737 p = strchr(name, '=');
11738 if (p != NULL) {
11739 p++;
11740 } else {
11741 vp = *findvar(hashvar(name), name);
11742 if (vp) {
11743 vp->flags |= flag;
11744 continue;
11745 }
Eric Andersencb57d552001-06-28 07:25:16 +000011746 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011747 setvar(name, p, flag);
11748 } while ((name = *++aptr) != NULL);
11749 return 0;
11750 }
Eric Andersencb57d552001-06-28 07:25:16 +000011751 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011752 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011753 return 0;
11754}
11755
Eric Andersen34506362001-08-02 05:02:46 +000011756
Eric Andersencb57d552001-06-28 07:25:16 +000011757/*
Eric Andersencb57d552001-06-28 07:25:16 +000011758 * Make a variable a local variable. When a variable is made local, it's
11759 * value and flags are saved in a localvar structure. The saved values
11760 * will be restored when the shell function returns. We handle the name
11761 * "-" as a special case.
11762 */
Rob Landley88621d72006-08-29 19:41:06 +000011763static void mklocal(char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011764{
Eric Andersencb57d552001-06-28 07:25:16 +000011765 struct localvar *lvp;
11766 struct var **vpp;
11767 struct var *vp;
11768
Denis Vlasenkob012b102007-02-19 22:43:01 +000011769 INT_OFF;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011770 lvp = ckmalloc(sizeof(struct localvar));
Denis Vlasenko9f739442006-12-16 23:49:13 +000011771 if (LONE_DASH(name)) {
Eric Andersencb57d552001-06-28 07:25:16 +000011772 char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011773 p = ckmalloc(sizeof(optlist));
11774 lvp->text = memcpy(p, optlist, sizeof(optlist));
Eric Andersencb57d552001-06-28 07:25:16 +000011775 vp = NULL;
11776 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011777 char *eq;
11778
Eric Andersencb57d552001-06-28 07:25:16 +000011779 vpp = hashvar(name);
11780 vp = *findvar(vpp, name);
Eric Andersenc470f442003-07-28 09:56:35 +000011781 eq = strchr(name, '=');
Eric Andersencb57d552001-06-28 07:25:16 +000011782 if (vp == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011783 if (eq)
11784 setvareq(name, VSTRFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +000011785 else
11786 setvar(name, NULL, VSTRFIXED);
Eric Andersenc470f442003-07-28 09:56:35 +000011787 vp = *vpp; /* the new variable */
Eric Andersencb57d552001-06-28 07:25:16 +000011788 lvp->flags = VUNSET;
11789 } else {
11790 lvp->text = vp->text;
11791 lvp->flags = vp->flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011792 vp->flags |= VSTRFIXED|VTEXTFIXED;
11793 if (eq)
11794 setvareq(name, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011795 }
11796 }
11797 lvp->vp = vp;
11798 lvp->next = localvars;
11799 localvars = lvp;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011800 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011801}
11802
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011803
Eric Andersenc470f442003-07-28 09:56:35 +000011804/*
11805 * The "local" command.
11806 */
Eric Andersenc470f442003-07-28 09:56:35 +000011807static int
11808localcmd(int argc, char **argv)
11809{
11810 char *name;
11811
11812 argv = argptr;
11813 while ((name = *argv++) != NULL) {
11814 mklocal(name);
11815 }
11816 return 0;
11817}
11818
11819
Eric Andersencb57d552001-06-28 07:25:16 +000011820/*
Eric Andersencb57d552001-06-28 07:25:16 +000011821 * The unset builtin command. We unset the function before we unset the
11822 * variable to allow a function to be unset when there is a readonly variable
11823 * with the same name.
11824 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011825static int
Eric Andersenc470f442003-07-28 09:56:35 +000011826unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011827{
11828 char **ap;
11829 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011830 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011831 int ret = 0;
11832
11833 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011834 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011835 }
Eric Andersencb57d552001-06-28 07:25:16 +000011836
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011837 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011838 if (flag != 'f') {
11839 i = unsetvar(*ap);
11840 ret |= i;
11841 if (!(i & 2))
11842 continue;
11843 }
11844 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011845 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011846 }
Eric Andersenc470f442003-07-28 09:56:35 +000011847 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011848}
11849
11850
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011851/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011852
Eric Andersenc470f442003-07-28 09:56:35 +000011853#include <sys/times.h>
11854
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011855static const unsigned char timescmd_str[] = {
11856 ' ', offsetof(struct tms, tms_utime),
11857 '\n', offsetof(struct tms, tms_stime),
11858 ' ', offsetof(struct tms, tms_cutime),
11859 '\n', offsetof(struct tms, tms_cstime),
11860 0
11861};
Eric Andersencb57d552001-06-28 07:25:16 +000011862
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011863static int timescmd(int ac, char **av)
11864{
11865 long int clk_tck, s, t;
11866 const unsigned char *p;
11867 struct tms buf;
11868
11869 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011870 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011871
11872 p = timescmd_str;
11873 do {
11874 t = *(clock_t *)(((char *) &buf) + p[1]);
11875 s = t / clk_tck;
11876 out1fmt("%ldm%ld.%.3lds%c",
11877 s/60, s%60,
11878 ((t - s * clk_tck) * 1000) / clk_tck,
11879 p[0]);
11880 } while (*(p += 2));
11881
Eric Andersencb57d552001-06-28 07:25:16 +000011882 return 0;
11883}
11884
Denis Vlasenko131ae172007-02-18 13:00:19 +000011885#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011886static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011887dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011888{
Eric Andersened9ecf72004-06-22 08:29:45 +000011889 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011890 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011891
Denis Vlasenkob012b102007-02-19 22:43:01 +000011892 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011893 result = arith(s, &errcode);
11894 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011895 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011896 ash_msg_and_raise_error("exponent less than 0");
Eric Andersen90898442003-08-06 11:20:52 +000011897 else if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011898 ash_msg_and_raise_error("divide by zero");
Eric Andersen90898442003-08-06 11:20:52 +000011899 else if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011900 ash_msg_and_raise_error("expression recursion loop detected");
Eric Andersenc470f442003-07-28 09:56:35 +000011901 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011902 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011903 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011904 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011905
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011906 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011907}
Eric Andersenc470f442003-07-28 09:56:35 +000011908
11909
11910/*
Eric Andersen90898442003-08-06 11:20:52 +000011911 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11912 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11913 *
11914 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011915 */
11916static int
Eric Andersen90898442003-08-06 11:20:52 +000011917letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011918{
Eric Andersenc470f442003-07-28 09:56:35 +000011919 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011920 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011921
Eric Andersen90898442003-08-06 11:20:52 +000011922 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011923 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011924 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011925 for (ap = argv + 1; *ap; ap++) {
11926 i = dash_arith(*ap);
11927 }
Eric Andersenc470f442003-07-28 09:56:35 +000011928
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011929 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011930}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011931#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011932
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011933/* miscbltin.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011934
11935/*
Eric Andersenaff114c2004-04-14 17:51:38 +000011936 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011937 */
11938
11939#undef rflag
11940
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011941#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011942typedef enum __rlimit_resource rlim_t;
11943#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011944
11945
Eric Andersenc470f442003-07-28 09:56:35 +000011946/*
11947 * The read builtin. The -e option causes backslashes to escape the
11948 * following character.
11949 *
11950 * This uses unbuffered input, which may be avoidable in some cases.
11951 */
Eric Andersenc470f442003-07-28 09:56:35 +000011952static int
11953readcmd(int argc, char **argv)
11954{
11955 char **ap;
11956 int backslash;
11957 char c;
11958 int rflag;
11959 char *prompt;
11960 const char *ifs;
11961 char *p;
11962 int startword;
11963 int status;
11964 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011965#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011966 int nch_flag = 0;
11967 int nchars = 0;
11968 int silent = 0;
11969 struct termios tty, old_tty;
11970#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011971#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011972 fd_set set;
11973 struct timeval ts;
11974
11975 ts.tv_sec = ts.tv_usec = 0;
11976#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011977
11978 rflag = 0;
11979 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011980#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011981 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011982#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011983 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011984#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011985 while ((i = nextopt("p:rt:")) != '\0')
11986#else
11987 while ((i = nextopt("p:r")) != '\0')
11988#endif
11989 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011990 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011991 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011992 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011993 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011994#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011995 case 'n':
11996 nchars = strtol(optionarg, &p, 10);
11997 if (*p)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011998 ash_msg_and_raise_error("invalid count");
Paul Fox02eb9342005-09-07 16:56:02 +000011999 nch_flag = (nchars > 0);
12000 break;
12001 case 's':
12002 silent = 1;
12003 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012004#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012005#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012006 case 't':
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000012007 ts.tv_sec = strtol(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012008 ts.tv_usec = 0;
12009 if (*p == '.') {
12010 char *p2;
12011 if (*++p) {
12012 int scale;
"Vladimir N. Oleynik"11d7c522005-09-26 13:24:45 +000012013 ts.tv_usec = strtol(p, &p2, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012014 if (*p2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012015 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012016 scale = p2 - p;
12017 /* normalize to usec */
12018 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012019 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012020 while (scale++ < 6)
12021 ts.tv_usec *= 10;
12022 }
12023 } else if (*p) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012024 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012025 }
12026 if ( ! ts.tv_sec && ! ts.tv_usec)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012027 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012028 break;
12029#endif
12030 case 'r':
12031 rflag = 1;
12032 break;
12033 default:
12034 break;
12035 }
Eric Andersenc470f442003-07-28 09:56:35 +000012036 }
12037 if (prompt && isatty(0)) {
12038 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012039 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012040 ap = argptr;
12041 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012042 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012043 ifs = bltinlookup("IFS");
12044 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012045 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012046#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012047 if (nch_flag || silent) {
12048 tcgetattr(0, &tty);
12049 old_tty = tty;
12050 if (nch_flag) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012051 tty.c_lflag &= ~ICANON;
12052 tty.c_cc[VMIN] = nchars;
Paul Fox02eb9342005-09-07 16:56:02 +000012053 }
12054 if (silent) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012055 tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
Paul Fox02eb9342005-09-07 16:56:02 +000012056
12057 }
12058 tcsetattr(0, TCSANOW, &tty);
12059 }
12060#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012061#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012062 if (ts.tv_sec || ts.tv_usec) {
12063 FD_ZERO (&set);
12064 FD_SET (0, &set);
12065
Denis Vlasenkof4dff772006-12-24 07:14:17 +000012066 i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
Paul Fox02eb9342005-09-07 16:56:02 +000012067 if (!i) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000012068#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012069 if (nch_flag)
12070 tcsetattr(0, TCSANOW, &old_tty);
12071#endif
12072 return 1;
12073 }
12074 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000012075#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012076 status = 0;
12077 startword = 1;
12078 backslash = 0;
12079 STARTSTACKSTR(p);
Denis Vlasenko131ae172007-02-18 13:00:19 +000012080#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012081 while (!nch_flag || nchars--)
Ned Ludd2123b7c2005-02-09 21:07:23 +000012082#else
12083 for (;;)
12084#endif
12085 {
Eric Andersenc470f442003-07-28 09:56:35 +000012086 if (read(0, &c, 1) != 1) {
12087 status = 1;
12088 break;
12089 }
12090 if (c == '\0')
12091 continue;
12092 if (backslash) {
12093 backslash = 0;
12094 if (c != '\n')
12095 goto put;
12096 continue;
12097 }
12098 if (!rflag && c == '\\') {
12099 backslash++;
12100 continue;
12101 }
12102 if (c == '\n')
12103 break;
12104 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12105 continue;
12106 }
12107 startword = 0;
12108 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12109 STACKSTRNUL(p);
12110 setvar(*ap, stackblock(), 0);
12111 ap++;
12112 startword = 1;
12113 STARTSTACKSTR(p);
12114 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012115 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012116 STPUTC(c, p);
12117 }
12118 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000012119#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012120 if (nch_flag || silent)
12121 tcsetattr(0, TCSANOW, &old_tty);
12122#endif
12123
Eric Andersenc470f442003-07-28 09:56:35 +000012124 STACKSTRNUL(p);
12125 /* Remove trailing blanks */
12126 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12127 *p = '\0';
12128 setvar(*ap, stackblock(), 0);
12129 while (*++ap != NULL)
12130 setvar(*ap, nullstr, 0);
12131 return status;
12132}
12133
12134
12135static int umaskcmd(int argc, char **argv)
12136{
12137 static const char permuser[3] = "ugo";
12138 static const char permmode[3] = "rwx";
12139 static const short int permmask[] = {
12140 S_IRUSR, S_IWUSR, S_IXUSR,
12141 S_IRGRP, S_IWGRP, S_IXGRP,
12142 S_IROTH, S_IWOTH, S_IXOTH
12143 };
12144
12145 char *ap;
12146 mode_t mask;
12147 int i;
12148 int symbolic_mode = 0;
12149
12150 while (nextopt("S") != '\0') {
12151 symbolic_mode = 1;
12152 }
12153
Denis Vlasenkob012b102007-02-19 22:43:01 +000012154 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012155 mask = umask(0);
12156 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012157 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012158
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012159 ap = *argptr;
12160 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012161 if (symbolic_mode) {
12162 char buf[18];
12163 char *p = buf;
12164
12165 for (i = 0; i < 3; i++) {
12166 int j;
12167
12168 *p++ = permuser[i];
12169 *p++ = '=';
12170 for (j = 0; j < 3; j++) {
12171 if ((mask & permmask[3 * i + j]) == 0) {
12172 *p++ = permmode[j];
12173 }
12174 }
12175 *p++ = ',';
12176 }
12177 *--p = 0;
12178 puts(buf);
12179 } else {
12180 out1fmt("%.4o\n", mask);
12181 }
12182 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012183 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012184 mask = 0;
12185 do {
12186 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012187 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012188 mask = (mask << 3) + (*ap - '0');
12189 } while (*++ap != '\0');
12190 umask(mask);
12191 } else {
12192 mask = ~mask & 0777;
12193 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012194 ash_msg_and_raise_error("Illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012195 }
12196 umask(~mask & 0777);
12197 }
12198 }
12199 return 0;
12200}
12201
12202/*
12203 * ulimit builtin
12204 *
12205 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12206 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12207 * ash by J.T. Conklin.
12208 *
12209 * Public domain.
12210 */
12211
12212struct limits {
12213 const char *name;
12214 int cmd;
12215 int factor; /* multiply by to get rlim_{cur,max} values */
12216 char option;
12217};
12218
12219static const struct limits limits[] = {
12220#ifdef RLIMIT_CPU
12221 { "time(seconds)", RLIMIT_CPU, 1, 't' },
12222#endif
12223#ifdef RLIMIT_FSIZE
12224 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
12225#endif
12226#ifdef RLIMIT_DATA
12227 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
12228#endif
12229#ifdef RLIMIT_STACK
12230 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
12231#endif
12232#ifdef RLIMIT_CORE
12233 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
12234#endif
12235#ifdef RLIMIT_RSS
12236 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
12237#endif
12238#ifdef RLIMIT_MEMLOCK
12239 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
12240#endif
12241#ifdef RLIMIT_NPROC
Glenn L McGrath76620622004-01-13 10:19:37 +000012242 { "process", RLIMIT_NPROC, 1, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012243#endif
12244#ifdef RLIMIT_NOFILE
Glenn L McGrath76620622004-01-13 10:19:37 +000012245 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012246#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012247#ifdef RLIMIT_AS
12248 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012249#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012250#ifdef RLIMIT_LOCKS
12251 { "locks", RLIMIT_LOCKS, 1, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012252#endif
12253 { (char *) 0, 0, 0, '\0' }
12254};
12255
Glenn L McGrath76620622004-01-13 10:19:37 +000012256enum limtype { SOFT = 0x1, HARD = 0x2 };
12257
12258static void printlim(enum limtype how, const struct rlimit *limit,
12259 const struct limits *l)
12260{
12261 rlim_t val;
12262
12263 val = limit->rlim_max;
12264 if (how & SOFT)
12265 val = limit->rlim_cur;
12266
12267 if (val == RLIM_INFINITY)
12268 out1fmt("unlimited\n");
12269 else {
12270 val /= l->factor;
12271 out1fmt("%lld\n", (long long) val);
12272 }
12273}
12274
Eric Andersenc470f442003-07-28 09:56:35 +000012275int
12276ulimitcmd(int argc, char **argv)
12277{
12278 int c;
12279 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012280 enum limtype how = SOFT | HARD;
Eric Andersenc470f442003-07-28 09:56:35 +000012281 const struct limits *l;
12282 int set, all = 0;
12283 int optc, what;
12284 struct rlimit limit;
12285
12286 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012287 while ((optc = nextopt("HSa"
12288#ifdef RLIMIT_CPU
12289 "t"
12290#endif
12291#ifdef RLIMIT_FSIZE
12292 "f"
12293#endif
12294#ifdef RLIMIT_DATA
12295 "d"
12296#endif
12297#ifdef RLIMIT_STACK
12298 "s"
12299#endif
12300#ifdef RLIMIT_CORE
12301 "c"
12302#endif
12303#ifdef RLIMIT_RSS
12304 "m"
12305#endif
12306#ifdef RLIMIT_MEMLOCK
12307 "l"
12308#endif
12309#ifdef RLIMIT_NPROC
12310 "p"
12311#endif
12312#ifdef RLIMIT_NOFILE
12313 "n"
12314#endif
12315#ifdef RLIMIT_AS
12316 "v"
12317#endif
12318#ifdef RLIMIT_LOCKS
12319 "w"
12320#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012321 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012322 switch (optc) {
12323 case 'H':
12324 how = HARD;
12325 break;
12326 case 'S':
12327 how = SOFT;
12328 break;
12329 case 'a':
12330 all = 1;
12331 break;
12332 default:
12333 what = optc;
12334 }
12335
Glenn L McGrath76620622004-01-13 10:19:37 +000012336 for (l = limits; l->option != what; l++)
Eric Andersenc470f442003-07-28 09:56:35 +000012337 ;
Eric Andersenc470f442003-07-28 09:56:35 +000012338
12339 set = *argptr ? 1 : 0;
12340 if (set) {
12341 char *p = *argptr;
12342
12343 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012344 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012345 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012346 val = RLIM_INFINITY;
12347 else {
12348 val = (rlim_t) 0;
12349
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012350 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012351 val = (val * 10) + (long)(c - '0');
12352 if (val < (rlim_t) 0)
12353 break;
12354 }
12355 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012356 ash_msg_and_raise_error("bad number");
Eric Andersenc470f442003-07-28 09:56:35 +000012357 val *= l->factor;
12358 }
12359 }
12360 if (all) {
12361 for (l = limits; l->name; l++) {
12362 getrlimit(l->cmd, &limit);
Eric Andersenc470f442003-07-28 09:56:35 +000012363 out1fmt("%-20s ", l->name);
Glenn L McGrath76620622004-01-13 10:19:37 +000012364 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012365 }
12366 return 0;
12367 }
12368
12369 getrlimit(l->cmd, &limit);
12370 if (set) {
12371 if (how & HARD)
12372 limit.rlim_max = val;
12373 if (how & SOFT)
12374 limit.rlim_cur = val;
12375 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012376 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012377 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012378 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012379 }
12380 return 0;
12381}
12382
Eric Andersen90898442003-08-06 11:20:52 +000012383
Denis Vlasenko131ae172007-02-18 13:00:19 +000012384#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012385
12386/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12387
12388 Permission is hereby granted, free of charge, to any person obtaining
12389 a copy of this software and associated documentation files (the
12390 "Software"), to deal in the Software without restriction, including
12391 without limitation the rights to use, copy, modify, merge, publish,
12392 distribute, sublicense, and/or sell copies of the Software, and to
12393 permit persons to whom the Software is furnished to do so, subject to
12394 the following conditions:
12395
12396 The above copyright notice and this permission notice shall be
12397 included in all copies or substantial portions of the Software.
12398
12399 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12400 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12401 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12402 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12403 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12404 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12405 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12406*/
12407
12408/* This is my infix parser/evaluator. It is optimized for size, intended
12409 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012410 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012411 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012412 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012413 * be that which POSIX specifies for shells. */
12414
12415/* The code uses a simple two-stack algorithm. See
12416 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012417 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012418 * this is based (this code differs in that it applies operators immediately
12419 * to the stack instead of adding them to a queue to end up with an
12420 * expression). */
12421
12422/* To use the routine, call it with an expression string and error return
12423 * pointer */
12424
12425/*
12426 * Aug 24, 2001 Manuel Novoa III
12427 *
12428 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12429 *
12430 * 1) In arith_apply():
12431 * a) Cached values of *numptr and &(numptr[-1]).
12432 * b) Removed redundant test for zero denominator.
12433 *
12434 * 2) In arith():
12435 * a) Eliminated redundant code for processing operator tokens by moving
12436 * to a table-based implementation. Also folded handling of parens
12437 * into the table.
12438 * b) Combined all 3 loops which called arith_apply to reduce generated
12439 * code size at the cost of speed.
12440 *
12441 * 3) The following expressions were treated as valid by the original code:
12442 * 1() , 0! , 1 ( *3 ) .
12443 * These bugs have been fixed by internally enclosing the expression in
12444 * parens and then checking that all binary ops and right parens are
12445 * preceded by a valid expression (NUM_TOKEN).
12446 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012447 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012448 * ctype's isspace() if it is used by another busybox applet or if additional
12449 * whitespace chars should be considered. Look below the "#include"s for a
12450 * precompiler test.
12451 */
12452
12453/*
12454 * Aug 26, 2001 Manuel Novoa III
12455 *
12456 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12457 *
12458 * Merge in Aaron's comments previously posted to the busybox list,
12459 * modified slightly to take account of my changes to the code.
12460 *
12461 */
12462
12463/*
12464 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12465 *
12466 * - allow access to variable,
12467 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12468 * - realize assign syntax (VAR=expr, +=, *= etc)
12469 * - realize exponentiation (** operator)
12470 * - realize comma separated - expr, expr
12471 * - realise ++expr --expr expr++ expr--
12472 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012473 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012474 * - was restored loses XOR operator
12475 * - remove one goto label, added three ;-)
12476 * - protect $((num num)) as true zero expr (Manuel`s error)
12477 * - always use special isspace(), see comment from bash ;-)
12478 */
12479
12480
12481#define arith_isspace(arithval) \
12482 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12483
12484
12485typedef unsigned char operator;
12486
12487/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012488 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012489 * precedence. The ID portion is so that multiple operators can have the
12490 * same precedence, ensuring that the leftmost one is evaluated first.
12491 * Consider * and /. */
12492
12493#define tok_decl(prec,id) (((id)<<5)|(prec))
12494#define PREC(op) ((op) & 0x1F)
12495
12496#define TOK_LPAREN tok_decl(0,0)
12497
12498#define TOK_COMMA tok_decl(1,0)
12499
12500#define TOK_ASSIGN tok_decl(2,0)
12501#define TOK_AND_ASSIGN tok_decl(2,1)
12502#define TOK_OR_ASSIGN tok_decl(2,2)
12503#define TOK_XOR_ASSIGN tok_decl(2,3)
12504#define TOK_PLUS_ASSIGN tok_decl(2,4)
12505#define TOK_MINUS_ASSIGN tok_decl(2,5)
12506#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12507#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12508
12509#define TOK_MUL_ASSIGN tok_decl(3,0)
12510#define TOK_DIV_ASSIGN tok_decl(3,1)
12511#define TOK_REM_ASSIGN tok_decl(3,2)
12512
12513/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012514#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012515
12516/* conditional is right associativity too */
12517#define TOK_CONDITIONAL tok_decl(4,0)
12518#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12519
12520#define TOK_OR tok_decl(5,0)
12521
12522#define TOK_AND tok_decl(6,0)
12523
12524#define TOK_BOR tok_decl(7,0)
12525
12526#define TOK_BXOR tok_decl(8,0)
12527
12528#define TOK_BAND tok_decl(9,0)
12529
12530#define TOK_EQ tok_decl(10,0)
12531#define TOK_NE tok_decl(10,1)
12532
12533#define TOK_LT tok_decl(11,0)
12534#define TOK_GT tok_decl(11,1)
12535#define TOK_GE tok_decl(11,2)
12536#define TOK_LE tok_decl(11,3)
12537
12538#define TOK_LSHIFT tok_decl(12,0)
12539#define TOK_RSHIFT tok_decl(12,1)
12540
12541#define TOK_ADD tok_decl(13,0)
12542#define TOK_SUB tok_decl(13,1)
12543
12544#define TOK_MUL tok_decl(14,0)
12545#define TOK_DIV tok_decl(14,1)
12546#define TOK_REM tok_decl(14,2)
12547
12548/* exponent is right associativity */
12549#define TOK_EXPONENT tok_decl(15,1)
12550
12551/* For now unary operators. */
12552#define UNARYPREC 16
12553#define TOK_BNOT tok_decl(UNARYPREC,0)
12554#define TOK_NOT tok_decl(UNARYPREC,1)
12555
12556#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12557#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12558
12559#define PREC_PRE (UNARYPREC+2)
12560
12561#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12562#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12563
12564#define PREC_POST (UNARYPREC+3)
12565
12566#define TOK_POST_INC tok_decl(PREC_POST, 0)
12567#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12568
12569#define SPEC_PREC (UNARYPREC+4)
12570
12571#define TOK_NUM tok_decl(SPEC_PREC, 0)
12572#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12573
12574#define NUMPTR (*numstackptr)
12575
Rob Landley88621d72006-08-29 19:41:06 +000012576static int tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012577{
12578 operator prec = PREC(op);
12579
12580 convert_prec_is_assing(prec);
12581 return (prec == PREC(TOK_ASSIGN) ||
12582 prec == PREC_PRE || prec == PREC_POST);
12583}
12584
Rob Landley88621d72006-08-29 19:41:06 +000012585static int is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012586{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012587 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12588 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012589}
12590
12591
12592typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012593 arith_t val;
12594 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012595 char contidional_second_val_initialized;
12596 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012597 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012598} v_n_t;
12599
12600
12601typedef struct CHK_VAR_RECURSIVE_LOOPED {
12602 const char *var;
12603 struct CHK_VAR_RECURSIVE_LOOPED *next;
12604} chk_var_recursive_looped_t;
12605
12606static chk_var_recursive_looped_t *prev_chk_var_recursive;
12607
12608
12609static int arith_lookup_val(v_n_t *t)
12610{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012611 if (t->var) {
12612 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012613
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012614 if (p) {
12615 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012616
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012617 /* recursive try as expression */
12618 chk_var_recursive_looped_t *cur;
12619 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012620
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012621 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12622 if (strcmp(cur->var, t->var) == 0) {
12623 /* expression recursion loop detected */
12624 return -5;
12625 }
12626 }
12627 /* save current lookuped var name */
12628 cur = prev_chk_var_recursive;
12629 cur_save.var = t->var;
12630 cur_save.next = cur;
12631 prev_chk_var_recursive = &cur_save;
12632
12633 t->val = arith (p, &errcode);
12634 /* restore previous ptr after recursiving */
12635 prev_chk_var_recursive = cur;
12636 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012637 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012638 /* allow undefined var as 0 */
12639 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012640 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012641 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012642}
12643
12644/* "applying" a token means performing it on the top elements on the integer
12645 * stack. For a unary operator it will only change the top element, but a
12646 * binary operator will pop two arguments and push a result */
Rob Landley88621d72006-08-29 19:41:06 +000012647static int arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012648{
Eric Andersen90898442003-08-06 11:20:52 +000012649 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012650 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012651 int ret_arith_lookup_val;
12652
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012653 /* There is no operator that can work without arguments */
12654 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012655 numptr_m1 = NUMPTR - 1;
12656
12657 /* check operand is var with noninteger value */
12658 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012659 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012660 return ret_arith_lookup_val;
12661
12662 rez = numptr_m1->val;
12663 if (op == TOK_UMINUS)
12664 rez *= -1;
12665 else if (op == TOK_NOT)
12666 rez = !rez;
12667 else if (op == TOK_BNOT)
12668 rez = ~rez;
12669 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12670 rez++;
12671 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12672 rez--;
12673 else if (op != TOK_UPLUS) {
12674 /* Binary operators */
12675
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012676 /* check and binary operators need two arguments */
12677 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012678
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012679 /* ... and they pop one */
12680 --NUMPTR;
12681 numptr_val = rez;
12682 if (op == TOK_CONDITIONAL) {
12683 if (! numptr_m1->contidional_second_val_initialized) {
12684 /* protect $((expr1 ? expr2)) without ": expr" */
12685 goto err;
12686 }
12687 rez = numptr_m1->contidional_second_val;
12688 } else if (numptr_m1->contidional_second_val_initialized) {
12689 /* protect $((expr1 : expr2)) without "expr ? " */
12690 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012691 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012692 numptr_m1 = NUMPTR - 1;
12693 if (op != TOK_ASSIGN) {
12694 /* check operand is var with noninteger value for not '=' */
12695 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12696 if (ret_arith_lookup_val)
12697 return ret_arith_lookup_val;
12698 }
12699 if (op == TOK_CONDITIONAL) {
12700 numptr_m1->contidional_second_val = rez;
12701 }
12702 rez = numptr_m1->val;
12703 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012704 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012705 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012706 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012707 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012708 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012709 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012710 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012711 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012712 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012713 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012714 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012715 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012716 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012717 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012718 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012719 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012720 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012721 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012722 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012723 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012724 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012725 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012726 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012727 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012728 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012729 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012730 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012731 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012732 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012733 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012734 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012735 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012736 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012737 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012738 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012739 /* protect $((expr : expr)) without "expr ? " */
12740 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012741 }
12742 numptr_m1->contidional_second_val_initialized = op;
12743 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012744 }
12745 else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012746 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012747 numptr_val : numptr_m1->contidional_second_val;
12748 }
12749 else if (op == TOK_EXPONENT) {
12750 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012751 return -3; /* exponent less than 0 */
12752 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012753 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012754
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012755 if (numptr_val)
12756 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012757 c *= rez;
12758 rez = c;
12759 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012760 }
12761 else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012762 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012763 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012764 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012765 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012766 rez %= numptr_val;
12767 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012768 if (tok_have_assign(op)) {
Eric Andersen90898442003-08-06 11:20:52 +000012769 char buf[32];
12770
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012771 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012772 /* Hmm, 1=2 ? */
12773 goto err;
12774 }
12775 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012776#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersena68ea1c2006-01-30 22:48:39 +000012777 snprintf(buf, sizeof(buf), "%lld", arith_t_type rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012778#else
Eric Andersena68ea1c2006-01-30 22:48:39 +000012779 snprintf(buf, sizeof(buf), "%ld", arith_t_type rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012780#endif
Eric Andersen90898442003-08-06 11:20:52 +000012781 setvar(numptr_m1->var, buf, 0);
12782 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012783 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012784 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012785 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012786 rez++;
12787 }
12788 numptr_m1->val = rez;
12789 /* protect geting var value, is number now */
12790 numptr_m1->var = NULL;
12791 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012792 err:
12793 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012794}
12795
12796/* longest must first */
12797static const char op_tokens[] = {
12798 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12799 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12800 '<','<', 0, TOK_LSHIFT,
12801 '>','>', 0, TOK_RSHIFT,
12802 '|','|', 0, TOK_OR,
12803 '&','&', 0, TOK_AND,
12804 '!','=', 0, TOK_NE,
12805 '<','=', 0, TOK_LE,
12806 '>','=', 0, TOK_GE,
12807 '=','=', 0, TOK_EQ,
12808 '|','=', 0, TOK_OR_ASSIGN,
12809 '&','=', 0, TOK_AND_ASSIGN,
12810 '*','=', 0, TOK_MUL_ASSIGN,
12811 '/','=', 0, TOK_DIV_ASSIGN,
12812 '%','=', 0, TOK_REM_ASSIGN,
12813 '+','=', 0, TOK_PLUS_ASSIGN,
12814 '-','=', 0, TOK_MINUS_ASSIGN,
12815 '-','-', 0, TOK_POST_DEC,
12816 '^','=', 0, TOK_XOR_ASSIGN,
12817 '+','+', 0, TOK_POST_INC,
12818 '*','*', 0, TOK_EXPONENT,
12819 '!', 0, TOK_NOT,
12820 '<', 0, TOK_LT,
12821 '>', 0, TOK_GT,
12822 '=', 0, TOK_ASSIGN,
12823 '|', 0, TOK_BOR,
12824 '&', 0, TOK_BAND,
12825 '*', 0, TOK_MUL,
12826 '/', 0, TOK_DIV,
12827 '%', 0, TOK_REM,
12828 '+', 0, TOK_ADD,
12829 '-', 0, TOK_SUB,
12830 '^', 0, TOK_BXOR,
12831 /* uniq */
12832 '~', 0, TOK_BNOT,
12833 ',', 0, TOK_COMMA,
12834 '?', 0, TOK_CONDITIONAL,
12835 ':', 0, TOK_CONDITIONAL_SEP,
12836 ')', 0, TOK_RPAREN,
12837 '(', 0, TOK_LPAREN,
12838 0
12839};
12840/* ptr to ")" */
12841#define endexpression &op_tokens[sizeof(op_tokens)-7]
12842
12843
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012844static arith_t arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012845{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012846 char arithval; /* Current character under analysis */
12847 operator lasttok, op;
12848 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012849
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012850 const char *p = endexpression;
12851 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012852
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012853 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012854
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012855 /* Stack of integers */
12856 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12857 * in any given correct or incorrect expression is left as an exercise to
12858 * the reader. */
12859 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12860 *numstackptr = numstack;
12861 /* Stack of operator tokens */
12862 operator *stack = alloca((datasizes) * sizeof(operator)),
12863 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012864
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012865 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12866 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012867
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012868 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012869 arithval = *expr;
12870 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012871 if (p == endexpression) {
12872 /* Null expression. */
12873 return 0;
12874 }
12875
12876 /* This is only reached after all tokens have been extracted from the
12877 * input stream. If there are still tokens on the operator stack, they
12878 * are to be applied in order. At the end, there should be a final
12879 * result on the integer stack */
12880
12881 if (expr != endexpression + 1) {
12882 /* If we haven't done so already, */
12883 /* append a closing right paren */
12884 expr = endexpression;
12885 /* and let the loop process it. */
12886 continue;
12887 }
12888 /* At this point, we're done with the expression. */
12889 if (numstackptr != numstack+1) {
12890 /* ... but if there isn't, it's bad */
12891 err:
12892 return (*perrcode = -1);
12893 }
12894 if (numstack->var) {
12895 /* expression is $((var)) only, lookup now */
12896 errcode = arith_lookup_val(numstack);
12897 }
12898 ret:
12899 *perrcode = errcode;
12900 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012901 }
12902
Eric Andersen90898442003-08-06 11:20:52 +000012903 /* Continue processing the expression. */
12904 if (arith_isspace(arithval)) {
12905 /* Skip whitespace */
12906 goto prologue;
12907 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012908 p = endofname(expr);
12909 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012910 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012911
12912 numstackptr->var = alloca(var_name_size);
12913 safe_strncpy(numstackptr->var, expr, var_name_size);
12914 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012915 num:
Eric Andersen90898442003-08-06 11:20:52 +000012916 numstackptr->contidional_second_val_initialized = 0;
12917 numstackptr++;
12918 lasttok = TOK_NUM;
12919 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012920 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012921 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012922 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012923#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012924 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012925#else
12926 numstackptr->val = strtol(expr, (char **) &expr, 0);
12927#endif
Eric Andersen90898442003-08-06 11:20:52 +000012928 goto num;
12929 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012930 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012931 const char *o;
12932
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012933 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012934 /* strange operator not found */
12935 goto err;
12936 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012937 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012938 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012939 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012940 /* found */
12941 expr = o - 1;
12942 break;
12943 }
12944 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012945 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012946 p++;
12947 /* skip zero delim */
12948 p++;
12949 }
12950 op = p[1];
12951
12952 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012953 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12954 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012955
12956 /* Plus and minus are binary (not unary) _only_ if the last
12957 * token was as number, or a right paren (which pretends to be
12958 * a number, since it evaluates to one). Think about it.
12959 * It makes sense. */
12960 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012961 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012962 case TOK_ADD:
12963 op = TOK_UPLUS;
12964 break;
12965 case TOK_SUB:
12966 op = TOK_UMINUS;
12967 break;
12968 case TOK_POST_INC:
12969 op = TOK_PRE_INC;
12970 break;
12971 case TOK_POST_DEC:
12972 op = TOK_PRE_DEC;
12973 break;
Eric Andersen90898442003-08-06 11:20:52 +000012974 }
12975 }
12976 /* We don't want a unary operator to cause recursive descent on the
12977 * stack, because there can be many in a row and it could cause an
12978 * operator to be evaluated before its argument is pushed onto the
12979 * integer stack. */
12980 /* But for binary operators, "apply" everything on the operator
12981 * stack until we find an operator with a lesser priority than the
12982 * one we have just extracted. */
12983 /* Left paren is given the lowest priority so it will never be
12984 * "applied" in this way.
12985 * if associativity is right and priority eq, applied also skip
12986 */
12987 prec = PREC(op);
12988 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12989 /* not left paren or unary */
12990 if (lasttok != TOK_NUM) {
12991 /* binary op must be preceded by a num */
12992 goto err;
12993 }
12994 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012995 if (op == TOK_RPAREN) {
12996 /* The algorithm employed here is simple: while we don't
12997 * hit an open paren nor the bottom of the stack, pop
12998 * tokens and apply them */
12999 if (stackptr[-1] == TOK_LPAREN) {
13000 --stackptr;
13001 /* Any operator directly after a */
13002 lasttok = TOK_NUM;
13003 /* close paren should consider itself binary */
13004 goto prologue;
13005 }
13006 } else {
13007 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013008
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 convert_prec_is_assing(prec);
13010 convert_prec_is_assing(prev_prec);
13011 if (prev_prec < prec)
13012 break;
13013 /* check right assoc */
13014 if (prev_prec == prec && is_right_associativity(prec))
13015 break;
13016 }
13017 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13018 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013019 }
13020 if (op == TOK_RPAREN) {
13021 goto err;
13022 }
13023 }
13024
13025 /* Push this operator to the stack and remember it. */
13026 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013027 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013028 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013029 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013030}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013031#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013032
13033
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013034/* ============ main() and helpers
13035 *
13036 * Main routine. We initialize things, parse the arguments, execute
13037 * profiles if we're a login shell, and then call cmdloop to execute
13038 * commands. The setjmp call sets up the location to jump to when an
13039 * exception occurs. When an exception occurs the variable "state"
13040 * is used to figure out how far we had gotten.
13041 */
13042
13043static void init(void)
13044{
13045 /* from input.c: */
13046 basepf.nextc = basepf.buf = basebuf;
13047
13048 /* from trap.c: */
13049 signal(SIGCHLD, SIG_DFL);
13050
13051 /* from var.c: */
13052 {
13053 char **envp;
13054 char ppid[32];
13055 const char *p;
13056 struct stat st1, st2;
13057
13058 initvar();
13059 for (envp = environ; envp && *envp; envp++) {
13060 if (strchr(*envp, '=')) {
13061 setvareq(*envp, VEXPORT|VTEXTFIXED);
13062 }
13063 }
13064
13065 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
13066 setvar("PPID", ppid, 0);
13067
13068 p = lookupvar("PWD");
13069 if (p)
13070 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13071 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13072 p = '\0';
13073 setpwd(p, 0);
13074 }
13075}
13076
13077/*
13078 * Process the shell command line arguments.
13079 */
13080static void
13081procargs(int argc, char **argv)
13082{
13083 int i;
13084 const char *xminusc;
13085 char **xargv;
13086
13087 xargv = argv;
13088 arg0 = xargv[0];
13089 if (argc > 0)
13090 xargv++;
13091 for (i = 0; i < NOPTS; i++)
13092 optlist[i] = 2;
13093 argptr = xargv;
13094 options(1);
13095 xargv = argptr;
13096 xminusc = minusc;
13097 if (*xargv == NULL) {
13098 if (xminusc)
13099 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13100 sflag = 1;
13101 }
13102 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13103 iflag = 1;
13104 if (mflag == 2)
13105 mflag = iflag;
13106 for (i = 0; i < NOPTS; i++)
13107 if (optlist[i] == 2)
13108 optlist[i] = 0;
13109#if DEBUG == 2
13110 debug = 1;
13111#endif
13112 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13113 if (xminusc) {
13114 minusc = *xargv++;
13115 if (*xargv)
13116 goto setarg0;
13117 } else if (!sflag) {
13118 setinputfile(*xargv, 0);
13119 setarg0:
13120 arg0 = *xargv++;
13121 commandname = arg0;
13122 }
13123
13124 shellparam.p = xargv;
13125#if ENABLE_ASH_GETOPTS
13126 shellparam.optind = 1;
13127 shellparam.optoff = -1;
13128#endif
13129 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
13130 while (*xargv) {
13131 shellparam.nparam++;
13132 xargv++;
13133 }
13134 optschanged();
13135}
13136
13137/*
13138 * Read /etc/profile or .profile.
13139 */
13140static void
13141read_profile(const char *name)
13142{
13143 int skip;
13144
13145 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13146 return;
13147 skip = cmdloop(0);
13148 popfile();
13149 if (skip)
13150 exitshell();
13151}
13152
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013153/*
13154 * This routine is called when an error or an interrupt occurs in an
13155 * interactive shell and control is returned to the main command loop.
13156 */
13157static void
13158reset(void)
13159{
13160 /* from eval.c: */
13161 evalskip = 0;
13162 loopnest = 0;
13163 /* from input.c: */
13164 parselleft = parsenleft = 0; /* clear input buffer */
13165 popallfiles();
13166 /* from parser.c: */
13167 tokpushback = 0;
13168 checkkwd = 0;
13169 /* from redir.c: */
13170 clearredir(0);
13171}
13172
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013173#if PROFILE
13174static short profile_buf[16384];
13175extern int etext();
13176#endif
13177
13178int ash_main(int argc, char **argv);
13179int ash_main(int argc, char **argv)
13180{
13181 char *shinit;
13182 volatile int state;
13183 struct jmploc jmploc;
13184 struct stackmark smark;
13185
13186#ifdef __GLIBC__
13187 dash_errno = __errno_location();
13188#endif
13189
13190#if PROFILE
13191 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13192#endif
13193
13194#if ENABLE_FEATURE_EDITING
13195 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13196#endif
13197 state = 0;
13198 if (setjmp(jmploc.loc)) {
13199 int e;
13200 int s;
13201
13202 reset();
13203
13204 e = exception;
13205 if (e == EXERROR)
13206 exitstatus = 2;
13207 s = state;
13208 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13209 exitshell();
13210
13211 if (e == EXINT) {
13212 outcslow('\n', stderr);
13213 }
13214 popstackmark(&smark);
13215 FORCE_INT_ON; /* enable interrupts */
13216 if (s == 1)
13217 goto state1;
13218 else if (s == 2)
13219 goto state2;
13220 else if (s == 3)
13221 goto state3;
13222 else
13223 goto state4;
13224 }
13225 exception_handler = &jmploc;
13226#if DEBUG
13227 opentrace();
13228 trputs("Shell args: ");
13229 trargs(argv);
13230#endif
13231 rootpid = getpid();
13232
13233#if ENABLE_ASH_RANDOM_SUPPORT
13234 rseed = rootpid + time(NULL);
13235#endif
13236 init();
13237 setstackmark(&smark);
13238 procargs(argc, argv);
13239#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13240 if (iflag) {
13241 const char *hp = lookupvar("HISTFILE");
13242
13243 if (hp == NULL) {
13244 hp = lookupvar("HOME");
13245 if (hp != NULL) {
13246 char *defhp = concat_path_file(hp, ".ash_history");
13247 setvar("HISTFILE", defhp, 0);
13248 free(defhp);
13249 }
13250 }
13251 }
13252#endif
13253 if (argv[0] && argv[0][0] == '-')
13254 isloginsh = 1;
13255 if (isloginsh) {
13256 state = 1;
13257 read_profile("/etc/profile");
13258 state1:
13259 state = 2;
13260 read_profile(".profile");
13261 }
13262 state2:
13263 state = 3;
13264 if (
13265#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013266 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013267#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013268 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013269 ) {
13270 shinit = lookupvar("ENV");
13271 if (shinit != NULL && *shinit != '\0') {
13272 read_profile(shinit);
13273 }
13274 }
13275 state3:
13276 state = 4;
13277 if (minusc)
13278 evalstring(minusc, 0);
13279
13280 if (sflag || minusc == NULL) {
13281#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13282 if ( iflag ) {
13283 const char *hp = lookupvar("HISTFILE");
13284
13285 if (hp != NULL)
13286 line_input_state->hist_file = hp;
13287 }
13288#endif
13289 state4: /* XXX ??? - why isn't this before the "if" statement */
13290 cmdloop(1);
13291 }
13292#if PROFILE
13293 monitor(0);
13294#endif
13295#ifdef GPROF
13296 {
13297 extern void _mcleanup(void);
13298 _mcleanup();
13299 }
13300#endif
13301 exitshell();
13302 /* NOTREACHED */
13303}
13304
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013305#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013306const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013307int main(int argc, char **argv)
13308{
13309 return ash_main(argc, argv);
13310}
13311#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013312
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013313
Eric Andersendf82f612001-06-28 07:46:40 +000013314/*-
13315 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013316 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013317 *
13318 * This code is derived from software contributed to Berkeley by
13319 * Kenneth Almquist.
13320 *
13321 * Redistribution and use in source and binary forms, with or without
13322 * modification, are permitted provided that the following conditions
13323 * are met:
13324 * 1. Redistributions of source code must retain the above copyright
13325 * notice, this list of conditions and the following disclaimer.
13326 * 2. Redistributions in binary form must reproduce the above copyright
13327 * notice, this list of conditions and the following disclaimer in the
13328 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013329 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013330 * may be used to endorse or promote products derived from this software
13331 * without specific prior written permission.
13332 *
13333 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13334 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13335 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13336 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13337 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13338 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13339 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13340 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13341 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13342 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13343 * SUCH DAMAGE.
13344 */