blob: 8a213d876fef6c62941c1f595163f0d55a3711cb [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 *
8 * This code is derived from software contributed to Berkeley by
9 * Kenneth Almquist.
10 *
Eric Andersendf82f612001-06-28 07:46:40 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
Eric Andersencb57d552001-06-28 07:25:16 +000015 *
Eric Andersendf82f612001-06-28 07:46:40 +000016 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
Eric Andersen2870d962001-07-02 17:27:21 +000025 * This version of ash is adapted from the source in Debian's ash 0.3.8-5
26 * package.
Eric Andersendf82f612001-06-28 07:46:40 +000027 *
Eric Andersen2870d962001-07-02 17:27:21 +000028 * Modified by Erik Andersen <andersee@debian.org> and
Eric Andersen7467c8d2001-07-12 20:26:32 +000029 * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
Eric Andersen2870d962001-07-02 17:27:21 +000030 *
Eric Andersendf82f612001-06-28 07:46:40 +000031 *
32 * Original copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000033 */
34
Eric Andersen2870d962001-07-02 17:27:21 +000035
36/* These defines allow you to adjust the feature set to be compiled
37 * into the ash shell. As a rule, enabling these options will make
38 * ash get bigger... With all of these options off, ash adds about
Eric Andersen62483552001-07-10 06:09:16 +000039 * 60k to busybox on an x86 system.*/
Eric Andersen2870d962001-07-02 17:27:21 +000040
41
42/* Enable job control. This allows you to run jobs in the background,
43 * which is great when ash is being used as an interactive shell, but
Eric Andersen3102ac42001-07-06 04:26:23 +000044 * it completely useless for is all you are doing is running scripts.
Eric Andersen2870d962001-07-02 17:27:21 +000045 * This adds about 2.5k on an x86 system. */
Eric Andersen62483552001-07-10 06:09:16 +000046#undef JOBS
Eric Andersen2870d962001-07-02 17:27:21 +000047
48/* This enables alias support in ash. If you want to support things
49 * like "alias ls='ls -l'" with ash, enable this. This is only useful
50 * when ash is used as an intractive shell. This adds about 1.5k */
51#define ASH_ALIAS
52
53/* If you need ash to act as a full Posix shell, with full math
Eric Andersen74bcd162001-07-30 21:41:37 +000054 * support, enable this. This adds a bit over 2k an x86 system. */
Eric Andersen34506362001-08-02 05:02:46 +000055//#undef ASH_MATH_SUPPORT
Eric Andersen74bcd162001-07-30 21:41:37 +000056#define ASH_MATH_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +000057
Eric Andersen2870d962001-07-02 17:27:21 +000058/* Getopts is used by shell procedures to parse positional parameters.
59 * You probably want to leave this disabled, and use the busybox getopt
60 * applet if you want to do this sort of thing. There are some scripts
Manuel Novoa III 16815d42001-08-10 19:36:07 +000061 * out there that use it, so it you need it, enable. Most people will
Eric Andersen2870d962001-07-02 17:27:21 +000062 * leave this disabled. This adds 1k on an x86 system. */
63#undef ASH_GETOPTS
64
65/* This allows you to override shell builtins and use whatever is on
66 * the filesystem. This is most useful when ash is acting as a
Eric Andersen62483552001-07-10 06:09:16 +000067 * standalone shell. Adds about 272 bytes. */
Eric Andersen2870d962001-07-02 17:27:21 +000068#undef ASH_CMDCMD
69
Eric Andersen2870d962001-07-02 17:27:21 +000070
Eric Andersen3102ac42001-07-06 04:26:23 +000071/* Optimize size vs speed as size */
72#define ASH_OPTIMIZE_FOR_SIZE
73
Eric Andersen2870d962001-07-02 17:27:21 +000074/* Enable this to compile in extra debugging noise. When debugging is
75 * on, debugging info will be written to $HOME/trace and a quit signal
76 * will generate a core dump. */
77#undef DEBUG
78
Eric Andersen2870d962001-07-02 17:27:21 +000079/* These are here to work with glibc -- Don't change these... */
Eric Andersendf82f612001-06-28 07:46:40 +000080#undef FNMATCH_BROKEN
81#undef GLOB_BROKEN
Eric Andersen5bb16772001-09-06 18:00:41 +000082#define IFS_BROKEN
Eric Andersencb57d552001-06-28 07:25:16 +000083
84#include <assert.h>
Manuel Novoa III 16815d42001-08-10 19:36:07 +000085#include <stddef.h>
Eric Andersencb57d552001-06-28 07:25:16 +000086#include <ctype.h>
87#include <dirent.h>
88#include <errno.h>
89#include <fcntl.h>
90#include <limits.h>
91#include <paths.h>
92#include <pwd.h>
93#include <setjmp.h>
94#include <signal.h>
95#include <stdarg.h>
Eric Andersencb57d552001-06-28 07:25:16 +000096#include <stdio.h>
97#include <stdlib.h>
98#include <string.h>
99#include <sysexits.h>
100#include <unistd.h>
101#include <sys/stat.h>
102#include <sys/cdefs.h>
103#include <sys/ioctl.h>
104#include <sys/param.h>
105#include <sys/resource.h>
106#include <sys/time.h>
107#include <sys/times.h>
108#include <sys/types.h>
109#include <sys/wait.h>
110
111
112#if !defined(FNMATCH_BROKEN)
113#include <fnmatch.h>
114#endif
115#if !defined(GLOB_BROKEN)
116#include <glob.h>
117#endif
118
Eric Andersen2870d962001-07-02 17:27:21 +0000119#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +0000120#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +0000121#endif
122
Eric Andersencb57d552001-06-28 07:25:16 +0000123#include "busybox.h"
Eric Andersen2870d962001-07-02 17:27:21 +0000124#include "cmdedit.h"
125
Eric Andersen2870d962001-07-02 17:27:21 +0000126/*
127 * This file was generated by the mksyntax program.
128 */
129
130/* Syntax classes */
131#define CWORD 0 /* character is nothing special */
132#define CNL 1 /* newline character */
133#define CBACK 2 /* a backslash character */
134#define CSQUOTE 3 /* single quote */
135#define CDQUOTE 4 /* double quote */
136#define CENDQUOTE 5 /* a terminating quote */
137#define CBQUOTE 6 /* backwards single quote */
138#define CVAR 7 /* a dollar sign */
139#define CENDVAR 8 /* a '}' character */
140#define CLP 9 /* a left paren in arithmetic */
141#define CRP 10 /* a right paren in arithmetic */
142#define CENDFILE 11 /* end of file */
143#define CCTL 12 /* like CWORD, except it must be escaped */
144#define CSPCL 13 /* these terminate a word */
145#define CIGN 14 /* character should be ignored */
146
Eric Andersen2870d962001-07-02 17:27:21 +0000147#define SYNBASE 130
148#define PEOF -130
149
150#define PEOA -129
151
152#define TEOF 0
153#define TNL 1
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000154#define TREDIR 2
155#define TWORD 3
156#define TASSIGN 4
157#define TSEMI 5
158#define TBACKGND 6
159#define TAND 7
160#define TOR 8
161#define TPIPE 9
162#define TLP 10
163#define TRP 11
164#define TENDCASE 12
165#define TENDBQUOTE 13
Eric Andersen2870d962001-07-02 17:27:21 +0000166#define TNOT 14
167#define TCASE 15
168#define TDO 16
169#define TDONE 17
170#define TELIF 18
171#define TELSE 19
172#define TESAC 20
173#define TFI 21
174#define TFOR 22
175#define TIF 23
176#define TIN 24
177#define TTHEN 25
178#define TUNTIL 26
179#define TWHILE 27
180#define TBEGIN 28
181#define TEND 29
182
183
Eric Andersen2870d962001-07-02 17:27:21 +0000184
185/* control characters in argument strings */
186#define CTLESC '\201'
187#define CTLVAR '\202'
188#define CTLENDVAR '\203'
189#define CTLBACKQ '\204'
190#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
191/* CTLBACKQ | CTLQUOTE == '\205' */
192#define CTLARI '\206'
193#define CTLENDARI '\207'
194#define CTLQUOTEMARK '\210'
195
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000196
Eric Andersen62483552001-07-10 06:09:16 +0000197#define is_digit(c) ((c)>='0' && (c)<='9')
Eric Andersen2870d962001-07-02 17:27:21 +0000198#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
199#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000200
201/*
202 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
203 * (assuming ascii char codes, as the original implementation did)
204 */
205#define is_special(c) \
206 ( (((unsigned int)c) - 33 < 32) \
207 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
208
Eric Andersen2870d962001-07-02 17:27:21 +0000209#define digit_val(c) ((c) - '0')
Eric Andersencb57d552001-06-28 07:25:16 +0000210
211
212#define _DIAGASSERT(x)
213
Eric Andersen3102ac42001-07-06 04:26:23 +0000214
Eric Andersencb57d552001-06-28 07:25:16 +0000215
Eric Andersen2870d962001-07-02 17:27:21 +0000216#define S_DFL 1 /* default signal handling (SIG_DFL) */
217#define S_CATCH 2 /* signal is caught */
218#define S_IGN 3 /* signal is ignored (SIG_IGN) */
219#define S_HARD_IGN 4 /* signal is ignored permenantly */
220#define S_RESET 5 /* temporary - to reset a hard ignored sig */
Eric Andersencb57d552001-06-28 07:25:16 +0000221
222
Eric Andersen2870d962001-07-02 17:27:21 +0000223/* variable substitution byte (follows CTLVAR) */
224#define VSTYPE 0x0f /* type of variable substitution */
225#define VSNUL 0x10 /* colon--treat the empty string as unset */
226#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
Eric Andersencb57d552001-06-28 07:25:16 +0000227
Eric Andersen2870d962001-07-02 17:27:21 +0000228/* values of VSTYPE field */
229#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
230#define VSMINUS 0x2 /* ${var-text} */
231#define VSPLUS 0x3 /* ${var+text} */
232#define VSQUESTION 0x4 /* ${var?message} */
233#define VSASSIGN 0x5 /* ${var=text} */
234#define VSTRIMLEFT 0x6 /* ${var#pattern} */
235#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
236#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
237#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
238#define VSLENGTH 0xa /* ${#var} */
Eric Andersencb57d552001-06-28 07:25:16 +0000239
Eric Andersen2870d962001-07-02 17:27:21 +0000240/* flags passed to redirect */
241#define REDIR_PUSH 01 /* save previous values of file descriptors */
Eric Andersen3102ac42001-07-06 04:26:23 +0000242#define REDIR_BACKQ 02 /* save the command output to pipe */
Eric Andersencb57d552001-06-28 07:25:16 +0000243
Eric Andersen2870d962001-07-02 17:27:21 +0000244/*
245 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
246 * so we use _setjmp instead.
247 */
248
Eric Andersen62483552001-07-10 06:09:16 +0000249#if defined(BSD)
Eric Andersen2870d962001-07-02 17:27:21 +0000250#define setjmp(jmploc) _setjmp(jmploc)
251#define longjmp(jmploc, val) _longjmp(jmploc, val)
252#endif
253
254/*
255 * Most machines require the value returned from malloc to be aligned
256 * in some way. The following macro will get this right on many machines.
257 */
258
259#ifndef ALIGN
260union align {
261 int i;
262 char *cp;
263};
264
265#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
266#endif
267
268#ifdef BB_LOCALE_SUPPORT
269#include <locale.h>
270static void change_lc_all(const char *value);
271static void change_lc_ctype(const char *value);
272#endif
273
274/*
275 * These macros allow the user to suspend the handling of interrupt signals
276 * over a period of time. This is similar to SIGHOLD to or sigblock, but
277 * much more efficient and portable. (But hacking the kernel is so much
278 * more fun than worrying about efficiency and portability. :-))
279 */
280
281static void onint (void);
282static volatile int suppressint;
283static volatile int intpending;
284
285#define INTOFF suppressint++
Eric Andersen3102ac42001-07-06 04:26:23 +0000286#ifndef ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +0000287#define INTON { if (--suppressint == 0 && intpending) onint(); }
Eric Andersen3102ac42001-07-06 04:26:23 +0000288#define FORCEINTON {suppressint = 0; if (intpending) onint();}
Eric Andersen2870d962001-07-02 17:27:21 +0000289#else
290static void __inton (void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000291static void forceinton (void);
Eric Andersen2870d962001-07-02 17:27:21 +0000292#define INTON __inton()
Eric Andersen3102ac42001-07-06 04:26:23 +0000293#define FORCEINTON forceinton()
Eric Andersen2870d962001-07-02 17:27:21 +0000294#endif
Eric Andersen3102ac42001-07-06 04:26:23 +0000295
Eric Andersen2870d962001-07-02 17:27:21 +0000296#define CLEAR_PENDING_INT intpending = 0
297#define int_pending() intpending
298
299
300typedef void *pointer;
301#ifndef NULL
302#define NULL (void *)0
303#endif
304
305static inline pointer ckmalloc (int sz) { return xmalloc(sz); }
306static inline pointer ckrealloc(void *p, int sz) { return xrealloc(p, sz); }
307static inline char * savestr (const char *s) { return xstrdup(s); }
308
309static pointer stalloc (int);
310static void stunalloc (pointer);
311static void ungrabstackstr (char *, char *);
312static char * growstackstr(void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000313static char * makestrspace(size_t newlen);
Eric Andersen2870d962001-07-02 17:27:21 +0000314static char *sstrdup (const char *);
315
316/*
317 * Parse trees for commands are allocated in lifo order, so we use a stack
318 * to make this more efficient, and also to avoid all sorts of exception
319 * handling code to handle interrupts in the middle of a parse.
320 *
321 * The size 504 was chosen because the Ultrix malloc handles that size
322 * well.
323 */
324
325#define MINSIZE 504 /* minimum size of a block */
326
327
328struct stack_block {
329 struct stack_block *prev;
330 char space[MINSIZE];
331};
332
333static struct stack_block stackbase;
334static struct stack_block *stackp = &stackbase;
335static struct stackmark *markp;
336static char *stacknxt = stackbase.space;
337static int stacknleft = MINSIZE;
338
339
340#define equal(s1, s2) (strcmp(s1, s2) == 0)
341
342#define stackblock() stacknxt
343#define stackblocksize() stacknleft
344#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
Eric Andersen3102ac42001-07-06 04:26:23 +0000345
Eric Andersen2870d962001-07-02 17:27:21 +0000346#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
347#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); }
Eric Andersen2870d962001-07-02 17:27:21 +0000348#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
Eric Andersen3102ac42001-07-06 04:26:23 +0000349
350
351#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
Eric Andersen2870d962001-07-02 17:27:21 +0000352#define STUNPUTC(p) (++sstrnleft, --p)
353#define STTOPC(p) p[-1]
354#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
355#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
356
357#define ckfree(p) free((pointer)(p))
358
Eric Andersen2870d962001-07-02 17:27:21 +0000359
360#ifdef DEBUG
361#define TRACE(param) trace param
362static void trace (const char *, ...);
363static void trargs (char **);
364static void showtree (union node *);
365static void trputc (int);
366static void trputs (const char *);
367static void opentrace (void);
368#else
369#define TRACE(param)
370#endif
371
372#define NSEMI 0
373#define NCMD 1
374#define NPIPE 2
375#define NREDIR 3
376#define NBACKGND 4
377#define NSUBSHELL 5
378#define NAND 6
379#define NOR 7
380#define NIF 8
381#define NWHILE 9
382#define NUNTIL 10
383#define NFOR 11
384#define NCASE 12
385#define NCLIST 13
386#define NDEFUN 14
387#define NARG 15
388#define NTO 16
389#define NFROM 17
390#define NFROMTO 18
391#define NAPPEND 19
392#define NTOOV 20
393#define NTOFD 21
394#define NFROMFD 22
395#define NHERE 23
396#define NXHERE 24
397#define NNOT 25
398
399/*
400 * expandarg() flags
401 */
402#define EXP_FULL 0x1 /* perform word splitting & file globbing */
403#define EXP_TILDE 0x2 /* do normal tilde expansion */
404#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
405#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
406#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
407#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
408
409
410#define NOPTS 16
411
412static char optet_vals[NOPTS];
413
414static const char * const optlist[NOPTS] = {
415 "e" "errexit",
416 "f" "noglob",
417 "I" "ignoreeof",
418 "i" "interactive",
419 "m" "monitor",
420 "n" "noexec",
421 "s" "stdin",
422 "x" "xtrace",
423 "v" "verbose",
424 "V" "vi",
425 "E" "emacs",
426 "C" "noclobber",
427 "a" "allexport",
428 "b" "notify",
429 "u" "nounset",
430 "q" "quietprofile"
431};
432
433#define optent_name(optent) (optent+1)
434#define optent_letter(optent) optent[0]
435#define optent_val(optent) optet_vals[optent]
436
437#define eflag optent_val(0)
438#define fflag optent_val(1)
439#define Iflag optent_val(2)
440#define iflag optent_val(3)
441#define mflag optent_val(4)
442#define nflag optent_val(5)
443#define sflag optent_val(6)
444#define xflag optent_val(7)
445#define vflag optent_val(8)
446#define Vflag optent_val(9)
447#define Eflag optent_val(10)
448#define Cflag optent_val(11)
449#define aflag optent_val(12)
450#define bflag optent_val(13)
451#define uflag optent_val(14)
452#define qflag optent_val(15)
453
454
455/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
456#define FORK_FG 0
457#define FORK_BG 1
458#define FORK_NOJOB 2
459
460
461struct nbinary {
462 int type;
463 union node *ch1;
464 union node *ch2;
465};
466
467
468struct ncmd {
469 int type;
470 int backgnd;
471 union node *assign;
472 union node *args;
473 union node *redirect;
474};
475
476
477struct npipe {
478 int type;
479 int backgnd;
480 struct nodelist *cmdlist;
481};
482
483
484struct nredir {
485 int type;
486 union node *n;
487 union node *redirect;
488};
489
490
491struct nif {
492 int type;
493 union node *test;
494 union node *ifpart;
495 union node *elsepart;
496};
497
498
499struct nfor {
500 int type;
501 union node *args;
502 union node *body;
503 char *var;
504};
505
506
507struct ncase {
508 int type;
509 union node *expr;
510 union node *cases;
511};
512
513
514struct nclist {
515 int type;
516 union node *next;
517 union node *pattern;
518 union node *body;
519};
520
521
522struct narg {
523 int type;
524 union node *next;
525 char *text;
526 struct nodelist *backquote;
527};
528
529
530struct nfile {
531 int type;
532 union node *next;
533 int fd;
534 union node *fname;
535 char *expfname;
536};
537
538
539struct ndup {
540 int type;
541 union node *next;
542 int fd;
543 int dupfd;
544 union node *vname;
545};
546
547
548struct nhere {
549 int type;
550 union node *next;
551 int fd;
552 union node *doc;
553};
554
555
556struct nnot {
557 int type;
558 union node *com;
559};
560
561
562union node {
563 int type;
564 struct nbinary nbinary;
565 struct ncmd ncmd;
566 struct npipe npipe;
567 struct nredir nredir;
568 struct nif nif;
569 struct nfor nfor;
570 struct ncase ncase;
571 struct nclist nclist;
572 struct narg narg;
573 struct nfile nfile;
574 struct ndup ndup;
575 struct nhere nhere;
576 struct nnot nnot;
577};
578
579
580struct nodelist {
581 struct nodelist *next;
582 union node *n;
583};
584
585struct backcmd { /* result of evalbackcmd */
586 int fd; /* file descriptor to read from */
587 char *buf; /* buffer */
588 int nleft; /* number of chars in buffer */
589 struct job *jp; /* job structure for command */
590};
591
592struct cmdentry {
593 int cmdtype;
594 union param {
595 int index;
596 union node *func;
597 const struct builtincmd *cmd;
598 } u;
599};
600
601struct strlist {
602 struct strlist *next;
603 char *text;
604};
605
606
607struct arglist {
608 struct strlist *list;
609 struct strlist **lastp;
610};
611
612struct strpush {
613 struct strpush *prev; /* preceding string on stack */
614 char *prevstring;
615 int prevnleft;
616#ifdef ASH_ALIAS
617 struct alias *ap; /* if push was associated with an alias */
618#endif
619 char *string; /* remember the string since it may change */
620};
621
622struct parsefile {
623 struct parsefile *prev; /* preceding file on stack */
624 int linno; /* current line */
625 int fd; /* file descriptor (or -1 if string) */
626 int nleft; /* number of chars left in this line */
627 int lleft; /* number of chars left in this buffer */
628 char *nextc; /* next char in buffer */
629 char *buf; /* input buffer */
630 struct strpush *strpush; /* for pushing strings at this level */
631 struct strpush basestrpush; /* so pushing one is fast */
632};
633
634struct stackmark {
635 struct stack_block *stackp;
636 char *stacknxt;
637 int stacknleft;
638 struct stackmark *marknext;
639};
640
641struct shparam {
642 int nparam; /* # of positional parameters (without $0) */
643 unsigned char malloc; /* if parameter list dynamically allocated */
644 char **p; /* parameter list */
645 int optind; /* next parameter to be processed by getopts */
646 int optoff; /* used by getopts */
647};
648
Eric Andersen62483552001-07-10 06:09:16 +0000649/*
650 * When commands are first encountered, they are entered in a hash table.
651 * This ensures that a full path search will not have to be done for them
652 * on each invocation.
653 *
654 * We should investigate converting to a linear search, even though that
655 * would make the command name "hash" a misnomer.
656 */
657#define CMDTABLESIZE 31 /* should be prime */
658#define ARB 1 /* actual size determined at run time */
659
660
661
662struct tblentry {
663 struct tblentry *next; /* next entry in hash chain */
664 union param param; /* definition of builtin function */
665 short cmdtype; /* index identifying command */
666 char rehash; /* if set, cd done since entry created */
667 char cmdname[ARB]; /* name of command */
668};
669
670
671static struct tblentry *cmdtable[CMDTABLESIZE];
672static int builtinloc = -1; /* index in path of %builtin, or -1 */
673static int exerrno = 0; /* Last exec error */
674
675
676static void tryexec (char *, char **, char **);
677static void printentry (struct tblentry *, int);
678static void clearcmdentry (int);
679static struct tblentry *cmdlookup (const char *, int);
680static void delete_cmd_entry (void);
681static int path_change (const char *, int *);
682
683
Eric Andersen2870d962001-07-02 17:27:21 +0000684static void flushall (void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000685static void out2fmt (const char *, ...)
686 __attribute__((__format__(__printf__,1,2)));
Eric Andersen2870d962001-07-02 17:27:21 +0000687static int xwrite (int, const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +0000688
Manuel Novoa III c639a352001-08-12 17:32:56 +0000689static inline void outstr (const char *p, FILE *file) { fputs(p, file); }
Eric Andersen3102ac42001-07-06 04:26:23 +0000690static void out1str(const char *p) { outstr(p, stdout); }
691static void out2str(const char *p) { outstr(p, stderr); }
Eric Andersen2870d962001-07-02 17:27:21 +0000692
Eric Andersen62483552001-07-10 06:09:16 +0000693#ifndef ASH_OPTIMIZE_FOR_SIZE
Eric Andersen3102ac42001-07-06 04:26:23 +0000694#define out2c(c) putc((c), stderr)
Eric Andersen62483552001-07-10 06:09:16 +0000695#else
696static void out2c(int c) { putc(c, stderr); }
697#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000698
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000699
700#ifdef ASH_OPTIMIZE_FOR_SIZE
701#define USE_SIT_FUNCTION
702#endif
703
704/* number syntax index */
705#define BASESYNTAX 0 /* not in quotes */
706#define DQSYNTAX 1 /* in double quotes */
707#define SQSYNTAX 2 /* in single quotes */
708#define ARISYNTAX 3 /* in arithmetic */
709
710static const char S_I_T[][4] = {
711 /* 0 */ { CSPCL, CIGN, CIGN, CIGN }, /* PEOA */
712 /* 1 */ { CSPCL, CWORD, CWORD, CWORD }, /* ' ' */
713 /* 2 */ { CNL, CNL, CNL, CNL }, /* \n */
714 /* 3 */ { CWORD, CCTL, CCTL, CWORD }, /* !*-/:=?[]~ */
715 /* 4 */ { CDQUOTE, CENDQUOTE, CWORD, CDQUOTE }, /* '"' */
716 /* 5 */ { CVAR, CVAR, CWORD, CVAR }, /* $ */
717 /* 6 */ { CSQUOTE, CWORD, CENDQUOTE, CSQUOTE }, /* "'" */
718 /* 7 */ { CSPCL, CWORD, CWORD, CLP }, /* ( */
719 /* 8 */ { CSPCL, CWORD, CWORD, CRP }, /* ) */
720 /* 9 */ { CBACK, CBACK, CCTL, CBACK }, /* \ */
721 /* 10 */ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* ` */
722 /* 11 */ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* } */
723#ifndef USE_SIT_FUNCTION
724 /* 12 */ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* PEOF */
725 /* 13 */ { CWORD, CWORD, CWORD, CWORD }, /* 0-9A-Za-z */
726 /* 14 */ { CCTL, CCTL, CCTL, CCTL } /* CTLESC ... */
727#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000728};
729
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000730#ifdef USE_SIT_FUNCTION
731
732#define U_C(c) ((unsigned char)(c))
733
734static int SIT(int c, int syntax)
735{
736 static const char spec_symbls[]="\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
737 static const char syntax_index_table [] = {
738 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
739 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
740 3, 1, 3, 3, 9, 3,10, 1, /* "=>?[\\]`|" */
741 11,3 }; /* "}~" */
742 const char *s;
743 int indx;
744
745 if(c==PEOF) /* 2^8+2 */
746 return CENDFILE;
747 if(c==PEOA) /* 2^8+1 */
748 indx = 0;
749 else if(U_C(c)>=U_C(CTLESC) && U_C(c)<=U_C(CTLQUOTEMARK))
750 return CCTL;
751 else {
752 s = strchr(spec_symbls, c);
753 if(s==0)
754 return CWORD;
755 indx = syntax_index_table[(s-spec_symbls)];
756 }
757 return S_I_T[indx][syntax];
758}
759
760#else /* USE_SIT_FUNCTION */
761
762#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
763
764#define CSPCL_CIGN_CIGN_CIGN 0
765#define CSPCL_CWORD_CWORD_CWORD 1
766#define CNL_CNL_CNL_CNL 2
767#define CWORD_CCTL_CCTL_CWORD 3
768#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE 4
769#define CVAR_CVAR_CWORD_CVAR 5
770#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE 6
771#define CSPCL_CWORD_CWORD_CLP 7
772#define CSPCL_CWORD_CWORD_CRP 8
773#define CBACK_CBACK_CCTL_CBACK 9
774#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
775#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
776#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
777#define CWORD_CWORD_CWORD_CWORD 13
778#define CCTL_CCTL_CCTL_CCTL 14
779
780static const char syntax_index_table[258] = {
781 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
782 /* 0 -130 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
783 /* 1 -129 PEOA */ CSPCL_CIGN_CIGN_CIGN,
784 /* 2 -128 0xff */ CWORD_CWORD_CWORD_CWORD,
785 /* 3 -127 */ CCTL_CCTL_CCTL_CCTL, /* CTLQUOTEMARK */
786 /* 4 -126 */ CCTL_CCTL_CCTL_CCTL,
787 /* 5 -125 */ CCTL_CCTL_CCTL_CCTL,
788 /* 6 -124 */ CCTL_CCTL_CCTL_CCTL,
789 /* 7 -123 */ CCTL_CCTL_CCTL_CCTL,
790 /* 8 -122 */ CCTL_CCTL_CCTL_CCTL,
791 /* 9 -121 */ CCTL_CCTL_CCTL_CCTL,
792 /* 10 -120 */ CCTL_CCTL_CCTL_CCTL, /* CTLESC */
793 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
794 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
795 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
796 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
797 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
798 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
799 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
800 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
801 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
802 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
803 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
804 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
805 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
806 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
807 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
808 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
809 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
810 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
811 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
812 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
813 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
814 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
815 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
816 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
817 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
818 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
819 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
820 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
821 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
822 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
823 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
824 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
825 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
826 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
827 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
828 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
829 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
830 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
831 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
832 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
833 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
834 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
835 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
836 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
837 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
838 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
839 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
840 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
841 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
842 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
843 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
844 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
845 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
846 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
847 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
848 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
849 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
850 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
851 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
852 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
853 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
854 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
855 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
856 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
857 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
858 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
859 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
860 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
861 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
862 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
863 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
864 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
865 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
866 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
867 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
868 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
869 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
870 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
871 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
872 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
873 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
874 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
875 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
876 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
877 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
878 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
879 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
880 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
881 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
882 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
883 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
884 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
885 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
886 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
887 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
888 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
889 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
890 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
891 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
892 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
893 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
894 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
895 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
896 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
897 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
898 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
899 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
900 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
901 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
902 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
903 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
904 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
905 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
906 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
907 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
908 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
909 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
910 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
911 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
912 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
913 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
914 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
915 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
916 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
917 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
918 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
919 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
920 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
921 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
922 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
923 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
924 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
925 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
926 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
927 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
928 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
929 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
930 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
931 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
932 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
933 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
934 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
935 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
936 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
937 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
938 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
939 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
940 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
941 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
942 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
943 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
944 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
945 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
946 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CDQUOTE,
947 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
948 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
949 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
950 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
951 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CSQUOTE,
952 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
953 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
954 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
955 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
956 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
957 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
958 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
959 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
960 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
961 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
962 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
963 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
964 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
965 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
966 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
967 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
968 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
969 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
970 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
971 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
972 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
973 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
974 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
975 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
976 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
977 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
978 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
979 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
980 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
981 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
982 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
983 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
984 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
985 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
986 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
987 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
988 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
989 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
990 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
991 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
992 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
993 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
994 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
995 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
996 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
997 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
998 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
999 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
1000 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
1001 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
1002 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
1003 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
1004 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
1005 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
1006 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
1007 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
1008 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
1009 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
1010 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
1011 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
1012 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
1013 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
1014 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
1015 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
1016 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
1017 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
1018 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
1019 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
1020 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
1021 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
1022 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
1023 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
1024 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
1025 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
1026 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
1027 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
1028 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
1029 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
1030 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
1031 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
1032 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
1033 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
1034 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
1035 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
1036 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
1037 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
1038 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
1039 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00001040};
1041
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001042#endif /* USE_SIT_FUNCTION */
Eric Andersen2870d962001-07-02 17:27:21 +00001043
Eric Andersen2870d962001-07-02 17:27:21 +00001044
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001045/* first char is indicating which tokens mark the end of a list */
1046static const char *const tokname_array[] = {
1047 "\1end of file",
1048 "\0newline",
1049 "\0redirection",
1050 "\0word",
1051 "\0assignment",
1052 "\0;",
1053 "\0&",
1054 "\0&&",
1055 "\0||",
1056 "\0|",
1057 "\0(",
1058 "\1)",
1059 "\1;;",
1060 "\1`",
Eric Andersen2870d962001-07-02 17:27:21 +00001061#define KWDOFFSET 14
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001062 /* the following are keywords */
1063 "\0!",
1064 "\0case",
1065 "\1do",
1066 "\1done",
1067 "\1elif",
1068 "\1else",
1069 "\1esac",
1070 "\1fi",
1071 "\0for",
1072 "\0if",
1073 "\0in",
1074 "\1then",
1075 "\0until",
1076 "\0while",
1077 "\0{",
1078 "\1}",
Eric Andersen2870d962001-07-02 17:27:21 +00001079};
1080
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001081static const char *tokname(int tok)
1082{
1083 static char buf[16];
1084
1085 if(tok>=TSEMI)
1086 buf[0] = '"';
1087 sprintf(buf+(tok>=TSEMI), "%s%c",
1088 tokname_array[tok]+1, (tok>=TSEMI ? '"' : 0));
1089 return buf;
1090}
Eric Andersen2870d962001-07-02 17:27:21 +00001091
1092static int plinno = 1; /* input line number */
1093
1094static int parselleft; /* copy of parsefile->lleft */
1095
1096static struct parsefile basepf; /* top level input file */
1097static char basebuf[BUFSIZ]; /* buffer for top level input file */
1098static struct parsefile *parsefile = &basepf; /* current input file */
1099
1100/*
1101 * NEOF is returned by parsecmd when it encounters an end of file. It
1102 * must be distinct from NULL, so we use the address of a variable that
1103 * happens to be handy.
1104 */
1105
1106static int tokpushback; /* last token pushed back */
1107#define NEOF ((union node *)&tokpushback)
1108static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
1109
1110
1111static void error (const char *, ...) __attribute__((__noreturn__));
1112static void exerror (int, const char *, ...) __attribute__((__noreturn__));
1113static void shellexec (char **, char **, const char *, int)
1114 __attribute__((noreturn));
1115static void exitshell (int) __attribute__((noreturn));
1116
1117static int goodname(const char *);
1118static void ignoresig (int);
1119static void onsig (int);
1120static void dotrap (void);
1121static int decode_signal (const char *, int);
1122
1123static void shprocvar(void);
1124static void deletefuncs(void);
1125static void setparam (char **);
1126static void freeparam (volatile struct shparam *);
1127
1128/* reasons for skipping commands (see comment on breakcmd routine) */
1129#define SKIPBREAK 1
1130#define SKIPCONT 2
1131#define SKIPFUNC 3
1132#define SKIPFILE 4
1133
1134/* values of cmdtype */
1135#define CMDUNKNOWN -1 /* no entry in table for command */
1136#define CMDNORMAL 0 /* command is an executable program */
1137#define CMDBUILTIN 1 /* command is a shell builtin */
1138#define CMDFUNCTION 2 /* command is a shell function */
1139
1140#define DO_ERR 1 /* find_command prints errors */
1141#define DO_ABS 2 /* find_command checks absolute paths */
1142#define DO_NOFUN 4 /* find_command ignores functions */
1143#define DO_BRUTE 8 /* find_command ignores hash table */
1144
1145/*
1146 * Shell variables.
1147 */
1148
1149/* flags */
1150#define VEXPORT 0x01 /* variable is exported */
1151#define VREADONLY 0x02 /* variable cannot be modified */
1152#define VSTRFIXED 0x04 /* variable struct is staticly allocated */
1153#define VTEXTFIXED 0x08 /* text is staticly allocated */
1154#define VSTACK 0x10 /* text is allocated on the stack */
1155#define VUNSET 0x20 /* the variable is not set */
1156#define VNOFUNC 0x40 /* don't call the callback function */
1157
1158
1159struct var {
1160 struct var *next; /* next entry in hash list */
1161 int flags; /* flags are defined above */
1162 char *text; /* name=value */
1163 void (*func) (const char *);
1164 /* function to be called when */
1165 /* the variable gets set/unset */
1166};
1167
1168struct localvar {
1169 struct localvar *next; /* next local variable in list */
1170 struct var *vp; /* the variable that was made local */
1171 int flags; /* saved flags */
1172 char *text; /* saved text */
1173};
1174
1175
Eric Andersen62483552001-07-10 06:09:16 +00001176#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00001177#define rmescapes(p) _rmescapes((p), 0)
1178static char *_rmescapes (char *, int);
1179#else
1180static void rmescapes (char *);
1181#endif
1182
1183static int casematch (union node *, const char *);
1184static void clearredir(void);
1185static void popstring(void);
1186static void readcmdfile (const char *);
1187
1188static int number (const char *);
1189static int is_number (const char *, int *num);
1190static char *single_quote (const char *);
1191static int nextopt (const char *);
1192
1193static void redirect (union node *, int);
1194static void popredir (void);
1195static int dup_as_newfd (int, int);
1196
1197static void changepath(const char *newval);
1198static void getoptsreset(const char *value);
1199
1200
1201static int parsenleft; /* copy of parsefile->nleft */
1202static char *parsenextc; /* copy of parsefile->nextc */
1203static int rootpid; /* pid of main shell */
1204static int rootshell; /* true if we aren't a child of the main shell */
1205
1206static const char spcstr[] = " ";
1207static const char snlfmt[] = "%s\n";
1208
1209static int sstrnleft;
1210static int herefd = -1;
1211
1212static struct localvar *localvars;
1213
1214static struct var vifs;
1215static struct var vmail;
1216static struct var vmpath;
1217static struct var vpath;
1218static struct var vps1;
1219static struct var vps2;
1220static struct var voptind;
1221#ifdef BB_LOCALE_SUPPORT
1222static struct var vlc_all;
1223static struct var vlc_ctype;
1224#endif
1225
1226struct varinit {
1227 struct var *var;
1228 int flags;
1229 const char *text;
1230 void (*func) (const char *);
1231};
1232
1233static const char defpathvar[] =
1234 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
1235#define defpath (defpathvar + 5)
1236
1237#ifdef IFS_BROKEN
1238static const char defifsvar[] = "IFS= \t\n";
1239#define defifs (defifsvar + 4)
1240#else
1241static const char defifs[] = " \t\n";
1242#endif
1243
1244static const struct varinit varinit[] = {
1245#ifdef IFS_BROKEN
1246 { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar,
1247#else
1248 { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=",
1249#endif
1250 NULL },
1251 { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
1252 NULL },
1253 { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
1254 NULL },
1255 { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar,
1256 changepath },
1257 /*
1258 * vps1 depends on uid
1259 */
1260 { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
1261 NULL },
1262 { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1",
1263 getoptsreset },
1264#ifdef BB_LOCALE_SUPPORT
1265 { &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=",
1266 change_lc_all },
1267 { &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=",
1268 change_lc_ctype },
1269#endif
1270 { NULL, 0, NULL,
1271 NULL }
1272};
1273
1274#define VTABSIZE 39
1275
1276static struct var *vartab[VTABSIZE];
1277
1278/*
1279 * The following macros access the values of the above variables.
1280 * They have to skip over the name. They return the null string
1281 * for unset variables.
1282 */
1283
1284#define ifsval() (vifs.text + 4)
1285#define ifsset() ((vifs.flags & VUNSET) == 0)
1286#define mailval() (vmail.text + 5)
1287#define mpathval() (vmpath.text + 9)
1288#define pathval() (vpath.text + 5)
1289#define ps1val() (vps1.text + 4)
1290#define ps2val() (vps2.text + 4)
1291#define optindval() (voptind.text + 7)
1292
1293#define mpathset() ((vmpath.flags & VUNSET) == 0)
1294
1295static void initvar (void);
1296static void setvar (const char *, const char *, int);
1297static void setvareq (char *, int);
1298static void listsetvar (struct strlist *);
Eric Andersen62483552001-07-10 06:09:16 +00001299static const char *lookupvar (const char *);
1300static const char *bltinlookup (const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001301static char **environment (void);
1302static int showvarscmd (int, char **);
1303static void mklocal (char *);
1304static void poplocalvars (void);
1305static int unsetvar (const char *);
1306static int varequal (const char *, const char *);
1307
1308
1309static char *arg0; /* value of $0 */
1310static struct shparam shellparam; /* current positional parameters */
1311static char **argptr; /* argument list for builtin commands */
1312static char *optionarg; /* set by nextopt (like getopt) */
1313static char *optptr; /* used by nextopt */
1314static char *minusc; /* argument to -c option */
1315
1316
1317#ifdef ASH_ALIAS
1318
1319#define ALIASINUSE 1
1320#define ALIASDEAD 2
1321
Eric Andersen3102ac42001-07-06 04:26:23 +00001322#define ATABSIZE 39
1323
Eric Andersen2870d962001-07-02 17:27:21 +00001324struct alias {
1325 struct alias *next;
1326 char *name;
1327 char *val;
1328 int flag;
1329};
1330
1331static struct alias *atab[ATABSIZE];
1332
1333static void setalias (char *, char *);
1334static struct alias **hashalias (const char *);
1335static struct alias *freealias (struct alias *);
1336static struct alias **__lookupalias (const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00001337
1338static void
1339setalias(name, val)
1340 char *name, *val;
1341{
1342 struct alias *ap, **app;
1343
1344 app = __lookupalias(name);
1345 ap = *app;
1346 INTOFF;
1347 if (ap) {
1348 if (!(ap->flag & ALIASINUSE)) {
1349 ckfree(ap->val);
1350 }
Eric Andersen2870d962001-07-02 17:27:21 +00001351 ap->val = savestr(val);
Eric Andersencb57d552001-06-28 07:25:16 +00001352 ap->flag &= ~ALIASDEAD;
1353 } else {
1354 /* not found */
1355 ap = ckmalloc(sizeof (struct alias));
1356 ap->name = savestr(name);
1357 ap->val = savestr(val);
1358 ap->flag = 0;
1359 ap->next = 0;
1360 *app = ap;
1361 }
1362 INTON;
1363}
1364
1365static int
Eric Andersen2870d962001-07-02 17:27:21 +00001366unalias(char *name)
1367{
Eric Andersencb57d552001-06-28 07:25:16 +00001368 struct alias **app;
1369
1370 app = __lookupalias(name);
1371
1372 if (*app) {
1373 INTOFF;
1374 *app = freealias(*app);
1375 INTON;
1376 return (0);
1377 }
1378
1379 return (1);
1380}
1381
Eric Andersencb57d552001-06-28 07:25:16 +00001382static void
Eric Andersen2870d962001-07-02 17:27:21 +00001383rmaliases(void)
1384{
Eric Andersencb57d552001-06-28 07:25:16 +00001385 struct alias *ap, **app;
1386 int i;
1387
1388 INTOFF;
1389 for (i = 0; i < ATABSIZE; i++) {
1390 app = &atab[i];
1391 for (ap = *app; ap; ap = *app) {
1392 *app = freealias(*app);
1393 if (ap == *app) {
1394 app = &ap->next;
1395 }
1396 }
1397 }
1398 INTON;
1399}
1400
Eric Andersen2870d962001-07-02 17:27:21 +00001401static struct alias *
1402lookupalias(const char *name, int check)
Eric Andersencb57d552001-06-28 07:25:16 +00001403{
1404 struct alias *ap = *__lookupalias(name);
1405
1406 if (check && ap && (ap->flag & ALIASINUSE))
1407 return (NULL);
1408 return (ap);
1409}
1410
Eric Andersen2870d962001-07-02 17:27:21 +00001411static void
1412printalias(const struct alias *ap) {
1413 char *p;
1414
1415 p = single_quote(ap->val);
Eric Andersen62483552001-07-10 06:09:16 +00001416 printf("alias %s=%s\n", ap->name, p);
Eric Andersen2870d962001-07-02 17:27:21 +00001417 stunalloc(p);
1418}
1419
Eric Andersencb57d552001-06-28 07:25:16 +00001420
1421/*
1422 * TODO - sort output
1423 */
1424static int
Eric Andersen2870d962001-07-02 17:27:21 +00001425aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001426{
1427 char *n, *v;
1428 int ret = 0;
1429 struct alias *ap;
1430
1431 if (argc == 1) {
1432 int i;
1433
1434 for (i = 0; i < ATABSIZE; i++)
1435 for (ap = atab[i]; ap; ap = ap->next) {
1436 printalias(ap);
1437 }
1438 return (0);
1439 }
1440 while ((n = *++argv) != NULL) {
1441 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
1442 if ((ap = *__lookupalias(n)) == NULL) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001443 out2fmt("%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00001444 ret = 1;
1445 } else
1446 printalias(ap);
1447 }
1448 else {
1449 *v++ = '\0';
1450 setalias(n, v);
1451 }
1452 }
1453
1454 return (ret);
1455}
1456
1457static int
Eric Andersen2870d962001-07-02 17:27:21 +00001458unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001459{
1460 int i;
1461
1462 while ((i = nextopt("a")) != '\0') {
1463 if (i == 'a') {
1464 rmaliases();
1465 return (0);
1466 }
1467 }
1468 for (i = 0; *argptr; argptr++) {
1469 if (unalias(*argptr)) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001470 out2fmt("%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00001471 i = 1;
1472 }
1473 }
1474
1475 return (i);
1476}
1477
1478static struct alias **
1479hashalias(p)
1480 const char *p;
1481 {
1482 unsigned int hashval;
1483
1484 hashval = *p << 4;
1485 while (*p)
1486 hashval+= *p++;
1487 return &atab[hashval % ATABSIZE];
1488}
1489
1490static struct alias *
1491freealias(struct alias *ap) {
1492 struct alias *next;
1493
1494 if (ap->flag & ALIASINUSE) {
1495 ap->flag |= ALIASDEAD;
1496 return ap;
1497 }
1498
1499 next = ap->next;
1500 ckfree(ap->name);
1501 ckfree(ap->val);
1502 ckfree(ap);
1503 return next;
1504}
1505
Eric Andersencb57d552001-06-28 07:25:16 +00001506
1507static struct alias **
1508__lookupalias(const char *name) {
1509 struct alias **app = hashalias(name);
1510
1511 for (; *app; app = &(*app)->next) {
1512 if (equal(name, (*app)->name)) {
1513 break;
1514 }
1515 }
1516
1517 return app;
1518}
Eric Andersen2870d962001-07-02 17:27:21 +00001519#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001520
1521#ifdef ASH_MATH_SUPPORT
Eric Andersen74bcd162001-07-30 21:41:37 +00001522/* The generated file arith.c has been replaced with a custom hand
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001523 * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
1524 * This is now part of libbb, so that it can be used by all the shells
Eric Andersen74bcd162001-07-30 21:41:37 +00001525 * in busybox. */
Eric Andersen2870d962001-07-02 17:27:21 +00001526#define ARITH_NUM 257
1527#define ARITH_LPAREN 258
1528#define ARITH_RPAREN 259
1529#define ARITH_OR 260
1530#define ARITH_AND 261
1531#define ARITH_BOR 262
1532#define ARITH_BXOR 263
1533#define ARITH_BAND 264
1534#define ARITH_EQ 265
1535#define ARITH_NE 266
1536#define ARITH_LT 267
1537#define ARITH_GT 268
1538#define ARITH_GE 269
1539#define ARITH_LE 270
1540#define ARITH_LSHIFT 271
1541#define ARITH_RSHIFT 272
1542#define ARITH_ADD 273
1543#define ARITH_SUB 274
1544#define ARITH_MUL 275
1545#define ARITH_DIV 276
1546#define ARITH_REM 277
1547#define ARITH_UNARYMINUS 278
1548#define ARITH_UNARYPLUS 279
1549#define ARITH_NOT 280
1550#define ARITH_BNOT 281
1551
1552static void expari (int);
Eric Andersencb57d552001-06-28 07:25:16 +00001553#endif
1554
Eric Andersen2870d962001-07-02 17:27:21 +00001555static char *trap[NSIG]; /* trap handler commands */
1556static char sigmode[NSIG - 1]; /* current value of signal */
1557static char gotsig[NSIG - 1]; /* indicates specified signal received */
1558static int pendingsigs; /* indicates some signal received */
1559
Eric Andersencb57d552001-06-28 07:25:16 +00001560/*
1561 * This file was generated by the mkbuiltins program.
1562 */
1563
Eric Andersen2870d962001-07-02 17:27:21 +00001564#ifdef JOBS
1565static int bgcmd (int, char **);
1566static int fgcmd (int, char **);
1567static int killcmd (int, char **);
1568#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001569static int bltincmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001570static int cdcmd (int, char **);
1571static int breakcmd (int, char **);
1572#ifdef ASH_CMDCMD
1573static int commandcmd (int, char **);
1574#endif
1575static int dotcmd (int, char **);
1576static int evalcmd (int, char **);
1577static int execcmd (int, char **);
1578static int exitcmd (int, char **);
1579static int exportcmd (int, char **);
1580static int histcmd (int, char **);
1581static int hashcmd (int, char **);
Eric Andersen1c039232001-07-07 00:05:55 +00001582static int helpcmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001583static int jobscmd (int, char **);
1584static int localcmd (int, char **);
Eric Andersen3102ac42001-07-06 04:26:23 +00001585#ifndef BB_PWD
Eric Andersen2870d962001-07-02 17:27:21 +00001586static int pwdcmd (int, char **);
1587#endif
1588static int readcmd (int, char **);
1589static int returncmd (int, char **);
1590static int setcmd (int, char **);
1591static int setvarcmd (int, char **);
1592static int shiftcmd (int, char **);
1593static int trapcmd (int, char **);
1594static int umaskcmd (int, char **);
1595#ifdef ASH_ALIAS
1596static int aliascmd (int, char **);
1597static int unaliascmd (int, char **);
1598#endif
1599static int unsetcmd (int, char **);
1600static int waitcmd (int, char **);
1601static int ulimitcmd (int, char **);
1602static int timescmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001603#ifdef ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +00001604static int letcmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001605#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001606static int typecmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001607#ifdef ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00001608static int getoptscmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001609#endif
1610
Eric Andersen2870d962001-07-02 17:27:21 +00001611#ifndef BB_TRUE_FALSE
Eric Andersen2870d962001-07-02 17:27:21 +00001612static int true_main (int, char **);
1613static int false_main (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001614#endif
1615
1616static void setpwd (const char *, int);
1617
1618
1619#define BUILTIN_NOSPEC "0"
1620#define BUILTIN_SPECIAL "1"
1621#define BUILTIN_REGULAR "2"
1622#define BUILTIN_ASSIGN "4"
1623#define BUILTIN_SPEC_ASSG "5"
1624#define BUILTIN_REG_ASSG "6"
1625
1626#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1627#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1628#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
1629
1630struct builtincmd {
1631 const char *name;
1632 int (*const builtinfunc) (int, char **);
1633 //unsigned flags;
1634};
1635
Eric Andersencb57d552001-06-28 07:25:16 +00001636
1637/* It is CRUCIAL that this listing be kept in ascii order, otherwise
1638 * the binary search in find_builtin() will stop working. If you value
1639 * your kneecaps, you'll be sure to *make sure* that any changes made
1640 * to this array result in the listing remaining in ascii order. You
1641 * have been warned.
1642 */
1643static const struct builtincmd builtincmds[] = {
Eric Andersen62483552001-07-10 06:09:16 +00001644 { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */
Eric Andersen2870d962001-07-02 17:27:21 +00001645 { BUILTIN_SPECIAL ":", true_main },
1646#ifdef ASH_ALIAS
1647 { BUILTIN_REG_ASSG "alias", aliascmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001648#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001649#ifdef JOBS
1650 { BUILTIN_REGULAR "bg", bgcmd },
1651#endif
1652 { BUILTIN_SPECIAL "break", breakcmd },
Eric Andersen3102ac42001-07-06 04:26:23 +00001653 { BUILTIN_SPECIAL "builtin", bltincmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001654 { BUILTIN_REGULAR "cd", cdcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001655 { BUILTIN_NOSPEC "chdir", cdcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001656#ifdef ASH_CMDCMD
1657 { BUILTIN_REGULAR "command", commandcmd },
1658#endif
1659 { BUILTIN_SPECIAL "continue", breakcmd },
1660 { BUILTIN_SPECIAL "eval", evalcmd },
1661 { BUILTIN_SPECIAL "exec", execcmd },
1662 { BUILTIN_SPECIAL "exit", exitcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001663 { BUILTIN_SPEC_ASSG "export", exportcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001664 { BUILTIN_REGULAR "false", false_main },
Eric Andersen2870d962001-07-02 17:27:21 +00001665 { BUILTIN_REGULAR "fc", histcmd },
1666#ifdef JOBS
1667 { BUILTIN_REGULAR "fg", fgcmd },
1668#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001669#ifdef ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00001670 { BUILTIN_REGULAR "getopts", getoptscmd },
1671#endif
1672 { BUILTIN_NOSPEC "hash", hashcmd },
Eric Andersen1c039232001-07-07 00:05:55 +00001673 { BUILTIN_NOSPEC "help", helpcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001674 { BUILTIN_REGULAR "jobs", jobscmd },
1675#ifdef JOBS
1676 { BUILTIN_REGULAR "kill", killcmd },
1677#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001678#ifdef ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +00001679 { BUILTIN_REGULAR "let", letcmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001680#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001681 { BUILTIN_ASSIGN "local", localcmd },
Eric Andersen3102ac42001-07-06 04:26:23 +00001682#ifndef BB_PWD
Eric Andersen2870d962001-07-02 17:27:21 +00001683 { BUILTIN_NOSPEC "pwd", pwdcmd },
1684#endif
1685 { BUILTIN_REGULAR "read", readcmd },
1686 { BUILTIN_SPEC_ASSG "readonly", exportcmd },
1687 { BUILTIN_SPECIAL "return", returncmd },
1688 { BUILTIN_SPECIAL "set", setcmd },
1689 { BUILTIN_NOSPEC "setvar", setvarcmd },
1690 { BUILTIN_SPECIAL "shift", shiftcmd },
1691 { BUILTIN_SPECIAL "times", timescmd },
1692 { BUILTIN_SPECIAL "trap", trapcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001693 { BUILTIN_REGULAR "true", true_main },
Eric Andersen2870d962001-07-02 17:27:21 +00001694 { BUILTIN_NOSPEC "type", typecmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001695 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
1696 { BUILTIN_REGULAR "umask", umaskcmd },
1697#ifdef ASH_ALIAS
1698 { BUILTIN_REGULAR "unalias", unaliascmd },
1699#endif
1700 { BUILTIN_SPECIAL "unset", unsetcmd },
1701 { BUILTIN_REGULAR "wait", waitcmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001702};
1703#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
1704
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001705#define DOTCMD &builtincmds[0]
Eric Andersen2870d962001-07-02 17:27:21 +00001706static struct builtincmd *BLTINCMD;
1707static struct builtincmd *EXECCMD;
1708static struct builtincmd *EVALCMD;
Eric Andersencb57d552001-06-28 07:25:16 +00001709
Eric Andersen2870d962001-07-02 17:27:21 +00001710/* states */
1711#define JOBSTOPPED 1 /* all procs are stopped */
1712#define JOBDONE 2 /* all procs are completed */
Eric Andersencb57d552001-06-28 07:25:16 +00001713
Eric Andersen2870d962001-07-02 17:27:21 +00001714/*
1715 * A job structure contains information about a job. A job is either a
1716 * single process or a set of processes contained in a pipeline. In the
1717 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1718 * array of pids.
1719 */
Eric Andersencb57d552001-06-28 07:25:16 +00001720
Eric Andersen2870d962001-07-02 17:27:21 +00001721struct procstat {
1722 pid_t pid; /* process id */
1723 int status; /* status flags (defined above) */
1724 char *cmd; /* text of command being run */
1725};
Eric Andersencb57d552001-06-28 07:25:16 +00001726
Eric Andersen2870d962001-07-02 17:27:21 +00001727
1728static int job_warning; /* user was warned about stopped jobs */
1729
1730#ifdef JOBS
1731static void setjobctl(int enable);
1732#else
1733#define setjobctl(on) /* do nothing */
Eric Andersencb57d552001-06-28 07:25:16 +00001734#endif
1735
Eric Andersen2870d962001-07-02 17:27:21 +00001736
1737struct job {
1738 struct procstat ps0; /* status of process */
1739 struct procstat *ps; /* status or processes when more than one */
1740 short nprocs; /* number of processes */
1741 short pgrp; /* process group of this job */
1742 char state; /* true if job is finished */
1743 char used; /* true if this entry is in used */
1744 char changed; /* true if status has changed */
1745#ifdef JOBS
1746 char jobctl; /* job running under job control */
1747#endif
1748};
1749
1750static struct job *jobtab; /* array of jobs */
1751static int njobs; /* size of array */
1752static int backgndpid = -1; /* pid of last background process */
1753#ifdef JOBS
1754static int initialpgrp; /* pgrp of shell on invocation */
1755static int curjob; /* current job */
1756static int jobctl;
1757#endif
1758static int intreceived;
1759
Eric Andersen62483552001-07-10 06:09:16 +00001760static struct job *makejob (const union node *, int);
1761static int forkshell (struct job *, const union node *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001762static int waitforjob (struct job *);
1763
1764static int docd (char *, int);
1765static char *getcomponent (void);
1766static void updatepwd (const char *);
1767static void getpwd (void);
1768
1769static char *padvance (const char **, const char *);
1770
1771static char nullstr[1]; /* zero length string */
1772static char *curdir = nullstr; /* current working directory */
1773static char *cdcomppath;
1774
Eric Andersencb57d552001-06-28 07:25:16 +00001775static int
1776cdcmd(argc, argv)
1777 int argc;
1778 char **argv;
1779{
1780 const char *dest;
1781 const char *path;
1782 char *p;
1783 struct stat statb;
1784 int print = 0;
1785
1786 nextopt(nullstr);
1787 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
1788 error("HOME not set");
1789 if (*dest == '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00001790 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001791 if (dest[0] == '-' && dest[1] == '\0') {
1792 dest = bltinlookup("OLDPWD");
1793 if (!dest || !*dest) {
1794 dest = curdir;
1795 }
1796 print = 1;
1797 if (dest)
Eric Andersen2870d962001-07-02 17:27:21 +00001798 print = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00001799 else
Eric Andersen2870d962001-07-02 17:27:21 +00001800 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001801 }
1802 if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
1803 path = nullstr;
1804 while ((p = padvance(&path, dest)) != NULL) {
1805 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1806 if (!print) {
1807 /*
1808 * XXX - rethink
1809 */
1810 if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
1811 p += 2;
1812 print = strcmp(p, dest);
1813 }
1814 if (docd(p, print) >= 0)
1815 return 0;
1816
1817 }
1818 }
1819 error("can't cd to %s", dest);
1820 /* NOTREACHED */
1821}
1822
1823
1824/*
1825 * Actually do the chdir. In an interactive shell, print the
1826 * directory name if "print" is nonzero.
1827 */
1828
1829static int
1830docd(dest, print)
1831 char *dest;
1832 int print;
1833{
1834 char *p;
1835 char *q;
1836 char *component;
1837 struct stat statb;
1838 int first;
1839 int badstat;
1840
1841 TRACE(("docd(\"%s\", %d) called\n", dest, print));
1842
1843 /*
1844 * Check each component of the path. If we find a symlink or
1845 * something we can't stat, clear curdir to force a getcwd()
1846 * next time we get the value of the current directory.
1847 */
1848 badstat = 0;
1849 cdcomppath = sstrdup(dest);
1850 STARTSTACKSTR(p);
1851 if (*dest == '/') {
1852 STPUTC('/', p);
1853 cdcomppath++;
1854 }
1855 first = 1;
1856 while ((q = getcomponent()) != NULL) {
1857 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
1858 continue;
1859 if (! first)
1860 STPUTC('/', p);
1861 first = 0;
1862 component = q;
1863 while (*q)
1864 STPUTC(*q++, p);
1865 if (equal(component, ".."))
1866 continue;
1867 STACKSTRNUL(p);
1868 if ((lstat(stackblock(), &statb) < 0)
1869 || (S_ISLNK(statb.st_mode))) {
1870 /* print = 1; */
1871 badstat = 1;
1872 break;
1873 }
1874 }
1875
1876 INTOFF;
1877 if (chdir(dest) < 0) {
1878 INTON;
1879 return -1;
1880 }
1881 updatepwd(badstat ? NULL : dest);
1882 INTON;
1883 if (print && iflag)
Eric Andersen62483552001-07-10 06:09:16 +00001884 printf(snlfmt, curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001885 return 0;
1886}
1887
1888
1889/*
1890 * Get the next component of the path name pointed to by cdcomppath.
1891 * This routine overwrites the string pointed to by cdcomppath.
1892 */
1893
1894static char *
1895getcomponent() {
1896 char *p;
1897 char *start;
1898
1899 if ((p = cdcomppath) == NULL)
1900 return NULL;
1901 start = cdcomppath;
1902 while (*p != '/' && *p != '\0')
1903 p++;
1904 if (*p == '\0') {
1905 cdcomppath = NULL;
1906 } else {
1907 *p++ = '\0';
1908 cdcomppath = p;
1909 }
1910 return start;
1911}
1912
1913
1914
1915/*
1916 * Update curdir (the name of the current directory) in response to a
1917 * cd command. We also call hashcd to let the routines in exec.c know
1918 * that the current directory has changed.
1919 */
1920
Eric Andersen2870d962001-07-02 17:27:21 +00001921static void hashcd (void);
1922
Eric Andersencb57d552001-06-28 07:25:16 +00001923static void
Eric Andersen2870d962001-07-02 17:27:21 +00001924updatepwd(const char *dir)
1925{
Eric Andersencb57d552001-06-28 07:25:16 +00001926 char *new;
1927 char *p;
1928 size_t len;
1929
Eric Andersen2870d962001-07-02 17:27:21 +00001930 hashcd(); /* update command hash table */
Eric Andersencb57d552001-06-28 07:25:16 +00001931
1932 /*
1933 * If our argument is NULL, we don't know the current directory
1934 * any more because we traversed a symbolic link or something
1935 * we couldn't stat().
1936 */
1937 if (dir == NULL || curdir == nullstr) {
1938 setpwd(0, 1);
1939 return;
1940 }
1941 len = strlen(dir);
1942 cdcomppath = sstrdup(dir);
1943 STARTSTACKSTR(new);
1944 if (*dir != '/') {
1945 p = curdir;
1946 while (*p)
1947 STPUTC(*p++, new);
1948 if (p[-1] == '/')
1949 STUNPUTC(new);
1950 }
1951 while ((p = getcomponent()) != NULL) {
1952 if (equal(p, "..")) {
1953 while (new > stackblock() && (STUNPUTC(new), *new) != '/');
1954 } else if (*p != '\0' && ! equal(p, ".")) {
1955 STPUTC('/', new);
1956 while (*p)
1957 STPUTC(*p++, new);
1958 }
1959 }
1960 if (new == stackblock())
1961 STPUTC('/', new);
1962 STACKSTRNUL(new);
1963 setpwd(stackblock(), 1);
1964}
1965
1966
Eric Andersen3102ac42001-07-06 04:26:23 +00001967#ifndef BB_PWD
Eric Andersencb57d552001-06-28 07:25:16 +00001968static int
1969pwdcmd(argc, argv)
1970 int argc;
1971 char **argv;
1972{
Eric Andersen62483552001-07-10 06:09:16 +00001973 printf(snlfmt, curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001974 return 0;
1975}
Eric Andersen2870d962001-07-02 17:27:21 +00001976#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001977
1978/*
1979 * Find out what the current directory is. If we already know the current
1980 * directory, this routine returns immediately.
1981 */
1982static void
Eric Andersen2870d962001-07-02 17:27:21 +00001983getpwd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00001984{
Eric Andersen2870d962001-07-02 17:27:21 +00001985 curdir = xgetcwd(0);
1986 if(curdir==0)
1987 curdir = nullstr;
Eric Andersencb57d552001-06-28 07:25:16 +00001988}
1989
1990static void
1991setpwd(const char *val, int setold)
1992{
1993 if (setold) {
1994 setvar("OLDPWD", curdir, VEXPORT);
1995 }
1996 INTOFF;
1997 if (curdir != nullstr) {
1998 free(curdir);
1999 curdir = nullstr;
2000 }
2001 if (!val) {
2002 getpwd();
2003 } else {
2004 curdir = savestr(val);
2005 }
2006 INTON;
2007 setvar("PWD", curdir, VEXPORT);
2008}
2009
Eric Andersencb57d552001-06-28 07:25:16 +00002010/*
2011 * Errors and exceptions.
2012 */
2013
2014/*
2015 * Code to handle exceptions in C.
2016 */
2017
Eric Andersen2870d962001-07-02 17:27:21 +00002018/*
2019 * We enclose jmp_buf in a structure so that we can declare pointers to
2020 * jump locations. The global variable handler contains the location to
2021 * jump to when an exception occurs, and the global variable exception
2022 * contains a code identifying the exeception. To implement nested
2023 * exception handlers, the user should save the value of handler on entry
2024 * to an inner scope, set handler to point to a jmploc structure for the
2025 * inner scope, and restore handler on exit from the scope.
2026 */
2027
2028struct jmploc {
2029 jmp_buf loc;
2030};
2031
2032/* exceptions */
2033#define EXINT 0 /* SIGINT received */
2034#define EXERROR 1 /* a generic error */
2035#define EXSHELLPROC 2 /* execute a shell procedure */
2036#define EXEXEC 3 /* command execution failed */
2037
2038static struct jmploc *handler;
Eric Andersencb57d552001-06-28 07:25:16 +00002039static int exception;
Eric Andersencb57d552001-06-28 07:25:16 +00002040
Eric Andersen2870d962001-07-02 17:27:21 +00002041static void exverror (int, const char *, va_list)
Eric Andersencb57d552001-06-28 07:25:16 +00002042 __attribute__((__noreturn__));
2043
2044/*
2045 * Called to raise an exception. Since C doesn't include exceptions, we
2046 * just do a longjmp to the exception handler. The type of exception is
2047 * stored in the global variable "exception".
2048 */
2049
Eric Andersen2870d962001-07-02 17:27:21 +00002050static void exraise (int) __attribute__((__noreturn__));
2051
Eric Andersencb57d552001-06-28 07:25:16 +00002052static void
Eric Andersen2870d962001-07-02 17:27:21 +00002053exraise(int e)
Eric Andersencb57d552001-06-28 07:25:16 +00002054{
2055#ifdef DEBUG
2056 if (handler == NULL)
2057 abort();
2058#endif
Eric Andersen62483552001-07-10 06:09:16 +00002059 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00002060 exception = e;
2061 longjmp(handler->loc, 1);
2062}
2063
2064
2065/*
2066 * Called from trap.c when a SIGINT is received. (If the user specifies
2067 * that SIGINT is to be trapped or ignored using the trap builtin, then
2068 * this routine is not called.) Suppressint is nonzero when interrupts
2069 * are held using the INTOFF macro. The call to _exit is necessary because
2070 * there is a short period after a fork before the signal handlers are
2071 * set to the appropriate value for the child. (The test for iflag is
2072 * just defensive programming.)
2073 */
2074
2075static void
Eric Andersen2870d962001-07-02 17:27:21 +00002076onint(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00002077 sigset_t mysigset;
2078
2079 if (suppressint) {
2080 intpending++;
2081 return;
2082 }
2083 intpending = 0;
2084 sigemptyset(&mysigset);
2085 sigprocmask(SIG_SETMASK, &mysigset, NULL);
2086 if (rootshell && iflag)
2087 exraise(EXINT);
2088 else {
2089 signal(SIGINT, SIG_DFL);
2090 raise(SIGINT);
2091 }
2092 /* NOTREACHED */
2093}
2094
2095
Eric Andersen2870d962001-07-02 17:27:21 +00002096static char *commandname; /* currently executing command */
2097
Eric Andersencb57d552001-06-28 07:25:16 +00002098/*
2099 * Exverror is called to raise the error exception. If the first argument
2100 * is not NULL then error prints an error message using printf style
2101 * formatting. It then raises the error exception.
2102 */
2103static void
Eric Andersen2870d962001-07-02 17:27:21 +00002104exverror(int cond, const char *msg, va_list ap)
Eric Andersencb57d552001-06-28 07:25:16 +00002105{
2106 CLEAR_PENDING_INT;
2107 INTOFF;
2108
2109#ifdef DEBUG
2110 if (msg)
2111 TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
2112 else
2113 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
2114#endif
2115 if (msg) {
2116 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +00002117 out2fmt("%s: ", commandname);
2118 vfprintf(stderr, msg, ap);
2119 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00002120 }
Eric Andersencb57d552001-06-28 07:25:16 +00002121 exraise(cond);
2122 /* NOTREACHED */
2123}
2124
2125
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002126static void
Eric Andersencb57d552001-06-28 07:25:16 +00002127error(const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00002128{
Eric Andersencb57d552001-06-28 07:25:16 +00002129 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00002130 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00002131 exverror(EXERROR, msg, ap);
2132 /* NOTREACHED */
2133 va_end(ap);
2134}
2135
2136
Eric Andersencb57d552001-06-28 07:25:16 +00002137static void
2138exerror(int cond, const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00002139{
Eric Andersencb57d552001-06-28 07:25:16 +00002140 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00002141 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00002142 exverror(cond, msg, ap);
2143 /* NOTREACHED */
2144 va_end(ap);
2145}
2146
2147
2148
2149/*
2150 * Table of error messages.
2151 */
2152
2153struct errname {
Eric Andersen2870d962001-07-02 17:27:21 +00002154 short errcode; /* error number */
Eric Andersen62483552001-07-10 06:09:16 +00002155 char action; /* operation which encountered the error */
Eric Andersencb57d552001-06-28 07:25:16 +00002156};
2157
Eric Andersen2870d962001-07-02 17:27:21 +00002158/*
2159 * Types of operations (passed to the errmsg routine).
2160 */
2161
2162#define E_OPEN 01 /* opening a file */
2163#define E_CREAT 02 /* creating a file */
2164#define E_EXEC 04 /* executing a program */
Eric Andersencb57d552001-06-28 07:25:16 +00002165
2166#define ALL (E_OPEN|E_CREAT|E_EXEC)
2167
2168static const struct errname errormsg[] = {
Eric Andersen2870d962001-07-02 17:27:21 +00002169 { EINTR, ALL },
2170 { EACCES, ALL },
2171 { EIO, ALL },
2172 { ENOENT, E_OPEN },
2173 { ENOENT, E_CREAT },
2174 { ENOENT, E_EXEC },
2175 { ENOTDIR, E_OPEN },
2176 { ENOTDIR, E_CREAT },
2177 { ENOTDIR, E_EXEC },
2178 { EISDIR, ALL },
2179 { EEXIST, E_CREAT },
2180#ifdef EMFILE
2181 { EMFILE, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002182#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002183 { ENFILE, ALL },
2184 { ENOSPC, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002185#ifdef EDQUOT
Eric Andersen2870d962001-07-02 17:27:21 +00002186 { EDQUOT, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002187#endif
2188#ifdef ENOSR
Eric Andersen2870d962001-07-02 17:27:21 +00002189 { ENOSR, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002190#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002191 { ENXIO, ALL },
2192 { EROFS, ALL },
2193 { ETXTBSY, ALL },
2194#ifdef EAGAIN
2195 { EAGAIN, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00002196#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002197 { ENOMEM, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002198#ifdef ENOLINK
Eric Andersen2870d962001-07-02 17:27:21 +00002199 { ENOLINK, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002200#endif
2201#ifdef EMULTIHOP
Eric Andersen2870d962001-07-02 17:27:21 +00002202 { EMULTIHOP, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002203#endif
2204#ifdef ECOMM
Eric Andersen2870d962001-07-02 17:27:21 +00002205 { ECOMM, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002206#endif
2207#ifdef ESTALE
Eric Andersen2870d962001-07-02 17:27:21 +00002208 { ESTALE, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002209#endif
2210#ifdef ETIMEDOUT
Eric Andersen2870d962001-07-02 17:27:21 +00002211 { ETIMEDOUT, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002212#endif
2213#ifdef ELOOP
Eric Andersen2870d962001-07-02 17:27:21 +00002214 { ELOOP, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002215#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002216 { E2BIG, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00002217#ifdef ELIBACC
Eric Andersen2870d962001-07-02 17:27:21 +00002218 { ELIBACC, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00002219#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002220};
2221
Eric Andersen2870d962001-07-02 17:27:21 +00002222#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname))
Eric Andersencb57d552001-06-28 07:25:16 +00002223
2224/*
2225 * Return a string describing an error. The returned string may be a
2226 * pointer to a static buffer that will be overwritten on the next call.
2227 * Action describes the operation that got the error.
2228 */
2229
2230static const char *
Eric Andersen2870d962001-07-02 17:27:21 +00002231errmsg(int e, int action)
Eric Andersencb57d552001-06-28 07:25:16 +00002232{
2233 struct errname const *ep;
2234 static char buf[12];
2235
Eric Andersen2870d962001-07-02 17:27:21 +00002236 for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) {
Eric Andersencb57d552001-06-28 07:25:16 +00002237 if (ep->errcode == e && (ep->action & action) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00002238 return strerror(e);
Eric Andersencb57d552001-06-28 07:25:16 +00002239 }
Eric Andersen2870d962001-07-02 17:27:21 +00002240
Eric Andersen3102ac42001-07-06 04:26:23 +00002241 snprintf(buf, sizeof buf, "error %d", e);
Eric Andersencb57d552001-06-28 07:25:16 +00002242 return buf;
2243}
2244
2245
Eric Andersen3102ac42001-07-06 04:26:23 +00002246#ifdef ASH_OPTIMIZE_FOR_SIZE
Eric Andersencb57d552001-06-28 07:25:16 +00002247static void
2248__inton() {
2249 if (--suppressint == 0 && intpending) {
2250 onint();
2251 }
2252}
Eric Andersen3102ac42001-07-06 04:26:23 +00002253static void forceinton (void) {
2254 suppressint = 0;
2255 if (intpending)
2256 onint();
2257}
Eric Andersencb57d552001-06-28 07:25:16 +00002258#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002259
2260/* flags in argument to evaltree */
Eric Andersen2870d962001-07-02 17:27:21 +00002261#define EV_EXIT 01 /* exit after evaluating tree */
2262#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2263#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00002264
Eric Andersen2870d962001-07-02 17:27:21 +00002265static int evalskip; /* set if we are skipping commands */
2266static int skipcount; /* number of levels to skip */
2267static int loopnest; /* current loop nesting level */
2268static int funcnest; /* depth of function calls */
Eric Andersencb57d552001-06-28 07:25:16 +00002269
2270
Eric Andersen2870d962001-07-02 17:27:21 +00002271static struct strlist *cmdenviron; /* environment for builtin command */
2272static int exitstatus; /* exit status of last command */
2273static int oexitstatus; /* saved exit status */
Eric Andersencb57d552001-06-28 07:25:16 +00002274
Eric Andersen62483552001-07-10 06:09:16 +00002275static void evalsubshell (const union node *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00002276static void expredir (union node *);
Eric Andersen2870d962001-07-02 17:27:21 +00002277static void prehash (union node *);
2278static void eprintlist (struct strlist *);
Eric Andersencb57d552001-06-28 07:25:16 +00002279
Eric Andersen2870d962001-07-02 17:27:21 +00002280static union node *parsecmd(int);
Eric Andersencb57d552001-06-28 07:25:16 +00002281/*
2282 * Called to reset things after an exception.
2283 */
2284
Eric Andersencb57d552001-06-28 07:25:16 +00002285/*
2286 * The eval commmand.
2287 */
Eric Andersen2870d962001-07-02 17:27:21 +00002288static void evalstring (char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00002289
2290static int
2291evalcmd(argc, argv)
2292 int argc;
2293 char **argv;
2294{
Eric Andersen2870d962001-07-02 17:27:21 +00002295 char *p;
2296 char *concat;
2297 char **ap;
Eric Andersencb57d552001-06-28 07:25:16 +00002298
Eric Andersen2870d962001-07-02 17:27:21 +00002299 if (argc > 1) {
2300 p = argv[1];
2301 if (argc > 2) {
2302 STARTSTACKSTR(concat);
2303 ap = argv + 2;
2304 for (;;) {
2305 while (*p)
2306 STPUTC(*p++, concat);
2307 if ((p = *ap++) == NULL)
2308 break;
2309 STPUTC(' ', concat);
2310 }
2311 STPUTC('\0', concat);
2312 p = grabstackstr(concat);
2313 }
2314 evalstring(p, EV_TESTED);
2315 }
2316 return exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00002317}
2318
Eric Andersencb57d552001-06-28 07:25:16 +00002319/*
2320 * Execute a command or commands contained in a string.
2321 */
2322
Eric Andersen2870d962001-07-02 17:27:21 +00002323static void evaltree (union node *, int);
2324static void setinputstring (char *);
2325static void popfile (void);
2326static void setstackmark(struct stackmark *mark);
2327static void popstackmark(struct stackmark *mark);
2328
2329
Eric Andersencb57d552001-06-28 07:25:16 +00002330static void
Eric Andersen2870d962001-07-02 17:27:21 +00002331evalstring(char *s, int flag)
2332{
Eric Andersencb57d552001-06-28 07:25:16 +00002333 union node *n;
2334 struct stackmark smark;
2335
2336 setstackmark(&smark);
2337 setinputstring(s);
2338 while ((n = parsecmd(0)) != NEOF) {
2339 evaltree(n, flag);
2340 popstackmark(&smark);
2341 }
2342 popfile();
2343 popstackmark(&smark);
2344}
2345
Eric Andersen2870d962001-07-02 17:27:21 +00002346static struct builtincmd *find_builtin (const char *);
Eric Andersen62483552001-07-10 06:09:16 +00002347static void expandarg (union node *, struct arglist *, int);
2348static void calcsize (const union node *);
2349static union node *copynode (const union node *);
2350
2351/*
2352 * Make a copy of a parse tree.
2353 */
2354
2355static int funcblocksize; /* size of structures in function */
2356static int funcstringsize; /* size of strings in node */
2357static pointer funcblock; /* block to allocate function from */
2358static char *funcstring; /* block to allocate strings from */
2359
2360
2361static inline union node *
2362copyfunc(union node *n)
2363{
2364 if (n == NULL)
2365 return NULL;
2366 funcblocksize = 0;
2367 funcstringsize = 0;
2368 calcsize(n);
2369 funcblock = ckmalloc(funcblocksize + funcstringsize);
2370 funcstring = (char *) funcblock + funcblocksize;
2371 return copynode(n);
2372}
2373
2374/*
2375 * Free a parse tree.
2376 */
Eric Andersencb57d552001-06-28 07:25:16 +00002377
2378static void
Eric Andersen62483552001-07-10 06:09:16 +00002379freefunc(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00002380{
Eric Andersen62483552001-07-10 06:09:16 +00002381 if (n)
2382 ckfree(n);
Eric Andersencb57d552001-06-28 07:25:16 +00002383}
2384
2385
Eric Andersen62483552001-07-10 06:09:16 +00002386/*
2387 * Add a new command entry, replacing any existing command entry for
2388 * the same name.
2389 */
2390
2391static inline void
2392addcmdentry(char *name, struct cmdentry *entry)
2393{
2394 struct tblentry *cmdp;
2395
2396 INTOFF;
2397 cmdp = cmdlookup(name, 1);
2398 if (cmdp->cmdtype == CMDFUNCTION) {
2399 freefunc(cmdp->param.func);
2400 }
2401 cmdp->cmdtype = entry->cmdtype;
2402 cmdp->param = entry->u;
2403 INTON;
2404}
2405
2406static inline void
2407evalloop(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002408{
2409 int status;
2410
2411 loopnest++;
2412 status = 0;
2413 for (;;) {
2414 evaltree(n->nbinary.ch1, EV_TESTED);
2415 if (evalskip) {
Eric Andersen2870d962001-07-02 17:27:21 +00002416skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00002417 evalskip = 0;
2418 continue;
2419 }
2420 if (evalskip == SKIPBREAK && --skipcount <= 0)
2421 evalskip = 0;
2422 break;
2423 }
2424 if (n->type == NWHILE) {
2425 if (exitstatus != 0)
2426 break;
2427 } else {
2428 if (exitstatus == 0)
2429 break;
2430 }
2431 evaltree(n->nbinary.ch2, flags & EV_TESTED);
2432 status = exitstatus;
2433 if (evalskip)
2434 goto skipping;
2435 }
2436 loopnest--;
2437 exitstatus = status;
2438}
2439
Eric Andersencb57d552001-06-28 07:25:16 +00002440static void
Eric Andersen62483552001-07-10 06:09:16 +00002441evalfor(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002442{
2443 struct arglist arglist;
2444 union node *argp;
2445 struct strlist *sp;
2446 struct stackmark smark;
2447
2448 setstackmark(&smark);
2449 arglist.lastp = &arglist.list;
2450 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
2451 oexitstatus = exitstatus;
2452 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2453 if (evalskip)
2454 goto out;
2455 }
2456 *arglist.lastp = NULL;
2457
2458 exitstatus = 0;
2459 loopnest++;
2460 for (sp = arglist.list ; sp ; sp = sp->next) {
2461 setvar(n->nfor.var, sp->text, 0);
2462 evaltree(n->nfor.body, flags & EV_TESTED);
2463 if (evalskip) {
2464 if (evalskip == SKIPCONT && --skipcount <= 0) {
2465 evalskip = 0;
2466 continue;
2467 }
2468 if (evalskip == SKIPBREAK && --skipcount <= 0)
2469 evalskip = 0;
2470 break;
2471 }
2472 }
2473 loopnest--;
2474out:
2475 popstackmark(&smark);
2476}
2477
Eric Andersen62483552001-07-10 06:09:16 +00002478static inline void
2479evalcase(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002480{
2481 union node *cp;
2482 union node *patp;
2483 struct arglist arglist;
2484 struct stackmark smark;
2485
2486 setstackmark(&smark);
2487 arglist.lastp = &arglist.list;
2488 oexitstatus = exitstatus;
2489 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
2490 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
2491 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
2492 if (casematch(patp, arglist.list->text)) {
2493 if (evalskip == 0) {
2494 evaltree(cp->nclist.body, flags);
2495 }
2496 goto out;
2497 }
2498 }
2499 }
2500out:
2501 popstackmark(&smark);
2502}
2503
Eric Andersencb57d552001-06-28 07:25:16 +00002504/*
Eric Andersencb57d552001-06-28 07:25:16 +00002505 * Evaluate a pipeline. All the processes in the pipeline are children
2506 * of the process creating the pipeline. (This differs from some versions
2507 * of the shell, which make the last process in a pipeline the parent
2508 * of all the rest.)
2509 */
2510
Eric Andersen62483552001-07-10 06:09:16 +00002511static inline void
Eric Andersencb57d552001-06-28 07:25:16 +00002512evalpipe(n)
2513 union node *n;
2514{
2515 struct job *jp;
2516 struct nodelist *lp;
2517 int pipelen;
2518 int prevfd;
2519 int pip[2];
2520
2521 TRACE(("evalpipe(0x%lx) called\n", (long)n));
2522 pipelen = 0;
2523 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
2524 pipelen++;
2525 INTOFF;
2526 jp = makejob(n, pipelen);
2527 prevfd = -1;
2528 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
2529 prehash(lp->n);
2530 pip[1] = -1;
2531 if (lp->next) {
2532 if (pipe(pip) < 0) {
2533 close(prevfd);
2534 error("Pipe call failed");
2535 }
2536 }
2537 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
2538 INTON;
2539 if (prevfd > 0) {
2540 close(0);
2541 dup_as_newfd(prevfd, 0);
2542 close(prevfd);
2543 if (pip[0] == 0) {
2544 pip[0] = -1;
2545 }
2546 }
2547 if (pip[1] >= 0) {
2548 if (pip[0] >= 0) {
2549 close(pip[0]);
2550 }
2551 if (pip[1] != 1) {
2552 close(1);
2553 dup_as_newfd(pip[1], 1);
2554 close(pip[1]);
2555 }
2556 }
2557 evaltree(lp->n, EV_EXIT);
2558 }
2559 if (prevfd >= 0)
2560 close(prevfd);
2561 prevfd = pip[0];
2562 close(pip[1]);
2563 }
2564 INTON;
2565 if (n->npipe.backgnd == 0) {
2566 INTOFF;
2567 exitstatus = waitforjob(jp);
2568 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
2569 INTON;
2570 }
2571}
2572
Eric Andersen2870d962001-07-02 17:27:21 +00002573static void find_command (const char *, struct cmdentry *, int, const char *);
2574
2575static int
2576isassignment(const char *word) {
2577 if (!is_name(*word)) {
2578 return 0;
2579 }
2580 do {
2581 word++;
2582 } while (is_in_name(*word));
2583 return *word == '=';
2584}
2585
Eric Andersen62483552001-07-10 06:09:16 +00002586
Eric Andersencb57d552001-06-28 07:25:16 +00002587static void
Eric Andersen3102ac42001-07-06 04:26:23 +00002588evalcommand(union node *cmd, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002589{
2590 struct stackmark smark;
2591 union node *argp;
2592 struct arglist arglist;
2593 struct arglist varlist;
2594 char **argv;
2595 int argc;
2596 char **envp;
2597 struct strlist *sp;
2598 int mode;
Eric Andersencb57d552001-06-28 07:25:16 +00002599 struct cmdentry cmdentry;
2600 struct job *jp;
2601 char *volatile savecmdname;
2602 volatile struct shparam saveparam;
2603 struct localvar *volatile savelocalvars;
2604 volatile int e;
2605 char *lastarg;
2606 const char *path;
2607 const struct builtincmd *firstbltin;
2608 struct jmploc *volatile savehandler;
2609 struct jmploc jmploc;
2610#if __GNUC__
2611 /* Avoid longjmp clobbering */
2612 (void) &argv;
2613 (void) &argc;
2614 (void) &lastarg;
2615 (void) &flags;
2616#endif
2617
2618 /* First expand the arguments. */
2619 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
2620 setstackmark(&smark);
2621 arglist.lastp = &arglist.list;
2622 varlist.lastp = &varlist.list;
2623 arglist.list = 0;
2624 oexitstatus = exitstatus;
2625 exitstatus = 0;
2626 path = pathval();
2627 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
2628 expandarg(argp, &varlist, EXP_VARTILDE);
2629 }
2630 for (
2631 argp = cmd->ncmd.args; argp && !arglist.list;
2632 argp = argp->narg.next
2633 ) {
2634 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2635 }
2636 if (argp) {
2637 struct builtincmd *bcmd;
Eric Andersen62483552001-07-10 06:09:16 +00002638 int pseudovarflag;
Eric Andersencb57d552001-06-28 07:25:16 +00002639 bcmd = find_builtin(arglist.list->text);
Eric Andersen2870d962001-07-02 17:27:21 +00002640 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
Eric Andersencb57d552001-06-28 07:25:16 +00002641 for (; argp; argp = argp->narg.next) {
2642 if (pseudovarflag && isassignment(argp->narg.text)) {
2643 expandarg(argp, &arglist, EXP_VARTILDE);
2644 continue;
2645 }
2646 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2647 }
2648 }
2649 *arglist.lastp = NULL;
2650 *varlist.lastp = NULL;
2651 expredir(cmd->ncmd.redirect);
2652 argc = 0;
2653 for (sp = arglist.list ; sp ; sp = sp->next)
2654 argc++;
2655 argv = stalloc(sizeof (char *) * (argc + 1));
2656
2657 for (sp = arglist.list ; sp ; sp = sp->next) {
2658 TRACE(("evalcommand arg: %s\n", sp->text));
2659 *argv++ = sp->text;
2660 }
2661 *argv = NULL;
2662 lastarg = NULL;
2663 if (iflag && funcnest == 0 && argc > 0)
2664 lastarg = argv[-1];
2665 argv -= argc;
2666
2667 /* Print the command if xflag is set. */
2668 if (xflag) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002669 out2c('+');
Eric Andersencb57d552001-06-28 07:25:16 +00002670 eprintlist(varlist.list);
2671 eprintlist(arglist.list);
Eric Andersen3102ac42001-07-06 04:26:23 +00002672 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00002673 }
2674
2675 /* Now locate the command. */
2676 if (argc == 0) {
2677 cmdentry.cmdtype = CMDBUILTIN;
2678 firstbltin = cmdentry.u.cmd = BLTINCMD;
2679 } else {
2680 const char *oldpath;
2681 int findflag = DO_ERR;
2682 int oldfindflag;
2683
2684 /*
2685 * Modify the command lookup path, if a PATH= assignment
2686 * is present
2687 */
2688 for (sp = varlist.list ; sp ; sp = sp->next)
2689 if (varequal(sp->text, defpathvar)) {
2690 path = sp->text + 5;
2691 findflag |= DO_BRUTE;
2692 }
2693 oldpath = path;
2694 oldfindflag = findflag;
2695 firstbltin = 0;
2696 for(;;) {
2697 find_command(argv[0], &cmdentry, findflag, path);
Eric Andersen2870d962001-07-02 17:27:21 +00002698 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
Eric Andersencb57d552001-06-28 07:25:16 +00002699 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002700 goto out;
2701 }
2702 /* implement bltin and command here */
2703 if (cmdentry.cmdtype != CMDBUILTIN) {
2704 break;
2705 }
2706 if (!firstbltin) {
2707 firstbltin = cmdentry.u.cmd;
2708 }
2709 if (cmdentry.u.cmd == BLTINCMD) {
2710 for(;;) {
2711 struct builtincmd *bcmd;
2712
2713 argv++;
2714 if (--argc == 0)
2715 goto found;
2716 if (!(bcmd = find_builtin(*argv))) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002717 out2fmt("%s: not found\n", *argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002718 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002719 goto out;
2720 }
2721 cmdentry.u.cmd = bcmd;
2722 if (bcmd != BLTINCMD)
2723 break;
2724 }
2725 }
Eric Andersen2870d962001-07-02 17:27:21 +00002726 if (cmdentry.u.cmd == find_builtin("command")) {
Eric Andersencb57d552001-06-28 07:25:16 +00002727 argv++;
2728 if (--argc == 0) {
2729 goto found;
2730 }
2731 if (*argv[0] == '-') {
2732 if (!equal(argv[0], "-p")) {
2733 argv--;
2734 argc++;
2735 break;
2736 }
2737 argv++;
2738 if (--argc == 0) {
2739 goto found;
2740 }
2741 path = defpath;
2742 findflag |= DO_BRUTE;
2743 } else {
2744 path = oldpath;
2745 findflag = oldfindflag;
2746 }
2747 findflag |= DO_NOFUN;
2748 continue;
2749 }
2750found:
2751 break;
2752 }
2753 }
2754
2755 /* Fork off a child process if necessary. */
2756 if (cmd->ncmd.backgnd
2757 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00002758 ) {
2759 jp = makejob(cmd, 1);
2760 mode = cmd->ncmd.backgnd;
Eric Andersencb57d552001-06-28 07:25:16 +00002761 if (forkshell(jp, cmd, mode) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00002762 goto parent; /* at end of routine */
Eric Andersencb57d552001-06-28 07:25:16 +00002763 flags |= EV_EXIT;
2764 }
2765
2766 /* This is the child process if a fork occurred. */
2767 /* Execute the command. */
2768 if (cmdentry.cmdtype == CMDFUNCTION) {
2769#ifdef DEBUG
2770 trputs("Shell function: "); trargs(argv);
2771#endif
2772 exitstatus = oexitstatus;
2773 redirect(cmd->ncmd.redirect, REDIR_PUSH);
2774 saveparam = shellparam;
2775 shellparam.malloc = 0;
2776 shellparam.nparam = argc - 1;
2777 shellparam.p = argv + 1;
2778 INTOFF;
2779 savelocalvars = localvars;
2780 localvars = NULL;
2781 INTON;
2782 if (setjmp(jmploc.loc)) {
2783 if (exception == EXSHELLPROC) {
2784 freeparam((volatile struct shparam *)
2785 &saveparam);
2786 } else {
2787 saveparam.optind = shellparam.optind;
2788 saveparam.optoff = shellparam.optoff;
2789 freeparam(&shellparam);
2790 shellparam = saveparam;
2791 }
2792 poplocalvars();
2793 localvars = savelocalvars;
2794 handler = savehandler;
2795 longjmp(handler->loc, 1);
2796 }
2797 savehandler = handler;
2798 handler = &jmploc;
2799 for (sp = varlist.list ; sp ; sp = sp->next)
2800 mklocal(sp->text);
2801 funcnest++;
2802 evaltree(cmdentry.u.func, flags & EV_TESTED);
2803 funcnest--;
2804 INTOFF;
2805 poplocalvars();
2806 localvars = savelocalvars;
2807 saveparam.optind = shellparam.optind;
2808 saveparam.optoff = shellparam.optoff;
2809 freeparam(&shellparam);
2810 shellparam = saveparam;
2811 handler = savehandler;
2812 popredir();
2813 INTON;
2814 if (evalskip == SKIPFUNC) {
2815 evalskip = 0;
2816 skipcount = 0;
2817 }
2818 if (flags & EV_EXIT)
2819 exitshell(exitstatus);
2820 } else if (cmdentry.cmdtype == CMDBUILTIN) {
2821#ifdef DEBUG
2822 trputs("builtin command: "); trargs(argv);
2823#endif
2824 mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
Eric Andersencb57d552001-06-28 07:25:16 +00002825 redirect(cmd->ncmd.redirect, mode);
2826 savecmdname = commandname;
Eric Andersen2870d962001-07-02 17:27:21 +00002827 if (IS_BUILTIN_SPECIAL(firstbltin)) {
Eric Andersencb57d552001-06-28 07:25:16 +00002828 listsetvar(varlist.list);
2829 } else {
2830 cmdenviron = varlist.list;
2831 }
2832 e = -1;
2833 if (setjmp(jmploc.loc)) {
2834 e = exception;
2835 exitstatus = (e == EXINT)? SIGINT+128 : 2;
2836 goto cmddone;
2837 }
2838 savehandler = handler;
2839 handler = &jmploc;
2840 commandname = argv[0];
2841 argptr = argv + 1;
Eric Andersen2870d962001-07-02 17:27:21 +00002842 optptr = NULL; /* initialize nextopt */
Eric Andersencb57d552001-06-28 07:25:16 +00002843 exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
2844 flushall();
2845cmddone:
Eric Andersencb57d552001-06-28 07:25:16 +00002846 cmdenviron = NULL;
2847 if (e != EXSHELLPROC) {
2848 commandname = savecmdname;
2849 if (flags & EV_EXIT)
2850 exitshell(exitstatus);
2851 }
2852 handler = savehandler;
2853 if (e != -1) {
2854 if ((e != EXERROR && e != EXEXEC)
2855 || cmdentry.u.cmd == BLTINCMD
2856 || cmdentry.u.cmd == DOTCMD
2857 || cmdentry.u.cmd == EVALCMD
2858 || cmdentry.u.cmd == EXECCMD)
2859 exraise(e);
2860 FORCEINTON;
2861 }
2862 if (cmdentry.u.cmd != EXECCMD)
2863 popredir();
Eric Andersencb57d552001-06-28 07:25:16 +00002864 } else {
2865#ifdef DEBUG
2866 trputs("normal command: "); trargs(argv);
2867#endif
2868 redirect(cmd->ncmd.redirect, 0);
2869 clearredir();
2870 for (sp = varlist.list ; sp ; sp = sp->next)
2871 setvareq(sp->text, VEXPORT|VSTACK);
2872 envp = environment();
2873 shellexec(argv, envp, path, cmdentry.u.index);
2874 }
2875 goto out;
2876
Eric Andersen2870d962001-07-02 17:27:21 +00002877parent: /* parent process gets here (if we forked) */
2878 if (mode == 0) { /* argument to fork */
Eric Andersencb57d552001-06-28 07:25:16 +00002879 INTOFF;
2880 exitstatus = waitforjob(jp);
2881 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002882 }
2883
2884out:
2885 if (lastarg)
2886 setvar("_", lastarg, 0);
2887 popstackmark(&smark);
2888}
2889
Eric Andersen62483552001-07-10 06:09:16 +00002890/*
2891 * Evaluate a parse tree. The value is left in the global variable
2892 * exitstatus.
2893 */
2894static void
2895evaltree(n, flags)
2896 union node *n;
2897 int flags;
2898{
2899 int checkexit = 0;
2900 if (n == NULL) {
2901 TRACE(("evaltree(NULL) called\n"));
2902 goto out;
2903 }
2904 TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
2905 switch (n->type) {
2906 case NSEMI:
2907 evaltree(n->nbinary.ch1, flags & EV_TESTED);
2908 if (evalskip)
2909 goto out;
2910 evaltree(n->nbinary.ch2, flags);
2911 break;
2912 case NAND:
2913 evaltree(n->nbinary.ch1, EV_TESTED);
2914 if (evalskip || exitstatus != 0)
2915 goto out;
2916 evaltree(n->nbinary.ch2, flags);
2917 break;
2918 case NOR:
2919 evaltree(n->nbinary.ch1, EV_TESTED);
2920 if (evalskip || exitstatus == 0)
2921 goto out;
2922 evaltree(n->nbinary.ch2, flags);
2923 break;
2924 case NREDIR:
2925 expredir(n->nredir.redirect);
2926 redirect(n->nredir.redirect, REDIR_PUSH);
2927 evaltree(n->nredir.n, flags);
2928 popredir();
2929 break;
2930 case NSUBSHELL:
2931 evalsubshell(n, flags);
2932 break;
2933 case NBACKGND:
2934 evalsubshell(n, flags);
2935 break;
2936 case NIF: {
2937 evaltree(n->nif.test, EV_TESTED);
2938 if (evalskip)
2939 goto out;
2940 if (exitstatus == 0)
2941 evaltree(n->nif.ifpart, flags);
2942 else if (n->nif.elsepart)
2943 evaltree(n->nif.elsepart, flags);
2944 else
2945 exitstatus = 0;
2946 break;
2947 }
2948 case NWHILE:
2949 case NUNTIL:
2950 evalloop(n, flags);
2951 break;
2952 case NFOR:
2953 evalfor(n, flags);
2954 break;
2955 case NCASE:
2956 evalcase(n, flags);
2957 break;
2958 case NDEFUN: {
2959 struct builtincmd *bcmd;
2960 struct cmdentry entry;
2961 if (
2962 (bcmd = find_builtin(n->narg.text)) &&
2963 IS_BUILTIN_SPECIAL(bcmd)
2964 ) {
2965 out2fmt("%s is a special built-in\n", n->narg.text);
2966 exitstatus = 1;
2967 break;
2968 }
2969 entry.cmdtype = CMDFUNCTION;
2970 entry.u.func = copyfunc(n->narg.next);
2971 addcmdentry(n->narg.text, &entry);
2972 exitstatus = 0;
2973 break;
2974 }
2975 case NNOT:
2976 evaltree(n->nnot.com, EV_TESTED);
2977 exitstatus = !exitstatus;
2978 break;
Eric Andersencb57d552001-06-28 07:25:16 +00002979
Eric Andersen62483552001-07-10 06:09:16 +00002980 case NPIPE:
2981 evalpipe(n);
2982 checkexit = 1;
2983 break;
2984 case NCMD:
2985 evalcommand(n, flags);
2986 checkexit = 1;
2987 break;
2988#ifdef DEBUG
2989 default:
2990 printf("Node type = %d\n", n->type);
2991 break;
2992#endif
2993 }
2994out:
2995 if (pendingsigs)
2996 dotrap();
2997 if (
2998 flags & EV_EXIT ||
2999 (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
3000 )
3001 exitshell(exitstatus);
3002}
3003
3004/*
3005 * Kick off a subshell to evaluate a tree.
3006 */
3007
3008static void
3009evalsubshell(const union node *n, int flags)
3010{
3011 struct job *jp;
3012 int backgnd = (n->type == NBACKGND);
3013
3014 expredir(n->nredir.redirect);
3015 jp = makejob(n, 1);
3016 if (forkshell(jp, n, backgnd) == 0) {
3017 if (backgnd)
3018 flags &=~ EV_TESTED;
3019 redirect(n->nredir.redirect, 0);
3020 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
3021 }
3022 if (! backgnd) {
3023 INTOFF;
3024 exitstatus = waitforjob(jp);
3025 INTON;
3026 }
3027}
3028
3029/*
3030 * Compute the names of the files in a redirection list.
3031 */
3032
3033static void fixredir(union node *n, const char *text, int err);
3034
3035static void
3036expredir(union node *n)
3037{
3038 union node *redir;
3039
3040 for (redir = n ; redir ; redir = redir->nfile.next) {
3041 struct arglist fn;
3042 fn.lastp = &fn.list;
3043 oexitstatus = exitstatus;
3044 switch (redir->type) {
3045 case NFROMTO:
3046 case NFROM:
3047 case NTO:
3048 case NAPPEND:
3049 case NTOOV:
3050 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3051 redir->nfile.expfname = fn.list->text;
3052 break;
3053 case NFROMFD:
3054 case NTOFD:
3055 if (redir->ndup.vname) {
3056 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
3057 fixredir(redir, fn.list->text, 1);
3058 }
3059 break;
3060 }
3061 }
3062}
3063
3064
3065/*
3066 * Execute a command inside back quotes. If it's a builtin command, we
3067 * want to save its output in a block obtained from malloc. Otherwise
3068 * we fork off a subprocess and get the output of the command via a pipe.
3069 * Should be called with interrupts off.
3070 */
3071
3072static void
3073evalbackcmd(union node *n, struct backcmd *result)
3074{
3075 int pip[2];
3076 struct job *jp;
3077 struct stackmark smark; /* unnecessary */
3078
3079 setstackmark(&smark);
3080 result->fd = -1;
3081 result->buf = NULL;
3082 result->nleft = 0;
3083 result->jp = NULL;
3084 if (n == NULL) {
3085 exitstatus = 0;
3086 goto out;
3087 }
3088 exitstatus = 0;
3089 if (pipe(pip) < 0)
3090 error("Pipe call failed");
3091 jp = makejob(n, 1);
3092 if (forkshell(jp, n, FORK_NOJOB) == 0) {
3093 FORCEINTON;
3094 close(pip[0]);
3095 if (pip[1] != 1) {
3096 close(1);
3097 dup_as_newfd(pip[1], 1);
3098 close(pip[1]);
3099 }
3100 eflag = 0;
3101 evaltree(n, EV_EXIT);
3102 }
3103 close(pip[1]);
3104 result->fd = pip[0];
3105 result->jp = jp;
3106out:
3107 popstackmark(&smark);
3108 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
3109 result->fd, result->buf, result->nleft, result->jp));
3110}
3111
3112
3113/*
3114 * Execute a simple command.
3115 */
Eric Andersencb57d552001-06-28 07:25:16 +00003116
3117/*
3118 * Search for a command. This is called before we fork so that the
3119 * location of the command will be available in the parent as well as
3120 * the child. The check for "goodname" is an overly conservative
3121 * check that the name will not be subject to expansion.
3122 */
3123
3124static void
3125prehash(n)
3126 union node *n;
3127{
3128 struct cmdentry entry;
3129
3130 if (n->type == NCMD && n->ncmd.args)
3131 if (goodname(n->ncmd.args->narg.text))
3132 find_command(n->ncmd.args->narg.text, &entry, 0,
3133 pathval());
3134}
3135
3136
Eric Andersencb57d552001-06-28 07:25:16 +00003137/*
3138 * Builtin commands. Builtin commands whose functions are closely
3139 * tied to evaluation are implemented here.
3140 */
3141
3142/*
3143 * No command given, or a bltin command with no arguments. Set the
3144 * specified variables.
3145 */
3146
3147int
3148bltincmd(argc, argv)
3149 int argc;
3150 char **argv;
3151{
3152 /*
3153 * Preserve exitstatus of a previous possible redirection
3154 * as POSIX mandates
3155 */
3156 return exitstatus;
3157}
3158
3159
3160/*
3161 * Handle break and continue commands. Break, continue, and return are
3162 * all handled by setting the evalskip flag. The evaluation routines
3163 * above all check this flag, and if it is set they start skipping
3164 * commands rather than executing them. The variable skipcount is
3165 * the number of loops to break/continue, or the number of function
3166 * levels to return. (The latter is always 1.) It should probably
3167 * be an error to break out of more loops than exist, but it isn't
3168 * in the standard shell so we don't make it one here.
3169 */
3170
3171static int
3172breakcmd(argc, argv)
3173 int argc;
3174 char **argv;
3175{
3176 int n = argc > 1 ? number(argv[1]) : 1;
3177
3178 if (n > loopnest)
3179 n = loopnest;
3180 if (n > 0) {
3181 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
3182 skipcount = n;
3183 }
3184 return 0;
3185}
3186
3187
3188/*
3189 * The return command.
3190 */
3191
3192static int
3193returncmd(argc, argv)
3194 int argc;
3195 char **argv;
3196{
3197 int ret = argc > 1 ? number(argv[1]) : oexitstatus;
3198
3199 if (funcnest) {
3200 evalskip = SKIPFUNC;
3201 skipcount = 1;
3202 return ret;
3203 }
3204 else {
3205 /* Do what ksh does; skip the rest of the file */
3206 evalskip = SKIPFILE;
3207 skipcount = 1;
3208 return ret;
3209 }
3210}
3211
3212
3213#ifndef BB_TRUE_FALSE
3214static int
3215false_main(argc, argv)
3216 int argc;
3217 char **argv;
3218{
3219 return 1;
3220}
3221
3222
3223static int
3224true_main(argc, argv)
3225 int argc;
3226 char **argv;
3227{
3228 return 0;
3229}
3230#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003231
3232/*
3233 * Controls whether the shell is interactive or not.
3234 */
3235
3236static void setsignal(int signo);
3237static void chkmail(int silent);
3238
3239
3240static void
3241setinteractive(int on)
3242{
3243 static int is_interactive;
Eric Andersen1c039232001-07-07 00:05:55 +00003244 static int do_banner=0;
Eric Andersen2870d962001-07-02 17:27:21 +00003245
3246 if (on == is_interactive)
3247 return;
3248 setsignal(SIGINT);
3249 setsignal(SIGQUIT);
3250 setsignal(SIGTERM);
3251 chkmail(1);
3252 is_interactive = on;
Eric Andersen1c039232001-07-07 00:05:55 +00003253 if (do_banner==0 && is_interactive) {
3254 /* Looks like they want an interactive shell */
3255 printf( "\n\n" BB_BANNER " Built-in shell (ash)\n");
3256 printf( "Enter 'help' for a list of built-in commands.\n\n");
3257 do_banner=1;
3258 }
Eric Andersen2870d962001-07-02 17:27:21 +00003259}
3260
3261static void
3262optschanged(void)
3263{
3264 setinteractive(iflag);
3265 setjobctl(mflag);
3266}
3267
Eric Andersencb57d552001-06-28 07:25:16 +00003268
3269static int
3270execcmd(argc, argv)
3271 int argc;
3272 char **argv;
3273{
3274 if (argc > 1) {
3275 struct strlist *sp;
3276
Eric Andersen2870d962001-07-02 17:27:21 +00003277 iflag = 0; /* exit on error */
Eric Andersencb57d552001-06-28 07:25:16 +00003278 mflag = 0;
3279 optschanged();
3280 for (sp = cmdenviron; sp ; sp = sp->next)
3281 setvareq(sp->text, VEXPORT|VSTACK);
3282 shellexec(argv + 1, environment(), pathval(), 0);
3283 }
3284 return 0;
3285}
3286
3287static void
3288eprintlist(struct strlist *sp)
3289{
3290 for (; sp; sp = sp->next) {
Eric Andersen3102ac42001-07-06 04:26:23 +00003291 out2fmt(" %s",sp->text);
Eric Andersencb57d552001-06-28 07:25:16 +00003292 }
3293}
Eric Andersencb57d552001-06-28 07:25:16 +00003294
3295/*
3296 * Exec a program. Never returns. If you change this routine, you may
3297 * have to change the find_command routine as well.
3298 */
3299
Eric Andersen2870d962001-07-02 17:27:21 +00003300static const char *pathopt; /* set by padvance */
3301
Eric Andersencb57d552001-06-28 07:25:16 +00003302static void
3303shellexec(argv, envp, path, idx)
3304 char **argv, **envp;
3305 const char *path;
3306 int idx;
3307{
3308 char *cmdname;
3309 int e;
3310
3311 if (strchr(argv[0], '/') != NULL) {
3312 tryexec(argv[0], argv, envp);
3313 e = errno;
3314 } else {
3315 e = ENOENT;
3316 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3317 if (--idx < 0 && pathopt == NULL) {
3318 tryexec(cmdname, argv, envp);
3319 if (errno != ENOENT && errno != ENOTDIR)
3320 e = errno;
3321 }
3322 stunalloc(cmdname);
3323 }
3324 }
3325
3326 /* Map to POSIX errors */
3327 switch (e) {
3328 case EACCES:
3329 exerrno = 126;
3330 break;
3331 case ENOENT:
3332 exerrno = 127;
3333 break;
3334 default:
3335 exerrno = 2;
3336 break;
3337 }
3338 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3339 /* NOTREACHED */
3340}
3341
Eric Andersen2870d962001-07-02 17:27:21 +00003342/*
3343 * Clear traps on a fork.
3344 */
3345static void
3346clear_traps(void) {
3347 char **tp;
3348
3349 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
3350 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
3351 INTOFF;
3352 ckfree(*tp);
3353 *tp = NULL;
3354 if (tp != &trap[0])
3355 setsignal(tp - trap);
3356 INTON;
3357 }
3358 }
3359}
3360
3361
3362static void
3363initshellproc(void) {
3364
3365#ifdef ASH_ALIAS
3366 /* from alias.c: */
3367 {
3368 rmaliases();
3369 }
3370#endif
3371 /* from eval.c: */
3372 {
3373 exitstatus = 0;
3374 }
3375
3376 /* from exec.c: */
3377 {
3378 deletefuncs();
3379 }
3380
3381 /* from jobs.c: */
3382 {
3383 backgndpid = -1;
3384#ifdef JOBS
3385 jobctl = 0;
3386#endif
3387 }
3388
3389 /* from options.c: */
3390 {
3391 int i;
3392
3393 for (i = 0; i < NOPTS; i++)
3394 optent_val(i) = 0;
3395 optschanged();
3396
3397 }
3398
3399 /* from redir.c: */
3400 {
3401 clearredir();
3402 }
3403
3404 /* from trap.c: */
3405 {
3406 char *sm;
3407
3408 clear_traps();
3409 for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
3410 if (*sm == S_IGN)
3411 *sm = S_HARD_IGN;
3412 }
3413 }
3414
3415 /* from var.c: */
3416 {
3417 shprocvar();
3418 }
3419}
3420
3421static int preadbuffer(void);
3422static void pushfile (void);
Eric Andersen2870d962001-07-02 17:27:21 +00003423
3424/*
3425 * Read a character from the script, returning PEOF on end of file.
3426 * Nul characters in the input are silently discarded.
3427 */
3428
Eric Andersen3102ac42001-07-06 04:26:23 +00003429#ifndef ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +00003430#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
3431static int
3432pgetc(void)
3433{
3434 return pgetc_macro();
3435}
3436#else
3437static int
3438pgetc_macro(void)
3439{
3440 return --parsenleft >= 0? *parsenextc++ : preadbuffer();
3441}
3442
3443static inline int
3444pgetc(void)
3445{
3446 return pgetc_macro();
3447}
3448#endif
3449
3450
3451/*
3452 * Undo the last call to pgetc. Only one character may be pushed back.
3453 * PEOF may be pushed back.
3454 */
3455
3456static void
3457pungetc() {
3458 parsenleft++;
3459 parsenextc--;
3460}
3461
3462
3463static void
3464popfile(void) {
3465 struct parsefile *pf = parsefile;
3466
3467 INTOFF;
3468 if (pf->fd >= 0)
3469 close(pf->fd);
3470 if (pf->buf)
3471 ckfree(pf->buf);
3472 while (pf->strpush)
3473 popstring();
3474 parsefile = pf->prev;
3475 ckfree(pf);
3476 parsenleft = parsefile->nleft;
3477 parselleft = parsefile->lleft;
3478 parsenextc = parsefile->nextc;
3479 plinno = parsefile->linno;
3480 INTON;
3481}
3482
3483
3484/*
3485 * Return to top level.
3486 */
3487
3488static void
3489popallfiles(void) {
3490 while (parsefile != &basepf)
3491 popfile();
3492}
3493
3494/*
3495 * Close the file(s) that the shell is reading commands from. Called
3496 * after a fork is done.
3497 */
3498
3499static void
3500closescript() {
3501 popallfiles();
3502 if (parsefile->fd > 0) {
3503 close(parsefile->fd);
3504 parsefile->fd = 0;
3505 }
3506}
3507
3508
3509/*
3510 * Like setinputfile, but takes an open file descriptor. Call this with
3511 * interrupts off.
3512 */
3513
3514static void
3515setinputfd(fd, push)
3516 int fd, push;
3517{
3518 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
3519 if (push) {
3520 pushfile();
3521 parsefile->buf = 0;
3522 } else {
3523 closescript();
3524 while (parsefile->strpush)
3525 popstring();
3526 }
3527 parsefile->fd = fd;
3528 if (parsefile->buf == NULL)
3529 parsefile->buf = ckmalloc(BUFSIZ);
3530 parselleft = parsenleft = 0;
3531 plinno = 1;
3532}
3533
3534
3535/*
3536 * Set the input to take input from a file. If push is set, push the
3537 * old input onto the stack first.
3538 */
3539
3540static void
3541setinputfile(const char *fname, int push)
3542{
3543 int fd;
3544 int myfileno2;
3545
3546 INTOFF;
3547 if ((fd = open(fname, O_RDONLY)) < 0)
3548 error("Can't open %s", fname);
3549 if (fd < 10) {
3550 myfileno2 = dup_as_newfd(fd, 10);
3551 close(fd);
3552 if (myfileno2 < 0)
3553 error("Out of file descriptors");
3554 fd = myfileno2;
3555 }
3556 setinputfd(fd, push);
3557 INTON;
3558}
3559
Eric Andersencb57d552001-06-28 07:25:16 +00003560
3561static void
Eric Andersen62483552001-07-10 06:09:16 +00003562tryexec(char *cmd, char **argv, char **envp)
3563{
Eric Andersencb57d552001-06-28 07:25:16 +00003564 int e;
Eric Andersencb57d552001-06-28 07:25:16 +00003565
Eric Andersen3102ac42001-07-06 04:26:23 +00003566#ifdef BB_FEATURE_SH_STANDALONE_SHELL
3567 char *name = cmd;
3568 char** argv_l=argv;
3569 int argc_l;
3570#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
3571 name = get_last_path_component(name);
3572#endif
3573 argv_l=envp;
3574 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
3575 putenv(*argv_l);
3576 argv_l=argv;
Eric Andersen62483552001-07-10 06:09:16 +00003577 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
Eric Andersen3102ac42001-07-06 04:26:23 +00003578 optind = 1;
3579 run_applet_by_name(name, argc_l, argv);
3580#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003581 execve(cmd, argv, envp);
Eric Andersencb57d552001-06-28 07:25:16 +00003582 e = errno;
3583 if (e == ENOEXEC) {
3584 INTOFF;
3585 initshellproc();
3586 setinputfile(cmd, 0);
3587 commandname = arg0 = savestr(argv[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00003588 setparam(argv + 1);
3589 exraise(EXSHELLPROC);
3590 }
3591 errno = e;
3592}
3593
Eric Andersen2870d962001-07-02 17:27:21 +00003594static char *commandtext (const union node *);
Eric Andersencb57d552001-06-28 07:25:16 +00003595
3596/*
3597 * Do a path search. The variable path (passed by reference) should be
3598 * set to the start of the path before the first call; padvance will update
3599 * this value as it proceeds. Successive calls to padvance will return
3600 * the possible path expansions in sequence. If an option (indicated by
3601 * a percent sign) appears in the path entry then the global variable
3602 * pathopt will be set to point to it; otherwise pathopt will be set to
3603 * NULL.
3604 */
3605
3606static const char *pathopt;
3607
Eric Andersen2870d962001-07-02 17:27:21 +00003608static void growstackblock(void);
3609
3610
Eric Andersencb57d552001-06-28 07:25:16 +00003611static char *
Eric Andersen2870d962001-07-02 17:27:21 +00003612padvance(const char **path, const char *name)
3613{
Eric Andersencb57d552001-06-28 07:25:16 +00003614 const char *p;
3615 char *q;
3616 const char *start;
3617 int len;
3618
3619 if (*path == NULL)
3620 return NULL;
3621 start = *path;
3622 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
Eric Andersen2870d962001-07-02 17:27:21 +00003623 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +00003624 while (stackblocksize() < len)
3625 growstackblock();
3626 q = stackblock();
3627 if (p != start) {
3628 memcpy(q, start, p - start);
3629 q += p - start;
3630 *q++ = '/';
3631 }
3632 strcpy(q, name);
3633 pathopt = NULL;
3634 if (*p == '%') {
3635 pathopt = ++p;
3636 while (*p && *p != ':') p++;
3637 }
3638 if (*p == ':')
3639 *path = p + 1;
3640 else
3641 *path = NULL;
3642 return stalloc(len);
3643}
3644
Eric Andersen62483552001-07-10 06:09:16 +00003645/*
3646 * Wrapper around strcmp for qsort/bsearch/...
3647 */
3648static int
3649pstrcmp(const void *a, const void *b)
3650{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003651 return strcmp((const char *) a, (*(const char *const *) b) + 1);
Eric Andersen62483552001-07-10 06:09:16 +00003652}
3653
3654/*
3655 * Find a keyword is in a sorted array.
3656 */
3657
3658static const char *const *
3659findkwd(const char *s)
3660{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003661 return bsearch(s, tokname_array + KWDOFFSET,
3662 (sizeof(tokname_array)/sizeof(const char *)) - KWDOFFSET,
3663 sizeof(const char *), pstrcmp);
Eric Andersen62483552001-07-10 06:09:16 +00003664}
Eric Andersencb57d552001-06-28 07:25:16 +00003665
3666
3667/*** Command hashing code ***/
3668
3669
3670static int
3671hashcmd(argc, argv)
3672 int argc;
3673 char **argv;
3674{
3675 struct tblentry **pp;
3676 struct tblentry *cmdp;
3677 int c;
3678 int verbose;
3679 struct cmdentry entry;
3680 char *name;
Eric Andersen62483552001-07-10 06:09:16 +00003681#ifdef ASH_ALIAS
3682 const struct alias *ap;
3683#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003684
3685 verbose = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003686 while ((c = nextopt("rvV")) != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00003687 if (c == 'r') {
3688 clearcmdentry(0);
3689 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00003690 } else if (c == 'v' || c == 'V') {
3691 verbose = c;
Eric Andersencb57d552001-06-28 07:25:16 +00003692 }
3693 }
3694 if (*argptr == NULL) {
3695 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3696 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3697 if (cmdp->cmdtype != CMDBUILTIN) {
3698 printentry(cmdp, verbose);
3699 }
3700 }
3701 }
3702 return 0;
3703 }
3704 c = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003705 while ((name = *argptr++) != NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00003706 if ((cmdp = cmdlookup(name, 0)) != NULL
3707 && (cmdp->cmdtype == CMDNORMAL
3708 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
3709 delete_cmd_entry();
Eric Andersen62483552001-07-10 06:09:16 +00003710#ifdef ASH_ALIAS
3711 /* Then look at the aliases */
3712 if ((ap = lookupalias(name, 0)) != NULL) {
3713 if (verbose=='v')
3714 printf("%s is an alias for %s\n", name, ap->val);
3715 else
3716 printalias(ap);
3717 continue;
3718 }
3719#endif
3720 /* First look at the keywords */
3721 if (findkwd(name)!=0) {
3722 if (verbose=='v')
3723 printf("%s is a shell keyword\n", name);
3724 else
3725 printf(snlfmt, name);
3726 continue;
3727 }
3728
Eric Andersencb57d552001-06-28 07:25:16 +00003729 find_command(name, &entry, DO_ERR, pathval());
3730 if (entry.cmdtype == CMDUNKNOWN) c = 1;
3731 else if (verbose) {
3732 cmdp = cmdlookup(name, 0);
Eric Andersen62483552001-07-10 06:09:16 +00003733 if (cmdp) printentry(cmdp, verbose=='v');
Eric Andersencb57d552001-06-28 07:25:16 +00003734 flushall();
3735 }
Eric Andersencb57d552001-06-28 07:25:16 +00003736 }
3737 return c;
3738}
3739
Eric Andersencb57d552001-06-28 07:25:16 +00003740static void
3741printentry(cmdp, verbose)
3742 struct tblentry *cmdp;
3743 int verbose;
3744 {
3745 int idx;
3746 const char *path;
3747 char *name;
3748
Eric Andersen62483552001-07-10 06:09:16 +00003749 printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00003750 if (cmdp->cmdtype == CMDNORMAL) {
3751 idx = cmdp->param.index;
3752 path = pathval();
3753 do {
3754 name = padvance(&path, cmdp->cmdname);
3755 stunalloc(name);
3756 } while (--idx >= 0);
Eric Andersen62483552001-07-10 06:09:16 +00003757 if(verbose)
3758 out1str(name);
Eric Andersencb57d552001-06-28 07:25:16 +00003759 } else if (cmdp->cmdtype == CMDBUILTIN) {
Eric Andersen62483552001-07-10 06:09:16 +00003760 if(verbose)
3761 out1str("a shell builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00003762 } else if (cmdp->cmdtype == CMDFUNCTION) {
Eric Andersencb57d552001-06-28 07:25:16 +00003763 if (verbose) {
3764 INTOFF;
Eric Andersen62483552001-07-10 06:09:16 +00003765 out1str("a function\n");
Eric Andersencb57d552001-06-28 07:25:16 +00003766 name = commandtext(cmdp->param.func);
Eric Andersen62483552001-07-10 06:09:16 +00003767 printf("%s() {\n %s\n}", cmdp->cmdname, name);
Eric Andersencb57d552001-06-28 07:25:16 +00003768 ckfree(name);
3769 INTON;
3770 }
3771#ifdef DEBUG
3772 } else {
3773 error("internal error: cmdtype %d", cmdp->cmdtype);
3774#endif
3775 }
Eric Andersen62483552001-07-10 06:09:16 +00003776 printf(snlfmt, cmdp->rehash ? "*" : nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00003777}
3778
3779
3780
Eric Andersen1c039232001-07-07 00:05:55 +00003781/*** List the available builtins ***/
3782
3783
3784static int helpcmd(int argc, char** argv)
3785{
3786 int col, i;
Eric Andersen1c039232001-07-07 00:05:55 +00003787
Eric Andersen62483552001-07-10 06:09:16 +00003788 printf("\nBuilt-in commands:\n-------------------\n");
3789 for (col=0, i=0; i < NUMBUILTINS; i++) {
3790 col += printf("%c%s", ((col == 0) ? '\t' : ' '),
3791 builtincmds[i].name+1);
Eric Andersen1c039232001-07-07 00:05:55 +00003792 if (col > 60) {
3793 printf("\n");
3794 col = 0;
3795 }
3796 }
3797#ifdef BB_FEATURE_SH_STANDALONE_SHELL
3798 {
Eric Andersen1c039232001-07-07 00:05:55 +00003799 extern const struct BB_applet applets[];
3800 extern const size_t NUM_APPLETS;
3801
Eric Andersen62483552001-07-10 06:09:16 +00003802 for (i=0; i < NUM_APPLETS; i++) {
3803
3804 col += printf("%c%s", ((col == 0) ? '\t' : ' '),
3805 applets[i].name);
Eric Andersen1c039232001-07-07 00:05:55 +00003806 if (col > 60) {
3807 printf("\n");
3808 col = 0;
3809 }
3810 }
3811 }
3812#endif
3813 printf("\n\n");
3814 return EXIT_SUCCESS;
3815}
3816
Eric Andersencb57d552001-06-28 07:25:16 +00003817/*
3818 * Resolve a command name. If you change this routine, you may have to
3819 * change the shellexec routine as well.
3820 */
3821
Eric Andersen2870d962001-07-02 17:27:21 +00003822static int prefix (const char *, const char *);
3823
Eric Andersencb57d552001-06-28 07:25:16 +00003824static void
Eric Andersen2870d962001-07-02 17:27:21 +00003825find_command(const char *name, struct cmdentry *entry, int act, const char *path)
Eric Andersencb57d552001-06-28 07:25:16 +00003826{
3827 struct tblentry *cmdp;
3828 int idx;
3829 int prev;
3830 char *fullname;
3831 struct stat statb;
3832 int e;
3833 int bltin;
3834 int firstchange;
3835 int updatetbl;
Eric Andersen62483552001-07-10 06:09:16 +00003836 int regular;
Eric Andersencb57d552001-06-28 07:25:16 +00003837 struct builtincmd *bcmd;
3838
3839 /* If name contains a slash, don't use the hash table */
3840 if (strchr(name, '/') != NULL) {
3841 if (act & DO_ABS) {
3842 while (stat(name, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003843 if (errno != ENOENT && errno != ENOTDIR)
3844 e = errno;
3845 entry->cmdtype = CMDUNKNOWN;
3846 entry->u.index = -1;
3847 return;
3848 }
3849 entry->cmdtype = CMDNORMAL;
3850 entry->u.index = -1;
3851 return;
3852 }
3853 entry->cmdtype = CMDNORMAL;
3854 entry->u.index = 0;
3855 return;
3856 }
3857
3858 updatetbl = 1;
3859 if (act & DO_BRUTE) {
3860 firstchange = path_change(path, &bltin);
3861 } else {
3862 bltin = builtinloc;
3863 firstchange = 9999;
3864 }
3865
3866 /* If name is in the table, and not invalidated by cd, we're done */
3867 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
3868 if (cmdp->cmdtype == CMDFUNCTION) {
3869 if (act & DO_NOFUN) {
3870 updatetbl = 0;
3871 } else {
3872 goto success;
3873 }
3874 } else if (act & DO_BRUTE) {
3875 if ((cmdp->cmdtype == CMDNORMAL &&
3876 cmdp->param.index >= firstchange) ||
3877 (cmdp->cmdtype == CMDBUILTIN &&
3878 ((builtinloc < 0 && bltin >= 0) ?
3879 bltin : builtinloc) >= firstchange)) {
3880 /* need to recompute the entry */
3881 } else {
3882 goto success;
3883 }
3884 } else {
3885 goto success;
3886 }
3887 }
3888
3889 bcmd = find_builtin(name);
Eric Andersen2870d962001-07-02 17:27:21 +00003890 regular = bcmd && IS_BUILTIN_REGULAR(bcmd);
Eric Andersencb57d552001-06-28 07:25:16 +00003891
3892 if (regular) {
3893 if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
Eric Andersen2870d962001-07-02 17:27:21 +00003894 goto success;
Eric Andersencb57d552001-06-28 07:25:16 +00003895 }
3896 } else if (act & DO_BRUTE) {
3897 if (firstchange == 0) {
3898 updatetbl = 0;
3899 }
3900 }
3901
3902 /* If %builtin not in path, check for builtin next */
3903 if (regular || (bltin < 0 && bcmd)) {
3904builtin:
3905 if (!updatetbl) {
3906 entry->cmdtype = CMDBUILTIN;
3907 entry->u.cmd = bcmd;
3908 return;
3909 }
3910 INTOFF;
3911 cmdp = cmdlookup(name, 1);
3912 cmdp->cmdtype = CMDBUILTIN;
3913 cmdp->param.cmd = bcmd;
3914 INTON;
3915 goto success;
3916 }
3917
3918 /* We have to search path. */
Eric Andersen2870d962001-07-02 17:27:21 +00003919 prev = -1; /* where to start */
3920 if (cmdp && cmdp->rehash) { /* doing a rehash */
Eric Andersencb57d552001-06-28 07:25:16 +00003921 if (cmdp->cmdtype == CMDBUILTIN)
3922 prev = builtinloc;
3923 else
3924 prev = cmdp->param.index;
3925 }
3926
3927 e = ENOENT;
3928 idx = -1;
3929loop:
3930 while ((fullname = padvance(&path, name)) != NULL) {
3931 stunalloc(fullname);
3932 idx++;
3933 if (idx >= firstchange) {
3934 updatetbl = 0;
3935 }
3936 if (pathopt) {
3937 if (prefix("builtin", pathopt)) {
3938 if ((bcmd = find_builtin(name))) {
3939 goto builtin;
3940 }
3941 continue;
3942 } else if (!(act & DO_NOFUN) &&
3943 prefix("func", pathopt)) {
3944 /* handled below */
3945 } else {
Eric Andersen2870d962001-07-02 17:27:21 +00003946 continue; /* ignore unimplemented options */
Eric Andersencb57d552001-06-28 07:25:16 +00003947 }
3948 }
3949 /* if rehash, don't redo absolute path names */
3950 if (fullname[0] == '/' && idx <= prev &&
3951 idx < firstchange) {
3952 if (idx < prev)
3953 continue;
3954 TRACE(("searchexec \"%s\": no change\n", name));
3955 goto success;
3956 }
3957 while (stat(fullname, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003958 if (errno != ENOENT && errno != ENOTDIR)
3959 e = errno;
3960 goto loop;
3961 }
Eric Andersen2870d962001-07-02 17:27:21 +00003962 e = EACCES; /* if we fail, this will be the error */
Eric Andersencb57d552001-06-28 07:25:16 +00003963 if (!S_ISREG(statb.st_mode))
3964 continue;
Eric Andersen2870d962001-07-02 17:27:21 +00003965 if (pathopt) { /* this is a %func directory */
Eric Andersencb57d552001-06-28 07:25:16 +00003966 stalloc(strlen(fullname) + 1);
3967 readcmdfile(fullname);
3968 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
3969 error("%s not defined in %s", name, fullname);
3970 stunalloc(fullname);
3971 goto success;
3972 }
Eric Andersencb57d552001-06-28 07:25:16 +00003973 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
3974 /* If we aren't called with DO_BRUTE and cmdp is set, it must
3975 be a function and we're being called with DO_NOFUN */
3976 if (!updatetbl) {
3977 entry->cmdtype = CMDNORMAL;
3978 entry->u.index = idx;
3979 return;
3980 }
3981 INTOFF;
3982 cmdp = cmdlookup(name, 1);
3983 cmdp->cmdtype = CMDNORMAL;
3984 cmdp->param.index = idx;
3985 INTON;
3986 goto success;
3987 }
3988
3989 /* We failed. If there was an entry for this command, delete it */
3990 if (cmdp && updatetbl)
3991 delete_cmd_entry();
3992 if (act & DO_ERR)
Eric Andersen3102ac42001-07-06 04:26:23 +00003993 out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
Eric Andersencb57d552001-06-28 07:25:16 +00003994 entry->cmdtype = CMDUNKNOWN;
3995 return;
3996
3997success:
3998 cmdp->rehash = 0;
3999 entry->cmdtype = cmdp->cmdtype;
4000 entry->u = cmdp->param;
4001}
4002
4003
4004
4005/*
4006 * Search the table of builtin commands.
4007 */
4008
Eric Andersen2870d962001-07-02 17:27:21 +00004009static int
4010bstrcmp(const void *name, const void *b)
4011{
4012 return strcmp((const char *)name, (*(const char *const *) b)+1);
4013}
4014
4015static struct builtincmd *
4016find_builtin(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00004017{
4018 struct builtincmd *bp;
4019
Eric Andersen2870d962001-07-02 17:27:21 +00004020 bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
4021 bstrcmp
Eric Andersencb57d552001-06-28 07:25:16 +00004022 );
4023 return bp;
4024}
4025
4026
4027/*
4028 * Called when a cd is done. Marks all commands so the next time they
4029 * are executed they will be rehashed.
4030 */
4031
4032static void
Eric Andersen2870d962001-07-02 17:27:21 +00004033hashcd(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00004034 struct tblentry **pp;
4035 struct tblentry *cmdp;
4036
4037 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
4038 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4039 if (cmdp->cmdtype == CMDNORMAL
4040 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
4041 cmdp->rehash = 1;
4042 }
4043 }
4044}
4045
4046
4047
4048/*
4049 * Called before PATH is changed. The argument is the new value of PATH;
4050 * pathval() still returns the old value at this point. Called with
4051 * interrupts off.
4052 */
4053
4054static void
Eric Andersen2870d962001-07-02 17:27:21 +00004055changepath(const char *newval)
Eric Andersencb57d552001-06-28 07:25:16 +00004056{
4057 int firstchange;
4058 int bltin;
4059
4060 firstchange = path_change(newval, &bltin);
4061 if (builtinloc < 0 && bltin >= 0)
Eric Andersen2870d962001-07-02 17:27:21 +00004062 builtinloc = bltin; /* zap builtins */
Eric Andersencb57d552001-06-28 07:25:16 +00004063 clearcmdentry(firstchange);
4064 builtinloc = bltin;
4065}
4066
4067
4068/*
4069 * Clear out command entries. The argument specifies the first entry in
4070 * PATH which has changed.
4071 */
4072
4073static void
4074clearcmdentry(firstchange)
4075 int firstchange;
4076{
4077 struct tblentry **tblp;
4078 struct tblentry **pp;
4079 struct tblentry *cmdp;
4080
4081 INTOFF;
4082 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4083 pp = tblp;
4084 while ((cmdp = *pp) != NULL) {
4085 if ((cmdp->cmdtype == CMDNORMAL &&
4086 cmdp->param.index >= firstchange)
4087 || (cmdp->cmdtype == CMDBUILTIN &&
4088 builtinloc >= firstchange)) {
4089 *pp = cmdp->next;
4090 ckfree(cmdp);
4091 } else {
4092 pp = &cmdp->next;
4093 }
4094 }
4095 }
4096 INTON;
4097}
4098
4099
4100/*
4101 * Delete all functions.
4102 */
4103
Eric Andersencb57d552001-06-28 07:25:16 +00004104static void
Eric Andersen2870d962001-07-02 17:27:21 +00004105deletefuncs(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00004106 struct tblentry **tblp;
4107 struct tblentry **pp;
4108 struct tblentry *cmdp;
4109
4110 INTOFF;
4111 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4112 pp = tblp;
4113 while ((cmdp = *pp) != NULL) {
4114 if (cmdp->cmdtype == CMDFUNCTION) {
4115 *pp = cmdp->next;
4116 freefunc(cmdp->param.func);
4117 ckfree(cmdp);
4118 } else {
4119 pp = &cmdp->next;
4120 }
4121 }
4122 }
4123 INTON;
4124}
4125
4126
4127
4128/*
4129 * Locate a command in the command hash table. If "add" is nonzero,
4130 * add the command to the table if it is not already present. The
4131 * variable "lastcmdentry" is set to point to the address of the link
4132 * pointing to the entry, so that delete_cmd_entry can delete the
4133 * entry.
4134 */
4135
Eric Andersen2870d962001-07-02 17:27:21 +00004136static struct tblentry **lastcmdentry;
Eric Andersencb57d552001-06-28 07:25:16 +00004137
4138static struct tblentry *
Eric Andersen2870d962001-07-02 17:27:21 +00004139cmdlookup(const char *name, int add)
Eric Andersencb57d552001-06-28 07:25:16 +00004140{
4141 int hashval;
Eric Andersen2870d962001-07-02 17:27:21 +00004142 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +00004143 struct tblentry *cmdp;
4144 struct tblentry **pp;
4145
4146 p = name;
4147 hashval = *p << 4;
4148 while (*p)
4149 hashval += *p++;
4150 hashval &= 0x7FFF;
4151 pp = &cmdtable[hashval % CMDTABLESIZE];
4152 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4153 if (equal(cmdp->cmdname, name))
4154 break;
4155 pp = &cmdp->next;
4156 }
4157 if (add && cmdp == NULL) {
4158 INTOFF;
4159 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
4160 + strlen(name) + 1);
4161 cmdp->next = NULL;
4162 cmdp->cmdtype = CMDUNKNOWN;
4163 cmdp->rehash = 0;
4164 strcpy(cmdp->cmdname, name);
4165 INTON;
4166 }
4167 lastcmdentry = pp;
4168 return cmdp;
4169}
4170
4171/*
4172 * Delete the command entry returned on the last lookup.
4173 */
4174
4175static void
4176delete_cmd_entry() {
4177 struct tblentry *cmdp;
4178
4179 INTOFF;
4180 cmdp = *lastcmdentry;
4181 *lastcmdentry = cmdp->next;
4182 ckfree(cmdp);
4183 INTON;
4184}
4185
4186
4187
Eric Andersencb57d552001-06-28 07:25:16 +00004188
4189
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004190static const unsigned char nodesize[26] = {
Eric Andersen62483552001-07-10 06:09:16 +00004191 ALIGN(sizeof (struct nbinary)),
4192 ALIGN(sizeof (struct ncmd)),
4193 ALIGN(sizeof (struct npipe)),
4194 ALIGN(sizeof (struct nredir)),
4195 ALIGN(sizeof (struct nredir)),
4196 ALIGN(sizeof (struct nredir)),
4197 ALIGN(sizeof (struct nbinary)),
4198 ALIGN(sizeof (struct nbinary)),
4199 ALIGN(sizeof (struct nif)),
4200 ALIGN(sizeof (struct nbinary)),
4201 ALIGN(sizeof (struct nbinary)),
4202 ALIGN(sizeof (struct nfor)),
4203 ALIGN(sizeof (struct ncase)),
4204 ALIGN(sizeof (struct nclist)),
4205 ALIGN(sizeof (struct narg)),
4206 ALIGN(sizeof (struct narg)),
4207 ALIGN(sizeof (struct nfile)),
4208 ALIGN(sizeof (struct nfile)),
4209 ALIGN(sizeof (struct nfile)),
4210 ALIGN(sizeof (struct nfile)),
4211 ALIGN(sizeof (struct nfile)),
4212 ALIGN(sizeof (struct ndup)),
4213 ALIGN(sizeof (struct ndup)),
4214 ALIGN(sizeof (struct nhere)),
4215 ALIGN(sizeof (struct nhere)),
4216 ALIGN(sizeof (struct nnot)),
4217};
Eric Andersencb57d552001-06-28 07:25:16 +00004218
Eric Andersencb57d552001-06-28 07:25:16 +00004219
4220
4221/*
4222 * Delete a function if it exists.
4223 */
4224
4225static void
Eric Andersen2870d962001-07-02 17:27:21 +00004226unsetfunc(char *name)
4227{
Eric Andersencb57d552001-06-28 07:25:16 +00004228 struct tblentry *cmdp;
4229
4230 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
4231 freefunc(cmdp->param.func);
4232 delete_cmd_entry();
4233 }
4234}
4235
Eric Andersen2870d962001-07-02 17:27:21 +00004236
4237/*
Eric Andersencb57d552001-06-28 07:25:16 +00004238 * Locate and print what a word is...
4239 */
4240
4241static int
Eric Andersen62483552001-07-10 06:09:16 +00004242typecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004243{
4244 int i;
4245 int err = 0;
Eric Andersen62483552001-07-10 06:09:16 +00004246 char *argv_a[2];
4247
4248 argv_a[1] = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004249
4250 for (i = 1; i < argc; i++) {
Eric Andersen62483552001-07-10 06:09:16 +00004251 argv_a[0] = argv[i];
4252 argptr = argv_a;
4253 optptr = "v";
4254 err |= hashcmd(argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00004255 }
4256 return err;
4257}
4258
Eric Andersen2870d962001-07-02 17:27:21 +00004259#ifdef ASH_CMDCMD
Eric Andersencb57d552001-06-28 07:25:16 +00004260static int
4261commandcmd(argc, argv)
4262 int argc;
4263 char **argv;
4264{
4265 int c;
4266 int default_path = 0;
4267 int verify_only = 0;
4268 int verbose_verify_only = 0;
4269
4270 while ((c = nextopt("pvV")) != '\0')
4271 switch (c) {
4272 case 'p':
4273 default_path = 1;
4274 break;
4275 case 'v':
4276 verify_only = 1;
4277 break;
4278 case 'V':
4279 verbose_verify_only = 1;
4280 break;
Eric Andersencb57d552001-06-28 07:25:16 +00004281 }
4282
4283 if (default_path + verify_only + verbose_verify_only > 1 ||
4284 !*argptr) {
Eric Andersen62483552001-07-10 06:09:16 +00004285 out2str(
4286 "command [-p] command [arg ...]\n"
4287 "command {-v|-V} command\n");
Eric Andersencb57d552001-06-28 07:25:16 +00004288 return EX_USAGE;
4289 }
4290
Eric Andersencb57d552001-06-28 07:25:16 +00004291 if (verify_only || verbose_verify_only) {
Eric Andersen62483552001-07-10 06:09:16 +00004292 char *argv_a[2];
4293
4294 argv_a[1] = 0;
4295 argv_a[0] = *argptr;
4296 argptr = argv_a;
4297 optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
4298 return hashcmd(argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00004299 }
Eric Andersencb57d552001-06-28 07:25:16 +00004300
4301 return 0;
4302}
Eric Andersen2870d962001-07-02 17:27:21 +00004303#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004304
4305static int
4306path_change(newval, bltin)
4307 const char *newval;
4308 int *bltin;
4309{
4310 const char *old, *new;
4311 int idx;
4312 int firstchange;
4313
4314 old = pathval();
4315 new = newval;
Eric Andersen2870d962001-07-02 17:27:21 +00004316 firstchange = 9999; /* assume no change */
Eric Andersencb57d552001-06-28 07:25:16 +00004317 idx = 0;
4318 *bltin = -1;
4319 for (;;) {
4320 if (*old != *new) {
4321 firstchange = idx;
4322 if ((*old == '\0' && *new == ':')
4323 || (*old == ':' && *new == '\0'))
4324 firstchange++;
Eric Andersen2870d962001-07-02 17:27:21 +00004325 old = new; /* ignore subsequent differences */
Eric Andersencb57d552001-06-28 07:25:16 +00004326 }
4327 if (*new == '\0')
4328 break;
4329 if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
4330 *bltin = idx;
4331 if (*new == ':') {
4332 idx++;
4333 }
4334 new++, old++;
4335 }
4336 if (builtinloc >= 0 && *bltin < 0)
4337 firstchange = 0;
4338 return firstchange;
4339}
Eric Andersencb57d552001-06-28 07:25:16 +00004340/*
4341 * Routines to expand arguments to commands. We have to deal with
4342 * backquotes, shell variables, and file metacharacters.
4343 */
4344/*
4345 * _rmescape() flags
4346 */
Eric Andersen2870d962001-07-02 17:27:21 +00004347#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4348#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Eric Andersencb57d552001-06-28 07:25:16 +00004349
4350/*
4351 * Structure specifying which parts of the string should be searched
4352 * for IFS characters.
4353 */
4354
4355struct ifsregion {
Eric Andersen2870d962001-07-02 17:27:21 +00004356 struct ifsregion *next; /* next region in list */
4357 int begoff; /* offset of start of region */
4358 int endoff; /* offset of end of region */
4359 int nulonly; /* search for nul bytes only */
Eric Andersencb57d552001-06-28 07:25:16 +00004360};
4361
4362
Eric Andersen2870d962001-07-02 17:27:21 +00004363static char *expdest; /* output of current string */
4364static struct nodelist *argbackq; /* list of back quote expressions */
4365static struct ifsregion ifsfirst; /* first struct in list of ifs regions */
4366static struct ifsregion *ifslastp; /* last struct in list */
4367static struct arglist exparg; /* holds expanded arg list */
Eric Andersencb57d552001-06-28 07:25:16 +00004368
Eric Andersen2870d962001-07-02 17:27:21 +00004369static void argstr (char *, int);
4370static char *exptilde (char *, int);
4371static void expbackq (union node *, int, int);
4372static int subevalvar (char *, char *, int, int, int, int, int);
Eric Andersen2870d962001-07-02 17:27:21 +00004373static int varisset (char *, int);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004374static void strtodest (const char *, int, int);
Eric Andersen2870d962001-07-02 17:27:21 +00004375static void varvalue (char *, int, int);
4376static void recordregion (int, int, int);
4377static void removerecordregions (int);
4378static void ifsbreakup (char *, struct arglist *);
4379static void ifsfree (void);
4380static void expandmeta (struct strlist *, int);
Eric Andersen62483552001-07-10 06:09:16 +00004381#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00004382#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
4383#if !defined(GLOB_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00004384static void addglob (const glob_t *);
Eric Andersencb57d552001-06-28 07:25:16 +00004385#endif
4386#endif
Eric Andersen62483552001-07-10 06:09:16 +00004387#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersen2870d962001-07-02 17:27:21 +00004388static void expmeta (char *, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004389#endif
Eric Andersen62483552001-07-10 06:09:16 +00004390#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersen2870d962001-07-02 17:27:21 +00004391static struct strlist *expsort (struct strlist *);
4392static struct strlist *msort (struct strlist *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004393#endif
Eric Andersen2870d962001-07-02 17:27:21 +00004394static int patmatch (char *, char *, int);
Eric Andersen62483552001-07-10 06:09:16 +00004395#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00004396static int patmatch2 (char *, char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004397#else
Eric Andersen2870d962001-07-02 17:27:21 +00004398static int pmatch (char *, char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004399#define patmatch2 patmatch
4400#endif
Eric Andersen2870d962001-07-02 17:27:21 +00004401static char *cvtnum (int, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004402
4403/*
4404 * Expand shell variables and backquotes inside a here document.
4405 */
4406
Eric Andersen2870d962001-07-02 17:27:21 +00004407/* arg: the document, fd: where to write the expanded version */
Eric Andersen62483552001-07-10 06:09:16 +00004408static inline void
Eric Andersen2870d962001-07-02 17:27:21 +00004409expandhere(union node *arg, int fd)
4410{
Eric Andersencb57d552001-06-28 07:25:16 +00004411 herefd = fd;
4412 expandarg(arg, (struct arglist *)NULL, 0);
4413 xwrite(fd, stackblock(), expdest - stackblock());
4414}
4415
4416
4417/*
4418 * Perform variable substitution and command substitution on an argument,
4419 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4420 * perform splitting and file name expansion. When arglist is NULL, perform
4421 * here document expansion.
4422 */
4423
4424static void
4425expandarg(arg, arglist, flag)
4426 union node *arg;
4427 struct arglist *arglist;
4428 int flag;
4429{
4430 struct strlist *sp;
4431 char *p;
4432
4433 argbackq = arg->narg.backquote;
4434 STARTSTACKSTR(expdest);
4435 ifsfirst.next = NULL;
4436 ifslastp = NULL;
4437 argstr(arg->narg.text, flag);
4438 if (arglist == NULL) {
Eric Andersen2870d962001-07-02 17:27:21 +00004439 return; /* here document expanded */
Eric Andersencb57d552001-06-28 07:25:16 +00004440 }
4441 STPUTC('\0', expdest);
4442 p = grabstackstr(expdest);
4443 exparg.lastp = &exparg.list;
4444 /*
4445 * TODO - EXP_REDIR
4446 */
4447 if (flag & EXP_FULL) {
4448 ifsbreakup(p, &exparg);
4449 *exparg.lastp = NULL;
4450 exparg.lastp = &exparg.list;
4451 expandmeta(exparg.list, flag);
4452 } else {
4453 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
4454 rmescapes(p);
4455 sp = (struct strlist *)stalloc(sizeof (struct strlist));
4456 sp->text = p;
4457 *exparg.lastp = sp;
4458 exparg.lastp = &sp->next;
4459 }
4460 ifsfree();
4461 *exparg.lastp = NULL;
4462 if (exparg.list) {
4463 *arglist->lastp = exparg.list;
4464 arglist->lastp = exparg.lastp;
4465 }
4466}
4467
4468
Eric Andersen62483552001-07-10 06:09:16 +00004469/*
4470 * Expand a variable, and return a pointer to the next character in the
4471 * input string.
4472 */
4473
4474static inline char *
4475evalvar(p, flag)
4476 char *p;
4477 int flag;
4478{
4479 int subtype;
4480 int varflags;
4481 char *var;
4482 const char *val;
4483 int patloc;
4484 int c;
4485 int set;
4486 int special;
4487 int startloc;
4488 int varlen;
4489 int easy;
4490 int quotes = flag & (EXP_FULL | EXP_CASE);
4491
4492 varflags = *p++;
4493 subtype = varflags & VSTYPE;
4494 var = p;
4495 special = 0;
4496 if (! is_name(*p))
4497 special = 1;
4498 p = strchr(p, '=') + 1;
4499again: /* jump here after setting a variable with ${var=text} */
4500 if (special) {
4501 set = varisset(var, varflags & VSNUL);
4502 val = NULL;
4503 } else {
4504 val = lookupvar(var);
4505 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
4506 val = NULL;
4507 set = 0;
4508 } else
4509 set = 1;
4510 }
4511 varlen = 0;
4512 startloc = expdest - stackblock();
4513 if (set && subtype != VSPLUS) {
4514 /* insert the value of the variable */
4515 if (special) {
4516 varvalue(var, varflags & VSQUOTE, flag);
4517 if (subtype == VSLENGTH) {
4518 varlen = expdest - stackblock() - startloc;
4519 STADJUST(-varlen, expdest);
4520 }
4521 } else {
4522 if (subtype == VSLENGTH) {
4523 varlen = strlen(val);
4524 } else {
4525 strtodest(
4526 val,
4527 varflags & VSQUOTE ?
4528 DQSYNTAX : BASESYNTAX,
4529 quotes
4530 );
4531 }
4532 }
4533 }
4534
4535 if (subtype == VSPLUS)
4536 set = ! set;
4537
4538 easy = ((varflags & VSQUOTE) == 0 ||
4539 (*var == '@' && shellparam.nparam != 1));
4540
4541
4542 switch (subtype) {
4543 case VSLENGTH:
4544 expdest = cvtnum(varlen, expdest);
4545 goto record;
4546
4547 case VSNORMAL:
4548 if (!easy)
4549 break;
4550record:
4551 recordregion(startloc, expdest - stackblock(),
4552 varflags & VSQUOTE);
4553 break;
4554
4555 case VSPLUS:
4556 case VSMINUS:
4557 if (!set) {
4558 argstr(p, flag);
4559 break;
4560 }
4561 if (easy)
4562 goto record;
4563 break;
4564
4565 case VSTRIMLEFT:
4566 case VSTRIMLEFTMAX:
4567 case VSTRIMRIGHT:
4568 case VSTRIMRIGHTMAX:
4569 if (!set)
4570 break;
4571 /*
4572 * Terminate the string and start recording the pattern
4573 * right after it
4574 */
4575 STPUTC('\0', expdest);
4576 patloc = expdest - stackblock();
4577 if (subevalvar(p, NULL, patloc, subtype,
4578 startloc, varflags, quotes) == 0) {
4579 int amount = (expdest - stackblock() - patloc) + 1;
4580 STADJUST(-amount, expdest);
4581 }
4582 /* Remove any recorded regions beyond start of variable */
4583 removerecordregions(startloc);
4584 goto record;
4585
4586 case VSASSIGN:
4587 case VSQUESTION:
4588 if (!set) {
4589 if (subevalvar(p, var, 0, subtype, startloc,
4590 varflags, quotes)) {
4591 varflags &= ~VSNUL;
4592 /*
4593 * Remove any recorded regions beyond
4594 * start of variable
4595 */
4596 removerecordregions(startloc);
4597 goto again;
4598 }
4599 break;
4600 }
4601 if (easy)
4602 goto record;
4603 break;
4604
4605#ifdef DEBUG
4606 default:
4607 abort();
4608#endif
4609 }
4610
4611 if (subtype != VSNORMAL) { /* skip to end of alternative */
4612 int nesting = 1;
4613 for (;;) {
4614 if ((c = *p++) == CTLESC)
4615 p++;
4616 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
4617 if (set)
4618 argbackq = argbackq->next;
4619 } else if (c == CTLVAR) {
4620 if ((*p++ & VSTYPE) != VSNORMAL)
4621 nesting++;
4622 } else if (c == CTLENDVAR) {
4623 if (--nesting == 0)
4624 break;
4625 }
4626 }
4627 }
4628 return p;
4629}
4630
Eric Andersencb57d552001-06-28 07:25:16 +00004631
4632/*
4633 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4634 * characters to allow for further processing. Otherwise treat
4635 * $@ like $* since no splitting will be performed.
4636 */
4637
4638static void
4639argstr(p, flag)
4640 char *p;
4641 int flag;
4642{
4643 char c;
Eric Andersen2870d962001-07-02 17:27:21 +00004644 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
Eric Andersencb57d552001-06-28 07:25:16 +00004645 int firsteq = 1;
4646
4647 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
4648 p = exptilde(p, flag);
4649 for (;;) {
4650 switch (c = *p++) {
4651 case '\0':
4652 case CTLENDVAR: /* ??? */
4653 goto breakloop;
4654 case CTLQUOTEMARK:
4655 /* "$@" syntax adherence hack */
4656 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
4657 break;
4658 if ((flag & EXP_FULL) != 0)
4659 STPUTC(c, expdest);
4660 break;
4661 case CTLESC:
4662 if (quotes)
4663 STPUTC(c, expdest);
4664 c = *p++;
4665 STPUTC(c, expdest);
4666 break;
4667 case CTLVAR:
4668 p = evalvar(p, flag);
4669 break;
4670 case CTLBACKQ:
4671 case CTLBACKQ|CTLQUOTE:
4672 expbackq(argbackq->n, c & CTLQUOTE, flag);
4673 argbackq = argbackq->next;
4674 break;
4675#ifdef ASH_MATH_SUPPORT
4676 case CTLENDARI:
4677 expari(flag);
4678 break;
Eric Andersen2870d962001-07-02 17:27:21 +00004679#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004680 case ':':
4681 case '=':
4682 /*
4683 * sort of a hack - expand tildes in variable
4684 * assignments (after the first '=' and after ':'s).
4685 */
4686 STPUTC(c, expdest);
4687 if (flag & EXP_VARTILDE && *p == '~') {
4688 if (c == '=') {
4689 if (firsteq)
4690 firsteq = 0;
4691 else
4692 break;
4693 }
4694 p = exptilde(p, flag);
4695 }
4696 break;
4697 default:
4698 STPUTC(c, expdest);
4699 }
4700 }
4701breakloop:;
4702 return;
4703}
4704
4705static char *
4706exptilde(p, flag)
4707 char *p;
4708 int flag;
4709{
4710 char c, *startp = p;
4711 struct passwd *pw;
4712 const char *home;
4713 int quotes = flag & (EXP_FULL | EXP_CASE);
4714
4715 while ((c = *p) != '\0') {
4716 switch(c) {
4717 case CTLESC:
4718 return (startp);
4719 case CTLQUOTEMARK:
4720 return (startp);
4721 case ':':
4722 if (flag & EXP_VARTILDE)
4723 goto done;
4724 break;
4725 case '/':
4726 goto done;
4727 }
4728 p++;
4729 }
4730done:
4731 *p = '\0';
4732 if (*(startp+1) == '\0') {
4733 if ((home = lookupvar("HOME")) == NULL)
4734 goto lose;
4735 } else {
4736 if ((pw = getpwnam(startp+1)) == NULL)
4737 goto lose;
4738 home = pw->pw_dir;
4739 }
4740 if (*home == '\0')
4741 goto lose;
4742 *p = c;
4743 strtodest(home, SQSYNTAX, quotes);
4744 return (p);
4745lose:
4746 *p = c;
4747 return (startp);
4748}
4749
4750
Eric Andersen2870d962001-07-02 17:27:21 +00004751static void
4752removerecordregions(int endoff)
Eric Andersencb57d552001-06-28 07:25:16 +00004753{
4754 if (ifslastp == NULL)
4755 return;
4756
4757 if (ifsfirst.endoff > endoff) {
4758 while (ifsfirst.next != NULL) {
4759 struct ifsregion *ifsp;
4760 INTOFF;
4761 ifsp = ifsfirst.next->next;
4762 ckfree(ifsfirst.next);
4763 ifsfirst.next = ifsp;
4764 INTON;
4765 }
4766 if (ifsfirst.begoff > endoff)
4767 ifslastp = NULL;
4768 else {
4769 ifslastp = &ifsfirst;
4770 ifsfirst.endoff = endoff;
4771 }
4772 return;
4773 }
Eric Andersen2870d962001-07-02 17:27:21 +00004774
Eric Andersencb57d552001-06-28 07:25:16 +00004775 ifslastp = &ifsfirst;
4776 while (ifslastp->next && ifslastp->next->begoff < endoff)
4777 ifslastp=ifslastp->next;
4778 while (ifslastp->next != NULL) {
4779 struct ifsregion *ifsp;
4780 INTOFF;
4781 ifsp = ifslastp->next->next;
4782 ckfree(ifslastp->next);
4783 ifslastp->next = ifsp;
4784 INTON;
4785 }
4786 if (ifslastp->endoff > endoff)
4787 ifslastp->endoff = endoff;
4788}
4789
4790
4791#ifdef ASH_MATH_SUPPORT
4792/*
4793 * Expand arithmetic expression. Backup to start of expression,
4794 * evaluate, place result in (backed up) result, adjust string position.
4795 */
4796static void
Eric Andersen2870d962001-07-02 17:27:21 +00004797expari(int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004798{
4799 char *p, *start;
Eric Andersen34506362001-08-02 05:02:46 +00004800 int errcode;
Eric Andersencb57d552001-06-28 07:25:16 +00004801 int result;
4802 int begoff;
4803 int quotes = flag & (EXP_FULL | EXP_CASE);
4804 int quoted;
4805
Eric Andersen2870d962001-07-02 17:27:21 +00004806 /* ifsfree(); */
Eric Andersencb57d552001-06-28 07:25:16 +00004807
4808 /*
4809 * This routine is slightly over-complicated for
4810 * efficiency. First we make sure there is
4811 * enough space for the result, which may be bigger
4812 * than the expression if we add exponentation. Next we
4813 * scan backwards looking for the start of arithmetic. If the
4814 * next previous character is a CTLESC character, then we
4815 * have to rescan starting from the beginning since CTLESC
4816 * characters have to be processed left to right.
4817 */
4818 CHECKSTRSPACE(10, expdest);
4819 USTPUTC('\0', expdest);
4820 start = stackblock();
4821 p = expdest - 1;
4822 while (*p != CTLARI && p >= start)
4823 --p;
4824 if (*p != CTLARI)
4825 error("missing CTLARI (shouldn't happen)");
4826 if (p > start && *(p-1) == CTLESC)
4827 for (p = start; *p != CTLARI; p++)
4828 if (*p == CTLESC)
4829 p++;
4830
4831 if (p[1] == '"')
4832 quoted=1;
4833 else
4834 quoted=0;
4835 begoff = p - start;
4836 removerecordregions(begoff);
4837 if (quotes)
4838 rmescapes(p+2);
Eric Andersen34506362001-08-02 05:02:46 +00004839 result = arith(p+2, &errcode);
4840 if (errcode < 0) {
4841 if(errcode == -2)
4842 error("divide by zero");
4843 else
4844 error("syntax error: \"%s\"\n", p+2);
4845 }
Eric Andersen3102ac42001-07-06 04:26:23 +00004846 snprintf(p, 12, "%d", result);
Eric Andersencb57d552001-06-28 07:25:16 +00004847
4848 while (*p++)
4849 ;
4850
4851 if (quoted == 0)
4852 recordregion(begoff, p - 1 - start, 0);
4853 result = expdest - p + 1;
4854 STADJUST(-result, expdest);
4855}
Eric Andersen2870d962001-07-02 17:27:21 +00004856#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004857
4858/*
4859 * Expand stuff in backwards quotes.
4860 */
4861
4862static void
4863expbackq(cmd, quoted, flag)
4864 union node *cmd;
4865 int quoted;
4866 int flag;
4867{
4868 volatile struct backcmd in;
4869 int i;
4870 char buf[128];
4871 char *p;
4872 char *dest = expdest;
4873 volatile struct ifsregion saveifs;
4874 struct ifsregion *volatile savelastp;
4875 struct nodelist *volatile saveargbackq;
4876 char lastc;
4877 int startloc = dest - stackblock();
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004878 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Eric Andersencb57d552001-06-28 07:25:16 +00004879 volatile int saveherefd;
4880 int quotes = flag & (EXP_FULL | EXP_CASE);
4881 struct jmploc jmploc;
4882 struct jmploc *volatile savehandler;
4883 int ex;
4884
4885#if __GNUC__
4886 /* Avoid longjmp clobbering */
4887 (void) &dest;
4888 (void) &syntax;
4889#endif
4890
4891 in.fd = -1;
4892 in.buf = 0;
4893 in.jp = 0;
4894
4895 INTOFF;
4896 saveifs = ifsfirst;
4897 savelastp = ifslastp;
4898 saveargbackq = argbackq;
4899 saveherefd = herefd;
4900 herefd = -1;
4901 if ((ex = setjmp(jmploc.loc))) {
4902 goto err1;
4903 }
4904 savehandler = handler;
4905 handler = &jmploc;
4906 INTON;
4907 p = grabstackstr(dest);
4908 evalbackcmd(cmd, (struct backcmd *) &in);
4909 ungrabstackstr(p, dest);
4910err1:
4911 INTOFF;
4912 ifsfirst = saveifs;
4913 ifslastp = savelastp;
4914 argbackq = saveargbackq;
4915 herefd = saveherefd;
4916 if (ex) {
4917 goto err2;
4918 }
4919
4920 p = in.buf;
4921 lastc = '\0';
4922 for (;;) {
4923 if (--in.nleft < 0) {
4924 if (in.fd < 0)
4925 break;
Eric Andersen7467c8d2001-07-12 20:26:32 +00004926 i = safe_read(in.fd, buf, sizeof buf);
Eric Andersencb57d552001-06-28 07:25:16 +00004927 TRACE(("expbackq: read returns %d\n", i));
4928 if (i <= 0)
4929 break;
4930 p = buf;
4931 in.nleft = i - 1;
4932 }
4933 lastc = *p++;
4934 if (lastc != '\0') {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004935 if (quotes && SIT(lastc, syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00004936 STPUTC(CTLESC, dest);
4937 STPUTC(lastc, dest);
4938 }
4939 }
4940
4941 /* Eat all trailing newlines */
4942 for (; dest > stackblock() && dest[-1] == '\n';)
4943 STUNPUTC(dest);
4944
4945err2:
4946 if (in.fd >= 0)
4947 close(in.fd);
4948 if (in.buf)
4949 ckfree(in.buf);
4950 if (in.jp)
4951 exitstatus = waitforjob(in.jp);
4952 handler = savehandler;
4953 if (ex) {
4954 longjmp(handler->loc, 1);
4955 }
4956 if (quoted == 0)
4957 recordregion(startloc, dest - stackblock(), 0);
4958 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4959 (dest - stackblock()) - startloc,
4960 (dest - stackblock()) - startloc,
4961 stackblock() + startloc));
4962 expdest = dest;
4963 INTON;
4964}
4965
Eric Andersencb57d552001-06-28 07:25:16 +00004966static int
4967subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
4968 char *p;
4969 char *str;
4970 int strloc;
4971 int subtype;
4972 int startloc;
4973 int varflags;
4974 int quotes;
4975{
4976 char *startp;
4977 char *loc = NULL;
4978 char *q;
4979 int c = 0;
4980 int saveherefd = herefd;
4981 struct nodelist *saveargbackq = argbackq;
4982 int amount;
4983
4984 herefd = -1;
4985 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
4986 STACKSTRNUL(expdest);
4987 herefd = saveherefd;
4988 argbackq = saveargbackq;
4989 startp = stackblock() + startloc;
4990 if (str == NULL)
4991 str = stackblock() + strloc;
4992
4993 switch (subtype) {
4994 case VSASSIGN:
4995 setvar(str, startp, 0);
4996 amount = startp - expdest;
4997 STADJUST(amount, expdest);
4998 varflags &= ~VSNUL;
4999 if (c != 0)
5000 *loc = c;
5001 return 1;
5002
5003 case VSQUESTION:
5004 if (*p != CTLENDVAR) {
Eric Andersen3102ac42001-07-06 04:26:23 +00005005 out2fmt(snlfmt, startp);
Eric Andersencb57d552001-06-28 07:25:16 +00005006 error((char *)NULL);
5007 }
5008 error("%.*s: parameter %snot set", p - str - 1,
5009 str, (varflags & VSNUL) ? "null or "
5010 : nullstr);
5011 /* NOTREACHED */
5012
5013 case VSTRIMLEFT:
5014 for (loc = startp; loc < str; loc++) {
5015 c = *loc;
5016 *loc = '\0';
5017 if (patmatch2(str, startp, quotes))
5018 goto recordleft;
5019 *loc = c;
5020 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00005021 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00005022 }
5023 return 0;
5024
5025 case VSTRIMLEFTMAX:
5026 for (loc = str - 1; loc >= startp;) {
5027 c = *loc;
5028 *loc = '\0';
5029 if (patmatch2(str, startp, quotes))
5030 goto recordleft;
5031 *loc = c;
5032 loc--;
5033 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
5034 for (q = startp; q < loc; q++)
5035 if (*q == CTLESC)
5036 q++;
5037 if (q > loc)
5038 loc--;
5039 }
5040 }
5041 return 0;
5042
5043 case VSTRIMRIGHT:
Eric Andersen2870d962001-07-02 17:27:21 +00005044 for (loc = str - 1; loc >= startp;) {
Eric Andersencb57d552001-06-28 07:25:16 +00005045 if (patmatch2(str, loc, quotes))
5046 goto recordright;
5047 loc--;
Eric Andersen2870d962001-07-02 17:27:21 +00005048 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
Eric Andersencb57d552001-06-28 07:25:16 +00005049 for (q = startp; q < loc; q++)
5050 if (*q == CTLESC)
5051 q++;
5052 if (q > loc)
5053 loc--;
5054 }
5055 }
5056 return 0;
5057
5058 case VSTRIMRIGHTMAX:
5059 for (loc = startp; loc < str - 1; loc++) {
5060 if (patmatch2(str, loc, quotes))
5061 goto recordright;
5062 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00005063 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00005064 }
5065 return 0;
5066
5067#ifdef DEBUG
5068 default:
5069 abort();
5070#endif
5071 }
5072
5073recordleft:
5074 *loc = c;
5075 amount = ((str - 1) - (loc - startp)) - expdest;
5076 STADJUST(amount, expdest);
5077 while (loc != str - 1)
5078 *startp++ = *loc++;
5079 return 1;
5080
5081recordright:
5082 amount = loc - expdest;
5083 STADJUST(amount, expdest);
5084 STPUTC('\0', expdest);
5085 STADJUST(-1, expdest);
5086 return 1;
5087}
5088
5089
5090/*
Eric Andersencb57d552001-06-28 07:25:16 +00005091 * Test whether a specialized variable is set.
5092 */
5093
5094static int
5095varisset(name, nulok)
5096 char *name;
5097 int nulok;
5098{
5099 if (*name == '!')
5100 return backgndpid != -1;
5101 else if (*name == '@' || *name == '*') {
5102 if (*shellparam.p == NULL)
5103 return 0;
5104
5105 if (nulok) {
5106 char **av;
5107
5108 for (av = shellparam.p; *av; av++)
5109 if (**av != '\0')
5110 return 1;
5111 return 0;
5112 }
5113 } else if (is_digit(*name)) {
5114 char *ap;
5115 int num = atoi(name);
5116
5117 if (num > shellparam.nparam)
5118 return 0;
5119
5120 if (num == 0)
5121 ap = arg0;
5122 else
5123 ap = shellparam.p[num - 1];
5124
5125 if (nulok && (ap == NULL || *ap == '\0'))
5126 return 0;
5127 }
5128 return 1;
5129}
5130
Eric Andersencb57d552001-06-28 07:25:16 +00005131/*
5132 * Put a string on the stack.
5133 */
5134
5135static void
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005136strtodest(const char *p, int syntax, int quotes)
Eric Andersencb57d552001-06-28 07:25:16 +00005137{
5138 while (*p) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005139 if (quotes && SIT(*p,syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00005140 STPUTC(CTLESC, expdest);
5141 STPUTC(*p++, expdest);
5142 }
5143}
5144
Eric Andersencb57d552001-06-28 07:25:16 +00005145/*
5146 * Add the value of a specialized variable to the stack string.
5147 */
5148
5149static void
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005150varvalue(char *name, int quoted, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00005151{
5152 int num;
5153 char *p;
5154 int i;
5155 int sep;
5156 int sepq = 0;
5157 char **ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005158 int syntax;
Eric Andersencb57d552001-06-28 07:25:16 +00005159 int allow_split = flags & EXP_FULL;
5160 int quotes = flags & (EXP_FULL | EXP_CASE);
5161
5162 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5163 switch (*name) {
5164 case '$':
5165 num = rootpid;
5166 goto numvar;
5167 case '?':
5168 num = oexitstatus;
5169 goto numvar;
5170 case '#':
5171 num = shellparam.nparam;
5172 goto numvar;
5173 case '!':
5174 num = backgndpid;
5175numvar:
5176 expdest = cvtnum(num, expdest);
5177 break;
5178 case '-':
5179 for (i = 0 ; i < NOPTS ; i++) {
Eric Andersen2870d962001-07-02 17:27:21 +00005180 if (optent_val(i))
5181 STPUTC(optent_letter(optlist[i]), expdest);
Eric Andersencb57d552001-06-28 07:25:16 +00005182 }
5183 break;
5184 case '@':
5185 if (allow_split && quoted) {
5186 sep = 1 << CHAR_BIT;
5187 goto param;
5188 }
5189 /* fall through */
5190 case '*':
5191 sep = ifsset() ? ifsval()[0] : ' ';
5192 if (quotes) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005193 sepq = SIT(sep,syntax) == CCTL;
Eric Andersencb57d552001-06-28 07:25:16 +00005194 }
5195param:
5196 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
5197 strtodest(p, syntax, quotes);
5198 if (*ap && sep) {
5199 if (sepq)
5200 STPUTC(CTLESC, expdest);
5201 STPUTC(sep, expdest);
5202 }
5203 }
5204 break;
5205 case '0':
5206 strtodest(arg0, syntax, quotes);
5207 break;
5208 default:
5209 num = atoi(name);
5210 if (num > 0 && num <= shellparam.nparam) {
5211 strtodest(shellparam.p[num - 1], syntax, quotes);
5212 }
5213 break;
5214 }
5215}
5216
5217
Eric Andersencb57d552001-06-28 07:25:16 +00005218/*
5219 * Record the fact that we have to scan this region of the
5220 * string for IFS characters.
5221 */
5222
5223static void
5224recordregion(start, end, nulonly)
5225 int start;
5226 int end;
5227 int nulonly;
5228{
5229 struct ifsregion *ifsp;
5230
5231 if (ifslastp == NULL) {
5232 ifsp = &ifsfirst;
5233 } else {
5234 INTOFF;
5235 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
5236 ifsp->next = NULL;
5237 ifslastp->next = ifsp;
5238 INTON;
5239 }
5240 ifslastp = ifsp;
5241 ifslastp->begoff = start;
5242 ifslastp->endoff = end;
5243 ifslastp->nulonly = nulonly;
5244}
5245
5246
5247
5248/*
5249 * Break the argument string into pieces based upon IFS and add the
5250 * strings to the argument list. The regions of the string to be
5251 * searched for IFS characters have been stored by recordregion.
5252 */
5253static void
5254ifsbreakup(string, arglist)
5255 char *string;
5256 struct arglist *arglist;
5257 {
5258 struct ifsregion *ifsp;
5259 struct strlist *sp;
5260 char *start;
5261 char *p;
5262 char *q;
5263 const char *ifs, *realifs;
5264 int ifsspc;
5265 int nulonly;
5266
5267
5268 start = string;
5269 ifsspc = 0;
5270 nulonly = 0;
5271 realifs = ifsset() ? ifsval() : defifs;
5272 if (ifslastp != NULL) {
5273 ifsp = &ifsfirst;
5274 do {
5275 p = string + ifsp->begoff;
5276 nulonly = ifsp->nulonly;
5277 ifs = nulonly ? nullstr : realifs;
5278 ifsspc = 0;
5279 while (p < string + ifsp->endoff) {
5280 q = p;
5281 if (*p == CTLESC)
5282 p++;
5283 if (strchr(ifs, *p)) {
5284 if (!nulonly)
5285 ifsspc = (strchr(defifs, *p) != NULL);
5286 /* Ignore IFS whitespace at start */
5287 if (q == start && ifsspc) {
5288 p++;
5289 start = p;
5290 continue;
5291 }
5292 *q = '\0';
5293 sp = (struct strlist *)stalloc(sizeof *sp);
5294 sp->text = start;
5295 *arglist->lastp = sp;
5296 arglist->lastp = &sp->next;
5297 p++;
5298 if (!nulonly) {
5299 for (;;) {
5300 if (p >= string + ifsp->endoff) {
5301 break;
5302 }
5303 q = p;
5304 if (*p == CTLESC)
5305 p++;
5306 if (strchr(ifs, *p) == NULL ) {
5307 p = q;
5308 break;
5309 } else if (strchr(defifs, *p) == NULL) {
5310 if (ifsspc) {
5311 p++;
5312 ifsspc = 0;
5313 } else {
5314 p = q;
5315 break;
5316 }
5317 } else
5318 p++;
5319 }
5320 }
5321 start = p;
5322 } else
5323 p++;
5324 }
5325 } while ((ifsp = ifsp->next) != NULL);
5326 if (!(*start || (!ifsspc && start > string && nulonly))) {
5327 return;
5328 }
5329 }
5330
5331 sp = (struct strlist *)stalloc(sizeof *sp);
5332 sp->text = start;
5333 *arglist->lastp = sp;
5334 arglist->lastp = &sp->next;
5335}
5336
5337static void
5338ifsfree()
5339{
5340 while (ifsfirst.next != NULL) {
5341 struct ifsregion *ifsp;
5342 INTOFF;
5343 ifsp = ifsfirst.next->next;
5344 ckfree(ifsfirst.next);
5345 ifsfirst.next = ifsp;
5346 INTON;
5347 }
5348 ifslastp = NULL;
5349 ifsfirst.next = NULL;
5350}
5351
Eric Andersen2870d962001-07-02 17:27:21 +00005352/*
5353 * Add a file name to the list.
5354 */
Eric Andersencb57d552001-06-28 07:25:16 +00005355
Eric Andersen2870d962001-07-02 17:27:21 +00005356static void
5357addfname(const char *name)
5358{
5359 char *p;
5360 struct strlist *sp;
5361
5362 p = sstrdup(name);
5363 sp = (struct strlist *)stalloc(sizeof *sp);
5364 sp->text = p;
5365 *exparg.lastp = sp;
5366 exparg.lastp = &sp->next;
5367}
Eric Andersencb57d552001-06-28 07:25:16 +00005368
5369/*
5370 * Expand shell metacharacters. At this point, the only control characters
5371 * should be escapes. The results are stored in the list exparg.
5372 */
5373
Eric Andersen62483552001-07-10 06:09:16 +00005374#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00005375static void
5376expandmeta(str, flag)
5377 struct strlist *str;
5378 int flag;
5379{
5380 const char *p;
5381 glob_t pglob;
5382 /* TODO - EXP_REDIR */
5383
5384 while (str) {
5385 if (fflag)
5386 goto nometa;
5387 p = preglob(str->text);
5388 INTOFF;
Eric Andersen34506362001-08-02 05:02:46 +00005389 switch (glob(p, 0, 0, &pglob)) {
Eric Andersencb57d552001-06-28 07:25:16 +00005390 case 0:
Eric Andersen34506362001-08-02 05:02:46 +00005391 if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0]))
Eric Andersencb57d552001-06-28 07:25:16 +00005392 goto nometa2;
5393 addglob(&pglob);
5394 globfree(&pglob);
5395 INTON;
5396 break;
5397 case GLOB_NOMATCH:
5398nometa2:
5399 globfree(&pglob);
5400 INTON;
5401nometa:
5402 *exparg.lastp = str;
5403 rmescapes(str->text);
5404 exparg.lastp = &str->next;
5405 break;
Eric Andersen2870d962001-07-02 17:27:21 +00005406 default: /* GLOB_NOSPACE */
Eric Andersencb57d552001-06-28 07:25:16 +00005407 error("Out of space");
5408 }
5409 str = str->next;
5410 }
5411}
5412
5413
5414/*
5415 * Add the result of glob(3) to the list.
5416 */
5417
5418static void
5419addglob(pglob)
5420 const glob_t *pglob;
5421{
5422 char **p = pglob->gl_pathv;
5423
5424 do {
5425 addfname(*p);
5426 } while (*++p);
5427}
5428
5429
Eric Andersen2870d962001-07-02 17:27:21 +00005430#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005431static char *expdir;
5432
5433
5434static void
5435expandmeta(str, flag)
5436 struct strlist *str;
5437 int flag;
5438{
5439 char *p;
5440 struct strlist **savelastp;
5441 struct strlist *sp;
5442 char c;
5443 /* TODO - EXP_REDIR */
5444
5445 while (str) {
5446 if (fflag)
5447 goto nometa;
5448 p = str->text;
Eric Andersen2870d962001-07-02 17:27:21 +00005449 for (;;) { /* fast check for meta chars */
Eric Andersencb57d552001-06-28 07:25:16 +00005450 if ((c = *p++) == '\0')
5451 goto nometa;
5452 if (c == '*' || c == '?' || c == '[' || c == '!')
5453 break;
5454 }
5455 savelastp = exparg.lastp;
5456 INTOFF;
5457 if (expdir == NULL) {
5458 int i = strlen(str->text);
5459 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
5460 }
5461
5462 expmeta(expdir, str->text);
5463 ckfree(expdir);
5464 expdir = NULL;
5465 INTON;
5466 if (exparg.lastp == savelastp) {
5467 /*
5468 * no matches
5469 */
5470nometa:
5471 *exparg.lastp = str;
5472 rmescapes(str->text);
5473 exparg.lastp = &str->next;
5474 } else {
5475 *exparg.lastp = NULL;
5476 *savelastp = sp = expsort(*savelastp);
5477 while (sp->next != NULL)
5478 sp = sp->next;
5479 exparg.lastp = &sp->next;
5480 }
5481 str = str->next;
5482 }
5483}
5484
5485
5486/*
5487 * Do metacharacter (i.e. *, ?, [...]) expansion.
5488 */
5489
5490static void
5491expmeta(enddir, name)
5492 char *enddir;
5493 char *name;
5494 {
5495 char *p;
5496 const char *cp;
5497 char *q;
5498 char *start;
5499 char *endname;
5500 int metaflag;
5501 struct stat statb;
5502 DIR *dirp;
5503 struct dirent *dp;
5504 int atend;
5505 int matchdot;
5506
5507 metaflag = 0;
5508 start = name;
5509 for (p = name ; ; p++) {
5510 if (*p == '*' || *p == '?')
5511 metaflag = 1;
5512 else if (*p == '[') {
5513 q = p + 1;
5514 if (*q == '!')
5515 q++;
5516 for (;;) {
5517 while (*q == CTLQUOTEMARK)
5518 q++;
5519 if (*q == CTLESC)
5520 q++;
5521 if (*q == '/' || *q == '\0')
5522 break;
5523 if (*++q == ']') {
5524 metaflag = 1;
5525 break;
5526 }
5527 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005528 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
Eric Andersencb57d552001-06-28 07:25:16 +00005529 metaflag = 1;
5530 } else if (*p == '\0')
5531 break;
5532 else if (*p == CTLQUOTEMARK)
5533 continue;
5534 else if (*p == CTLESC)
5535 p++;
5536 if (*p == '/') {
5537 if (metaflag)
5538 break;
5539 start = p + 1;
5540 }
5541 }
Eric Andersen2870d962001-07-02 17:27:21 +00005542 if (metaflag == 0) { /* we've reached the end of the file name */
Eric Andersencb57d552001-06-28 07:25:16 +00005543 if (enddir != expdir)
5544 metaflag++;
5545 for (p = name ; ; p++) {
5546 if (*p == CTLQUOTEMARK)
5547 continue;
5548 if (*p == CTLESC)
5549 p++;
5550 *enddir++ = *p;
5551 if (*p == '\0')
5552 break;
5553 }
5554 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
5555 addfname(expdir);
5556 return;
5557 }
5558 endname = p;
5559 if (start != name) {
5560 p = name;
5561 while (p < start) {
5562 while (*p == CTLQUOTEMARK)
5563 p++;
5564 if (*p == CTLESC)
5565 p++;
5566 *enddir++ = *p++;
5567 }
5568 }
5569 if (enddir == expdir) {
5570 cp = ".";
5571 } else if (enddir == expdir + 1 && *expdir == '/') {
5572 cp = "/";
5573 } else {
5574 cp = expdir;
5575 enddir[-1] = '\0';
5576 }
5577 if ((dirp = opendir(cp)) == NULL)
5578 return;
5579 if (enddir != expdir)
5580 enddir[-1] = '/';
5581 if (*endname == 0) {
5582 atend = 1;
5583 } else {
5584 atend = 0;
5585 *endname++ = '\0';
5586 }
5587 matchdot = 0;
5588 p = start;
5589 while (*p == CTLQUOTEMARK)
5590 p++;
5591 if (*p == CTLESC)
5592 p++;
5593 if (*p == '.')
5594 matchdot++;
5595 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
5596 if (dp->d_name[0] == '.' && ! matchdot)
5597 continue;
5598 if (patmatch(start, dp->d_name, 0)) {
5599 if (atend) {
Eric Andersen2870d962001-07-02 17:27:21 +00005600 strcpy(enddir, dp->d_name);
Eric Andersencb57d552001-06-28 07:25:16 +00005601 addfname(expdir);
5602 } else {
5603 for (p = enddir, cp = dp->d_name;
5604 (*p++ = *cp++) != '\0';)
5605 continue;
5606 p[-1] = '/';
5607 expmeta(p, endname);
5608 }
5609 }
5610 }
5611 closedir(dirp);
5612 if (! atend)
5613 endname[-1] = '/';
5614}
Eric Andersen2870d962001-07-02 17:27:21 +00005615#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005616
5617
Eric Andersencb57d552001-06-28 07:25:16 +00005618
Eric Andersen62483552001-07-10 06:09:16 +00005619#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersencb57d552001-06-28 07:25:16 +00005620/*
5621 * Sort the results of file name expansion. It calculates the number of
5622 * strings to sort and then calls msort (short for merge sort) to do the
5623 * work.
5624 */
5625
5626static struct strlist *
5627expsort(str)
5628 struct strlist *str;
5629 {
5630 int len;
5631 struct strlist *sp;
5632
5633 len = 0;
5634 for (sp = str ; sp ; sp = sp->next)
5635 len++;
5636 return msort(str, len);
5637}
5638
5639
5640static struct strlist *
5641msort(list, len)
5642 struct strlist *list;
5643 int len;
5644{
5645 struct strlist *p, *q = NULL;
5646 struct strlist **lpp;
5647 int half;
5648 int n;
5649
5650 if (len <= 1)
5651 return list;
5652 half = len >> 1;
5653 p = list;
5654 for (n = half ; --n >= 0 ; ) {
5655 q = p;
5656 p = p->next;
5657 }
Eric Andersen2870d962001-07-02 17:27:21 +00005658 q->next = NULL; /* terminate first half of list */
5659 q = msort(list, half); /* sort first half of list */
5660 p = msort(p, len - half); /* sort second half */
Eric Andersencb57d552001-06-28 07:25:16 +00005661 lpp = &list;
5662 for (;;) {
5663 if (strcmp(p->text, q->text) < 0) {
5664 *lpp = p;
5665 lpp = &p->next;
5666 if ((p = *lpp) == NULL) {
5667 *lpp = q;
5668 break;
5669 }
5670 } else {
5671 *lpp = q;
5672 lpp = &q->next;
5673 if ((q = *lpp) == NULL) {
5674 *lpp = p;
5675 break;
5676 }
5677 }
5678 }
5679 return list;
5680}
5681#endif
5682
5683
5684
5685/*
5686 * Returns true if the pattern matches the string.
5687 */
5688
Eric Andersen62483552001-07-10 06:09:16 +00005689#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00005690/* squoted: string might have quote chars */
Eric Andersencb57d552001-06-28 07:25:16 +00005691static int
Eric Andersen2870d962001-07-02 17:27:21 +00005692patmatch(char *pattern, char *string, int squoted)
5693{
Eric Andersencb57d552001-06-28 07:25:16 +00005694 const char *p;
5695 char *q;
5696
5697 p = preglob(pattern);
5698 q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string;
5699
5700 return !fnmatch(p, q, 0);
5701}
5702
5703
5704static int
Eric Andersen2870d962001-07-02 17:27:21 +00005705patmatch2(char *pattern, char *string, int squoted)
5706{
Eric Andersencb57d552001-06-28 07:25:16 +00005707 char *p;
5708 int res;
5709
5710 sstrnleft--;
5711 p = grabstackstr(expdest);
5712 res = patmatch(pattern, string, squoted);
5713 ungrabstackstr(p, expdest);
5714 return res;
5715}
5716#else
5717static int
Eric Andersen2870d962001-07-02 17:27:21 +00005718patmatch(char *pattern, char *string, int squoted) {
5719 return pmatch(pattern, string, squoted);
Eric Andersencb57d552001-06-28 07:25:16 +00005720}
5721
5722
5723static int
Eric Andersen2870d962001-07-02 17:27:21 +00005724pmatch(char *pattern, char *string, int squoted)
5725{
Eric Andersencb57d552001-06-28 07:25:16 +00005726 char *p, *q;
5727 char c;
5728
5729 p = pattern;
5730 q = string;
5731 for (;;) {
5732 switch (c = *p++) {
5733 case '\0':
5734 goto breakloop;
5735 case CTLESC:
5736 if (squoted && *q == CTLESC)
5737 q++;
5738 if (*q++ != *p++)
5739 return 0;
5740 break;
5741 case CTLQUOTEMARK:
5742 continue;
5743 case '?':
5744 if (squoted && *q == CTLESC)
5745 q++;
5746 if (*q++ == '\0')
5747 return 0;
5748 break;
5749 case '*':
5750 c = *p;
5751 while (c == CTLQUOTEMARK || c == '*')
5752 c = *++p;
5753 if (c != CTLESC && c != CTLQUOTEMARK &&
5754 c != '?' && c != '*' && c != '[') {
5755 while (*q != c) {
5756 if (squoted && *q == CTLESC &&
5757 q[1] == c)
5758 break;
5759 if (*q == '\0')
5760 return 0;
5761 if (squoted && *q == CTLESC)
5762 q++;
5763 q++;
5764 }
5765 }
5766 do {
5767 if (pmatch(p, q, squoted))
5768 return 1;
5769 if (squoted && *q == CTLESC)
5770 q++;
5771 } while (*q++ != '\0');
5772 return 0;
5773 case '[': {
5774 char *endp;
5775 int invert, found;
5776 char chr;
5777
5778 endp = p;
5779 if (*endp == '!')
5780 endp++;
5781 for (;;) {
5782 while (*endp == CTLQUOTEMARK)
5783 endp++;
5784 if (*endp == '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00005785 goto dft; /* no matching ] */
Eric Andersencb57d552001-06-28 07:25:16 +00005786 if (*endp == CTLESC)
5787 endp++;
5788 if (*++endp == ']')
5789 break;
5790 }
5791 invert = 0;
5792 if (*p == '!') {
5793 invert++;
5794 p++;
5795 }
5796 found = 0;
5797 chr = *q++;
5798 if (squoted && chr == CTLESC)
5799 chr = *q++;
5800 if (chr == '\0')
5801 return 0;
5802 c = *p++;
5803 do {
5804 if (c == CTLQUOTEMARK)
5805 continue;
5806 if (c == CTLESC)
5807 c = *p++;
5808 if (*p == '-' && p[1] != ']') {
5809 p++;
5810 while (*p == CTLQUOTEMARK)
5811 p++;
5812 if (*p == CTLESC)
5813 p++;
5814 if (chr >= c && chr <= *p)
5815 found = 1;
5816 p++;
5817 } else {
5818 if (chr == c)
5819 found = 1;
5820 }
5821 } while ((c = *p++) != ']');
5822 if (found == invert)
5823 return 0;
5824 break;
5825 }
Eric Andersen2870d962001-07-02 17:27:21 +00005826dft: default:
Eric Andersencb57d552001-06-28 07:25:16 +00005827 if (squoted && *q == CTLESC)
5828 q++;
5829 if (*q++ != c)
5830 return 0;
5831 break;
5832 }
5833 }
5834breakloop:
5835 if (*q != '\0')
5836 return 0;
5837 return 1;
5838}
5839#endif
5840
5841
5842
5843/*
5844 * Remove any CTLESC characters from a string.
5845 */
5846
Eric Andersen62483552001-07-10 06:09:16 +00005847#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00005848static char *
Eric Andersen2870d962001-07-02 17:27:21 +00005849_rmescapes(char *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005850{
5851 char *p, *q, *r;
5852 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5853
5854 p = strpbrk(str, qchars);
5855 if (!p) {
5856 return str;
5857 }
5858 q = p;
5859 r = str;
5860 if (flag & RMESCAPE_ALLOC) {
5861 size_t len = p - str;
5862 q = r = stalloc(strlen(p) + len + 1);
5863 if (len > 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00005864 memcpy(q, str, len);
5865 q += len;
Eric Andersencb57d552001-06-28 07:25:16 +00005866 }
5867 }
5868 while (*p) {
5869 if (*p == CTLQUOTEMARK) {
5870 p++;
5871 continue;
5872 }
5873 if (*p == CTLESC) {
5874 p++;
5875 if (flag & RMESCAPE_GLOB && *p != '/') {
5876 *q++ = '\\';
5877 }
5878 }
5879 *q++ = *p++;
5880 }
5881 *q = '\0';
5882 return r;
5883}
5884#else
5885static void
5886rmescapes(str)
5887 char *str;
5888{
5889 char *p, *q;
5890
5891 p = str;
5892 while (*p != CTLESC && *p != CTLQUOTEMARK) {
5893 if (*p++ == '\0')
5894 return;
5895 }
5896 q = p;
5897 while (*p) {
5898 if (*p == CTLQUOTEMARK) {
5899 p++;
5900 continue;
5901 }
5902 if (*p == CTLESC)
5903 p++;
5904 *q++ = *p++;
5905 }
5906 *q = '\0';
5907}
5908#endif
5909
5910
5911
5912/*
5913 * See if a pattern matches in a case statement.
5914 */
5915
5916static int
Eric Andersen2870d962001-07-02 17:27:21 +00005917casematch(union node *pattern, const char *val)
5918{
Eric Andersencb57d552001-06-28 07:25:16 +00005919 struct stackmark smark;
5920 int result;
5921 char *p;
5922
5923 setstackmark(&smark);
5924 argbackq = pattern->narg.backquote;
5925 STARTSTACKSTR(expdest);
5926 ifslastp = NULL;
5927 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5928 STPUTC('\0', expdest);
5929 p = grabstackstr(expdest);
Eric Andersen2870d962001-07-02 17:27:21 +00005930 result = patmatch(p, (char *)val, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00005931 popstackmark(&smark);
5932 return result;
5933}
5934
5935/*
5936 * Our own itoa().
5937 */
5938
5939static char *
5940cvtnum(num, buf)
5941 int num;
5942 char *buf;
5943 {
5944 int len;
5945
5946 CHECKSTRSPACE(32, buf);
5947 len = sprintf(buf, "%d", num);
5948 STADJUST(len, buf);
5949 return buf;
5950}
Eric Andersencb57d552001-06-28 07:25:16 +00005951/*
5952 * Editline and history functions (and glue).
5953 */
5954static int histcmd(argc, argv)
5955 int argc;
5956 char **argv;
5957{
5958 error("not compiled with history support");
5959 /* NOTREACHED */
5960}
5961
5962
Eric Andersencb57d552001-06-28 07:25:16 +00005963struct redirtab {
5964 struct redirtab *next;
Russ Dill4db35642001-07-26 05:58:40 +00005965 short renamed[10]; /* Current ash support only 0-9 descriptors */
Eric Andersen34506362001-08-02 05:02:46 +00005966 /* char on arm (and others) can't be negative */
Eric Andersencb57d552001-06-28 07:25:16 +00005967};
5968
Eric Andersen2870d962001-07-02 17:27:21 +00005969static struct redirtab *redirlist;
Eric Andersencb57d552001-06-28 07:25:16 +00005970
5971extern char **environ;
5972
5973
5974
5975/*
5976 * Initialization code.
5977 */
5978
5979static void
Eric Andersen2870d962001-07-02 17:27:21 +00005980init(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00005981
5982 /* from cd.c: */
5983 {
5984 setpwd(0, 0);
5985 }
5986
5987 /* from input.c: */
5988 {
5989 basepf.nextc = basepf.buf = basebuf;
5990 }
5991
Eric Andersencb57d552001-06-28 07:25:16 +00005992 /* from var.c: */
5993 {
5994 char **envp;
5995 char ppid[32];
5996
5997 initvar();
5998 for (envp = environ ; *envp ; envp++) {
5999 if (strchr(*envp, '=')) {
6000 setvareq(*envp, VEXPORT|VTEXTFIXED);
6001 }
6002 }
6003
Eric Andersen3102ac42001-07-06 04:26:23 +00006004 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
Eric Andersencb57d552001-06-28 07:25:16 +00006005 setvar("PPID", ppid, 0);
6006 }
6007}
6008
6009
6010
6011/*
6012 * This routine is called when an error or an interrupt occurs in an
6013 * interactive shell and control is returned to the main command loop.
6014 */
6015
Eric Andersen2870d962001-07-02 17:27:21 +00006016/* 1 == check for aliases, 2 == also check for assignments */
Eric Andersen7467c8d2001-07-12 20:26:32 +00006017static int checkalias; /* also used in no alias mode for check assignments */
Eric Andersen2870d962001-07-02 17:27:21 +00006018
Eric Andersencb57d552001-06-28 07:25:16 +00006019static void
Eric Andersen2870d962001-07-02 17:27:21 +00006020reset(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00006021
6022 /* from eval.c: */
6023 {
6024 evalskip = 0;
6025 loopnest = 0;
6026 funcnest = 0;
6027 }
6028
6029 /* from input.c: */
6030 {
6031 if (exception != EXSHELLPROC)
Eric Andersen2870d962001-07-02 17:27:21 +00006032 parselleft = parsenleft = 0; /* clear input buffer */
Eric Andersencb57d552001-06-28 07:25:16 +00006033 popallfiles();
6034 }
6035
6036 /* from parser.c: */
6037 {
6038 tokpushback = 0;
6039 checkkwd = 0;
6040 checkalias = 0;
6041 }
6042
6043 /* from redir.c: */
6044 {
6045 while (redirlist)
6046 popredir();
6047 }
6048
Eric Andersencb57d552001-06-28 07:25:16 +00006049}
6050
6051
6052
6053/*
Eric Andersencb57d552001-06-28 07:25:16 +00006054 * This file implements the input routines used by the parser.
6055 */
6056
6057#ifdef BB_FEATURE_COMMAND_EDITING
Eric Andersencb57d552001-06-28 07:25:16 +00006058static const char * cmdedit_prompt;
6059static inline void putprompt(const char *s) {
6060 cmdedit_prompt = s;
6061}
6062#else
6063static inline void putprompt(const char *s) {
6064 out2str(s);
6065}
6066#endif
6067
Eric Andersen2870d962001-07-02 17:27:21 +00006068#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00006069
Eric Andersencb57d552001-06-28 07:25:16 +00006070
Eric Andersencb57d552001-06-28 07:25:16 +00006071
Eric Andersen2870d962001-07-02 17:27:21 +00006072/*
6073 * Same as pgetc(), but ignores PEOA.
6074 */
Eric Andersencb57d552001-06-28 07:25:16 +00006075
Eric Andersen2870d962001-07-02 17:27:21 +00006076#ifdef ASH_ALIAS
6077static int
6078pgetc2()
6079{
6080 int c;
6081 do {
6082 c = pgetc_macro();
6083 } while (c == PEOA);
6084 return c;
Eric Andersencb57d552001-06-28 07:25:16 +00006085}
Eric Andersen2870d962001-07-02 17:27:21 +00006086#else
6087static inline int pgetc2() { return pgetc_macro(); }
Eric Andersencb57d552001-06-28 07:25:16 +00006088#endif
6089
Eric Andersencb57d552001-06-28 07:25:16 +00006090/*
6091 * Read a line from the script.
6092 */
6093
Eric Andersen62483552001-07-10 06:09:16 +00006094static inline char *
Eric Andersen2870d962001-07-02 17:27:21 +00006095pfgets(char *line, int len)
Eric Andersencb57d552001-06-28 07:25:16 +00006096{
6097 char *p = line;
6098 int nleft = len;
6099 int c;
6100
6101 while (--nleft > 0) {
6102 c = pgetc2();
6103 if (c == PEOF) {
6104 if (p == line)
6105 return NULL;
6106 break;
6107 }
6108 *p++ = c;
6109 if (c == '\n')
6110 break;
6111 }
6112 *p = '\0';
6113 return line;
6114}
6115
Eric Andersen62483552001-07-10 06:09:16 +00006116static inline int
Eric Andersen2870d962001-07-02 17:27:21 +00006117preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006118{
6119 int nr;
6120 char *buf = parsefile->buf;
6121 parsenextc = buf;
6122
6123retry:
6124#ifdef BB_FEATURE_COMMAND_EDITING
6125 {
Eric Andersen34506362001-08-02 05:02:46 +00006126 if (!iflag || parsefile->fd)
Eric Andersen7467c8d2001-07-12 20:26:32 +00006127 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersen2870d962001-07-02 17:27:21 +00006128 else {
Eric Andersen044228d2001-07-17 01:12:36 +00006129 nr = cmdedit_read_input((char*)cmdedit_prompt, buf);
Eric Andersencb57d552001-06-28 07:25:16 +00006130 }
6131 }
6132#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00006133 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00006134#endif
6135
6136 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006137 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6138 int flags = fcntl(0, F_GETFL, 0);
6139 if (flags >= 0 && flags & O_NONBLOCK) {
6140 flags &=~ O_NONBLOCK;
6141 if (fcntl(0, F_SETFL, flags) >= 0) {
6142 out2str("sh: turning off NDELAY mode\n");
6143 goto retry;
6144 }
6145 }
6146 }
6147 }
6148 return nr;
6149}
6150
Eric Andersen2870d962001-07-02 17:27:21 +00006151static void
6152popstring(void)
6153{
6154 struct strpush *sp = parsefile->strpush;
6155
6156 INTOFF;
6157#ifdef ASH_ALIAS
6158 if (sp->ap) {
6159 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6160 if (!checkalias) {
6161 checkalias = 1;
6162 }
6163 }
6164 if (sp->string != sp->ap->val) {
6165 ckfree(sp->string);
6166 }
6167
6168 sp->ap->flag &= ~ALIASINUSE;
6169 if (sp->ap->flag & ALIASDEAD) {
6170 unalias(sp->ap->name);
6171 }
6172 }
6173#endif
6174 parsenextc = sp->prevstring;
6175 parsenleft = sp->prevnleft;
6176/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6177 parsefile->strpush = sp->prev;
6178 if (sp != &(parsefile->basestrpush))
6179 ckfree(sp);
6180 INTON;
6181}
6182
6183
Eric Andersencb57d552001-06-28 07:25:16 +00006184/*
6185 * Refill the input buffer and return the next input character:
6186 *
6187 * 1) If a string was pushed back on the input, pop it;
6188 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6189 * from a string so we can't refill the buffer, return EOF.
6190 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6191 * 4) Process input up to the next newline, deleting nul characters.
6192 */
6193
6194static int
Eric Andersen2870d962001-07-02 17:27:21 +00006195preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006196{
6197 char *p, *q;
6198 int more;
6199 char savec;
6200
6201 while (parsefile->strpush) {
Eric Andersen2870d962001-07-02 17:27:21 +00006202#ifdef ASH_ALIAS
6203 if (parsenleft == -1 && parsefile->strpush->ap &&
6204 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00006205 return PEOA;
6206 }
Eric Andersen2870d962001-07-02 17:27:21 +00006207#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006208 popstring();
6209 if (--parsenleft >= 0)
6210 return (*parsenextc++);
6211 }
6212 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6213 return PEOF;
Eric Andersen3102ac42001-07-06 04:26:23 +00006214 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00006215
6216again:
6217 if (parselleft <= 0) {
6218 if ((parselleft = preadfd()) <= 0) {
6219 parselleft = parsenleft = EOF_NLEFT;
6220 return PEOF;
6221 }
6222 }
6223
6224 q = p = parsenextc;
6225
6226 /* delete nul characters */
6227 for (more = 1; more;) {
6228 switch (*p) {
6229 case '\0':
Eric Andersen2870d962001-07-02 17:27:21 +00006230 p++; /* Skip nul */
Eric Andersencb57d552001-06-28 07:25:16 +00006231 goto check;
6232
6233
6234 case '\n':
6235 parsenleft = q - parsenextc;
6236 more = 0; /* Stop processing here */
6237 break;
6238 }
6239
6240 *q++ = *p++;
6241check:
6242 if (--parselleft <= 0 && more) {
6243 parsenleft = q - parsenextc - 1;
6244 if (parsenleft < 0)
6245 goto again;
6246 more = 0;
6247 }
6248 }
6249
6250 savec = *q;
6251 *q = '\0';
6252
6253 if (vflag) {
6254 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00006255 }
6256
6257 *q = savec;
6258
6259 return *parsenextc++;
6260}
6261
Eric Andersencb57d552001-06-28 07:25:16 +00006262
6263/*
6264 * Push a string back onto the input at this current parsefile level.
6265 * We handle aliases this way.
6266 */
6267static void
Eric Andersen2870d962001-07-02 17:27:21 +00006268pushstring(char *s, int len, void *ap)
6269{
Eric Andersencb57d552001-06-28 07:25:16 +00006270 struct strpush *sp;
6271
6272 INTOFF;
6273/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6274 if (parsefile->strpush) {
6275 sp = ckmalloc(sizeof (struct strpush));
6276 sp->prev = parsefile->strpush;
6277 parsefile->strpush = sp;
6278 } else
6279 sp = parsefile->strpush = &(parsefile->basestrpush);
6280 sp->prevstring = parsenextc;
6281 sp->prevnleft = parsenleft;
Eric Andersen2870d962001-07-02 17:27:21 +00006282#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00006283 sp->ap = (struct alias *)ap;
6284 if (ap) {
6285 ((struct alias *)ap)->flag |= ALIASINUSE;
6286 sp->string = s;
6287 }
Eric Andersen2870d962001-07-02 17:27:21 +00006288#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006289 parsenextc = s;
6290 parsenleft = len;
6291 INTON;
6292}
6293
Eric Andersencb57d552001-06-28 07:25:16 +00006294
Eric Andersencb57d552001-06-28 07:25:16 +00006295/*
6296 * Like setinputfile, but takes input from a string.
6297 */
6298
6299static void
Eric Andersen62483552001-07-10 06:09:16 +00006300setinputstring(char *string)
6301{
Eric Andersencb57d552001-06-28 07:25:16 +00006302 INTOFF;
6303 pushfile();
6304 parsenextc = string;
6305 parsenleft = strlen(string);
6306 parsefile->buf = NULL;
6307 plinno = 1;
6308 INTON;
6309}
6310
6311
6312
6313/*
6314 * To handle the "." command, a stack of input files is used. Pushfile
6315 * adds a new entry to the stack and popfile restores the previous level.
6316 */
6317
6318static void
Eric Andersen2870d962001-07-02 17:27:21 +00006319pushfile(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00006320 struct parsefile *pf;
6321
6322 parsefile->nleft = parsenleft;
6323 parsefile->lleft = parselleft;
6324 parsefile->nextc = parsenextc;
6325 parsefile->linno = plinno;
6326 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
6327 pf->prev = parsefile;
6328 pf->fd = -1;
6329 pf->strpush = NULL;
6330 pf->basestrpush.prev = NULL;
6331 parsefile = pf;
6332}
6333
Eric Andersen2870d962001-07-02 17:27:21 +00006334#ifdef JOBS
6335static void restartjob (struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00006336#endif
Eric Andersen2870d962001-07-02 17:27:21 +00006337static void freejob (struct job *);
6338static struct job *getjob (const char *);
6339static int dowait (int, struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00006340static void waitonint(int);
6341
6342
Eric Andersen2870d962001-07-02 17:27:21 +00006343/*
6344 * We keep track of whether or not fd0 has been redirected. This is for
6345 * background commands, where we want to redirect fd0 to /dev/null only
6346 * if it hasn't already been redirected.
6347*/
6348static int fd0_redirected = 0;
6349
6350/* Return true if fd 0 has already been redirected at least once. */
6351static inline int
6352fd0_redirected_p () {
6353 return fd0_redirected != 0;
6354}
6355
Eric Andersen62483552001-07-10 06:09:16 +00006356static void dupredirect (const union node *, int, int fd1dup);
Eric Andersen2870d962001-07-02 17:27:21 +00006357
6358#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006359/*
6360 * Turn job control on and off.
6361 *
6362 * Note: This code assumes that the third arg to ioctl is a character
6363 * pointer, which is true on Berkeley systems but not System V. Since
6364 * System V doesn't have job control yet, this isn't a problem now.
6365 */
6366
Eric Andersen2870d962001-07-02 17:27:21 +00006367
Eric Andersencb57d552001-06-28 07:25:16 +00006368
6369static void setjobctl(int enable)
6370{
6371#ifdef OLD_TTY_DRIVER
6372 int ldisc;
6373#endif
6374
6375 if (enable == jobctl || rootshell == 0)
6376 return;
6377 if (enable) {
6378 do { /* while we are in the background */
6379#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006380 if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006381#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006382 initialpgrp = tcgetpgrp(2);
Eric Andersencb57d552001-06-28 07:25:16 +00006383 if (initialpgrp < 0) {
6384#endif
Eric Andersen8c145dc2001-07-10 16:57:09 +00006385 out2str("sh: can't access tty; job control turned off\n");
Eric Andersencb57d552001-06-28 07:25:16 +00006386 mflag = 0;
6387 return;
6388 }
6389 if (initialpgrp == -1)
6390 initialpgrp = getpgrp();
6391 else if (initialpgrp != getpgrp()) {
6392 killpg(initialpgrp, SIGTTIN);
6393 continue;
6394 }
6395 } while (0);
6396#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006397 if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
Eric Andersen8c145dc2001-07-10 16:57:09 +00006398 out2str("sh: need new tty driver to run job control; job control turned off\n");
Eric Andersencb57d552001-06-28 07:25:16 +00006399 mflag = 0;
6400 return;
6401 }
6402#endif
6403 setsignal(SIGTSTP);
6404 setsignal(SIGTTOU);
6405 setsignal(SIGTTIN);
6406 setpgid(0, rootpid);
6407#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006408 ioctl(2, TIOCSPGRP, (char *)&rootpid);
Eric Andersencb57d552001-06-28 07:25:16 +00006409#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006410 tcsetpgrp(2, rootpid);
Eric Andersencb57d552001-06-28 07:25:16 +00006411#endif
Eric Andersen8c145dc2001-07-10 16:57:09 +00006412 } else { /* turning job control off */
Eric Andersencb57d552001-06-28 07:25:16 +00006413 setpgid(0, initialpgrp);
6414#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006415 ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006416#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006417 tcsetpgrp(2, initialpgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006418#endif
6419 setsignal(SIGTSTP);
6420 setsignal(SIGTTOU);
6421 setsignal(SIGTTIN);
6422 }
6423 jobctl = enable;
6424}
6425#endif
6426
6427
Eric Andersen2870d962001-07-02 17:27:21 +00006428#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006429static int
6430killcmd(argc, argv)
6431 int argc;
6432 char **argv;
6433{
6434 int signo = -1;
6435 int list = 0;
6436 int i;
6437 pid_t pid;
6438 struct job *jp;
6439
6440 if (argc <= 1) {
6441usage:
6442 error(
6443"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6444"kill -l [exitstatus]"
6445 );
6446 }
6447
6448 if (*argv[1] == '-') {
6449 signo = decode_signal(argv[1] + 1, 1);
6450 if (signo < 0) {
6451 int c;
6452
6453 while ((c = nextopt("ls:")) != '\0')
6454 switch (c) {
6455 case 'l':
6456 list = 1;
6457 break;
6458 case 's':
6459 signo = decode_signal(optionarg, 1);
6460 if (signo < 0) {
6461 error(
6462 "invalid signal number or name: %s",
6463 optionarg
6464 );
6465 }
Eric Andersen2870d962001-07-02 17:27:21 +00006466 break;
Eric Andersencb57d552001-06-28 07:25:16 +00006467#ifdef DEBUG
6468 default:
6469 error(
6470 "nextopt returned character code 0%o", c);
6471#endif
6472 }
6473 } else
6474 argptr++;
6475 }
6476
6477 if (!list && signo < 0)
6478 signo = SIGTERM;
6479
6480 if ((signo < 0 || !*argptr) ^ list) {
6481 goto usage;
6482 }
6483
6484 if (list) {
Eric Andersen34506362001-08-02 05:02:46 +00006485 const char *name;
6486
Eric Andersencb57d552001-06-28 07:25:16 +00006487 if (!*argptr) {
6488 out1str("0\n");
6489 for (i = 1; i < NSIG; i++) {
Eric Andersen34506362001-08-02 05:02:46 +00006490 name = u_signal_names(0, &i, 1);
6491 if(name)
6492 printf(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00006493 }
6494 return 0;
6495 }
Eric Andersen34506362001-08-02 05:02:46 +00006496 name = u_signal_names(*argptr, &signo, -1);
6497 if (name)
6498 printf(snlfmt, name);
Eric Andersencb57d552001-06-28 07:25:16 +00006499 else
6500 error("invalid signal number or exit status: %s",
6501 *argptr);
6502 return 0;
6503 }
6504
6505 do {
6506 if (**argptr == '%') {
6507 jp = getjob(*argptr);
6508 if (jp->jobctl == 0)
6509 error("job %s not created under job control",
6510 *argptr);
6511 pid = -jp->ps[0].pid;
6512 } else
6513 pid = atoi(*argptr);
6514 if (kill(pid, signo) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00006515 error("%s: %m", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00006516 } while (*++argptr);
6517
6518 return 0;
6519}
6520
6521static int
6522fgcmd(argc, argv)
6523 int argc;
6524 char **argv;
6525{
6526 struct job *jp;
6527 int pgrp;
6528 int status;
6529
6530 jp = getjob(argv[1]);
6531 if (jp->jobctl == 0)
6532 error("job not created under job control");
6533 pgrp = jp->ps[0].pid;
6534#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006535 ioctl(2, TIOCSPGRP, (char *)&pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006536#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006537 tcsetpgrp(2, pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006538#endif
6539 restartjob(jp);
6540 INTOFF;
6541 status = waitforjob(jp);
6542 INTON;
6543 return status;
6544}
6545
6546
6547static int
6548bgcmd(argc, argv)
6549 int argc;
6550 char **argv;
6551{
6552 struct job *jp;
6553
6554 do {
6555 jp = getjob(*++argv);
6556 if (jp->jobctl == 0)
6557 error("job not created under job control");
6558 restartjob(jp);
6559 } while (--argc > 1);
6560 return 0;
6561}
6562
6563
6564static void
6565restartjob(jp)
6566 struct job *jp;
6567{
6568 struct procstat *ps;
6569 int i;
6570
6571 if (jp->state == JOBDONE)
6572 return;
6573 INTOFF;
6574 killpg(jp->ps[0].pid, SIGCONT);
6575 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
6576 if (WIFSTOPPED(ps->status)) {
6577 ps->status = -1;
6578 jp->state = 0;
6579 }
6580 }
6581 INTON;
6582}
6583#endif
6584
Eric Andersen2870d962001-07-02 17:27:21 +00006585static void showjobs(int change);
6586
Eric Andersencb57d552001-06-28 07:25:16 +00006587
6588static int
6589jobscmd(argc, argv)
6590 int argc;
6591 char **argv;
6592{
6593 showjobs(0);
6594 return 0;
6595}
6596
6597
6598/*
6599 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6600 * statuses have changed since the last call to showjobs.
6601 *
6602 * If the shell is interrupted in the process of creating a job, the
6603 * result may be a job structure containing zero processes. Such structures
6604 * will be freed here.
6605 */
6606
6607static void
6608showjobs(change)
6609 int change;
6610{
6611 int jobno;
6612 int procno;
6613 int i;
6614 struct job *jp;
6615 struct procstat *ps;
6616 int col;
6617 char s[64];
6618
6619 TRACE(("showjobs(%d) called\n", change));
6620 while (dowait(0, (struct job *)NULL) > 0);
6621 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
6622 if (! jp->used)
6623 continue;
6624 if (jp->nprocs == 0) {
6625 freejob(jp);
6626 continue;
6627 }
6628 if (change && ! jp->changed)
6629 continue;
6630 procno = jp->nprocs;
Eric Andersen2870d962001-07-02 17:27:21 +00006631 for (ps = jp->ps ; ; ps++) { /* for each process */
Eric Andersencb57d552001-06-28 07:25:16 +00006632 if (ps == jp->ps)
Eric Andersen3102ac42001-07-06 04:26:23 +00006633 snprintf(s, 64, "[%d] %ld ", jobno,
Eric Andersencb57d552001-06-28 07:25:16 +00006634 (long)ps->pid);
6635 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006636 snprintf(s, 64, " %ld ",
Eric Andersencb57d552001-06-28 07:25:16 +00006637 (long)ps->pid);
6638 out1str(s);
6639 col = strlen(s);
6640 s[0] = '\0';
6641 if (ps->status == -1) {
6642 /* don't print anything */
6643 } else if (WIFEXITED(ps->status)) {
Eric Andersen3102ac42001-07-06 04:26:23 +00006644 snprintf(s, 64, "Exit %d",
Eric Andersencb57d552001-06-28 07:25:16 +00006645 WEXITSTATUS(ps->status));
6646 } else {
Eric Andersen2870d962001-07-02 17:27:21 +00006647#ifdef JOBS
6648 if (WIFSTOPPED(ps->status))
Eric Andersencb57d552001-06-28 07:25:16 +00006649 i = WSTOPSIG(ps->status);
6650 else /* WIFSIGNALED(ps->status) */
6651#endif
6652 i = WTERMSIG(ps->status);
6653 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
Eric Andersen2870d962001-07-02 17:27:21 +00006654 strcpy(s, sys_siglist[i & 0x7F]);
Eric Andersencb57d552001-06-28 07:25:16 +00006655 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006656 snprintf(s, 64, "Signal %d", i & 0x7F);
Eric Andersencb57d552001-06-28 07:25:16 +00006657 if (WCOREDUMP(ps->status))
6658 strcat(s, " (core dumped)");
6659 }
6660 out1str(s);
6661 col += strlen(s);
Eric Andersen62483552001-07-10 06:09:16 +00006662 printf(
Eric Andersencb57d552001-06-28 07:25:16 +00006663 "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
6664 ps->cmd
6665 );
6666 if (--procno <= 0)
6667 break;
6668 }
6669 jp->changed = 0;
6670 if (jp->state == JOBDONE) {
6671 freejob(jp);
6672 }
6673 }
6674}
6675
6676
6677/*
6678 * Mark a job structure as unused.
6679 */
6680
6681static void
Eric Andersen62483552001-07-10 06:09:16 +00006682freejob(struct job *jp)
6683{
6684 const struct procstat *ps;
Eric Andersencb57d552001-06-28 07:25:16 +00006685 int i;
6686
6687 INTOFF;
6688 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
6689 if (ps->cmd != nullstr)
6690 ckfree(ps->cmd);
6691 }
6692 if (jp->ps != &jp->ps0)
6693 ckfree(jp->ps);
6694 jp->used = 0;
Eric Andersen2870d962001-07-02 17:27:21 +00006695#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006696 if (curjob == jp - jobtab + 1)
6697 curjob = 0;
6698#endif
6699 INTON;
6700}
6701
6702
6703
6704static int
6705waitcmd(argc, argv)
6706 int argc;
6707 char **argv;
6708{
6709 struct job *job;
6710 int status, retval;
6711 struct job *jp;
6712
6713 if (--argc > 0) {
6714start:
6715 job = getjob(*++argv);
6716 } else {
6717 job = NULL;
6718 }
Eric Andersen2870d962001-07-02 17:27:21 +00006719 for (;;) { /* loop until process terminated or stopped */
Eric Andersencb57d552001-06-28 07:25:16 +00006720 if (job != NULL) {
6721 if (job->state) {
6722 status = job->ps[job->nprocs - 1].status;
6723 if (! iflag)
6724 freejob(job);
6725 if (--argc) {
6726 goto start;
6727 }
6728 if (WIFEXITED(status))
6729 retval = WEXITSTATUS(status);
Eric Andersen2870d962001-07-02 17:27:21 +00006730#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006731 else if (WIFSTOPPED(status))
6732 retval = WSTOPSIG(status) + 128;
6733#endif
6734 else {
6735 /* XXX: limits number of signals */
6736 retval = WTERMSIG(status) + 128;
6737 }
6738 return retval;
6739 }
6740 } else {
6741 for (jp = jobtab ; ; jp++) {
Eric Andersen2870d962001-07-02 17:27:21 +00006742 if (jp >= jobtab + njobs) { /* no running procs */
Eric Andersencb57d552001-06-28 07:25:16 +00006743 return 0;
6744 }
6745 if (jp->used && jp->state == 0)
6746 break;
6747 }
6748 }
6749 if (dowait(2, 0) < 0 && errno == EINTR) {
6750 return 129;
6751 }
6752 }
6753}
6754
6755
6756
6757/*
6758 * Convert a job name to a job structure.
6759 */
6760
6761static struct job *
Eric Andersen2870d962001-07-02 17:27:21 +00006762getjob(const char *name)
6763{
Eric Andersencb57d552001-06-28 07:25:16 +00006764 int jobno;
6765 struct job *jp;
6766 int pid;
6767 int i;
6768
6769 if (name == NULL) {
Eric Andersen2870d962001-07-02 17:27:21 +00006770#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006771currentjob:
6772 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
6773 error("No current job");
6774 return &jobtab[jobno - 1];
6775#else
6776 error("No current job");
6777#endif
6778 } else if (name[0] == '%') {
6779 if (is_digit(name[1])) {
6780 jobno = number(name + 1);
6781 if (jobno > 0 && jobno <= njobs
6782 && jobtab[jobno - 1].used != 0)
6783 return &jobtab[jobno - 1];
Eric Andersen2870d962001-07-02 17:27:21 +00006784#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006785 } else if (name[1] == '%' && name[2] == '\0') {
6786 goto currentjob;
6787#endif
6788 } else {
6789 struct job *found = NULL;
6790 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6791 if (jp->used && jp->nprocs > 0
6792 && prefix(name + 1, jp->ps[0].cmd)) {
6793 if (found)
6794 error("%s: ambiguous", name);
6795 found = jp;
6796 }
6797 }
6798 if (found)
6799 return found;
6800 }
Eric Andersen2870d962001-07-02 17:27:21 +00006801 } else if (is_number(name, &pid)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006802 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6803 if (jp->used && jp->nprocs > 0
6804 && jp->ps[jp->nprocs - 1].pid == pid)
6805 return jp;
6806 }
6807 }
6808 error("No such job: %s", name);
6809 /* NOTREACHED */
6810}
6811
6812
6813
6814/*
6815 * Return a new job structure,
6816 */
6817
Eric Andersen2870d962001-07-02 17:27:21 +00006818static struct job *
Eric Andersen62483552001-07-10 06:09:16 +00006819makejob(const union node *node, int nprocs)
Eric Andersencb57d552001-06-28 07:25:16 +00006820{
6821 int i;
6822 struct job *jp;
6823
6824 for (i = njobs, jp = jobtab ; ; jp++) {
6825 if (--i < 0) {
6826 INTOFF;
6827 if (njobs == 0) {
6828 jobtab = ckmalloc(4 * sizeof jobtab[0]);
6829 } else {
6830 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
6831 memcpy(jp, jobtab, njobs * sizeof jp[0]);
6832 /* Relocate `ps' pointers */
6833 for (i = 0; i < njobs; i++)
6834 if (jp[i].ps == &jobtab[i].ps0)
6835 jp[i].ps = &jp[i].ps0;
6836 ckfree(jobtab);
6837 jobtab = jp;
6838 }
6839 jp = jobtab + njobs;
6840 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
6841 INTON;
6842 break;
6843 }
6844 if (jp->used == 0)
6845 break;
6846 }
6847 INTOFF;
6848 jp->state = 0;
6849 jp->used = 1;
6850 jp->changed = 0;
6851 jp->nprocs = 0;
Eric Andersen2870d962001-07-02 17:27:21 +00006852#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006853 jp->jobctl = jobctl;
6854#endif
6855 if (nprocs > 1) {
6856 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
6857 } else {
6858 jp->ps = &jp->ps0;
6859 }
6860 INTON;
6861 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
6862 jp - jobtab + 1));
6863 return jp;
6864}
6865
6866
6867/*
6868 * Fork of a subshell. If we are doing job control, give the subshell its
6869 * own process group. Jp is a job structure that the job is to be added to.
6870 * N is the command that will be evaluated by the child. Both jp and n may
6871 * be NULL. The mode parameter can be one of the following:
Eric Andersen2870d962001-07-02 17:27:21 +00006872 * FORK_FG - Fork off a foreground process.
6873 * FORK_BG - Fork off a background process.
6874 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
6875 * process group even if job control is on.
Eric Andersencb57d552001-06-28 07:25:16 +00006876 *
6877 * When job control is turned off, background processes have their standard
6878 * input redirected to /dev/null (except for the second and later processes
6879 * in a pipeline).
6880 */
6881
Eric Andersen2870d962001-07-02 17:27:21 +00006882
6883
Eric Andersencb57d552001-06-28 07:25:16 +00006884static int
Eric Andersen62483552001-07-10 06:09:16 +00006885forkshell(struct job *jp, const union node *n, int mode)
Eric Andersencb57d552001-06-28 07:25:16 +00006886{
6887 int pid;
Eric Andersen62483552001-07-10 06:09:16 +00006888#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006889 int pgrp;
Eric Andersen62483552001-07-10 06:09:16 +00006890#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006891 const char *devnull = _PATH_DEVNULL;
6892 const char *nullerr = "Can't open %s";
6893
6894 TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
6895 mode));
6896 INTOFF;
6897 pid = fork();
6898 if (pid == -1) {
6899 TRACE(("Fork failed, errno=%d\n", errno));
6900 INTON;
6901 error("Cannot fork");
6902 }
6903 if (pid == 0) {
6904 struct job *p;
6905 int wasroot;
6906 int i;
6907
6908 TRACE(("Child shell %d\n", getpid()));
6909 wasroot = rootshell;
6910 rootshell = 0;
6911 closescript();
6912 INTON;
6913 clear_traps();
Eric Andersen2870d962001-07-02 17:27:21 +00006914#ifdef JOBS
6915 jobctl = 0; /* do job control only in root shell */
Eric Andersencb57d552001-06-28 07:25:16 +00006916 if (wasroot && mode != FORK_NOJOB && mflag) {
6917 if (jp == NULL || jp->nprocs == 0)
6918 pgrp = getpid();
6919 else
6920 pgrp = jp->ps[0].pid;
6921 setpgid(0, pgrp);
6922 if (mode == FORK_FG) {
6923 /*** this causes superfluous TIOCSPGRPS ***/
6924#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006925 if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006926 error("TIOCSPGRP failed, errno=%d", errno);
6927#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006928 if (tcsetpgrp(2, pgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006929 error("tcsetpgrp failed, errno=%d", errno);
6930#endif
6931 }
6932 setsignal(SIGTSTP);
6933 setsignal(SIGTTOU);
6934 } else if (mode == FORK_BG) {
6935 ignoresig(SIGINT);
6936 ignoresig(SIGQUIT);
6937 if ((jp == NULL || jp->nprocs == 0) &&
6938 ! fd0_redirected_p ()) {
6939 close(0);
6940 if (open(devnull, O_RDONLY) != 0)
6941 error(nullerr, devnull);
6942 }
6943 }
6944#else
6945 if (mode == FORK_BG) {
6946 ignoresig(SIGINT);
6947 ignoresig(SIGQUIT);
6948 if ((jp == NULL || jp->nprocs == 0) &&
6949 ! fd0_redirected_p ()) {
6950 close(0);
6951 if (open(devnull, O_RDONLY) != 0)
6952 error(nullerr, devnull);
6953 }
6954 }
6955#endif
6956 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
6957 if (p->used)
6958 freejob(p);
6959 if (wasroot && iflag) {
6960 setsignal(SIGINT);
6961 setsignal(SIGQUIT);
6962 setsignal(SIGTERM);
6963 }
6964 return pid;
6965 }
Eric Andersen62483552001-07-10 06:09:16 +00006966#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00006967 if (rootshell && mode != FORK_NOJOB && mflag) {
6968 if (jp == NULL || jp->nprocs == 0)
6969 pgrp = pid;
6970 else
6971 pgrp = jp->ps[0].pid;
6972 setpgid(pid, pgrp);
6973 }
Eric Andersen62483552001-07-10 06:09:16 +00006974#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006975 if (mode == FORK_BG)
Eric Andersen2870d962001-07-02 17:27:21 +00006976 backgndpid = pid; /* set $! */
Eric Andersencb57d552001-06-28 07:25:16 +00006977 if (jp) {
6978 struct procstat *ps = &jp->ps[jp->nprocs++];
6979 ps->pid = pid;
6980 ps->status = -1;
6981 ps->cmd = nullstr;
6982 if (iflag && rootshell && n)
6983 ps->cmd = commandtext(n);
6984 }
6985 INTON;
6986 TRACE(("In parent shell: child = %d\n", pid));
6987 return pid;
6988}
6989
6990
6991
6992/*
6993 * Wait for job to finish.
6994 *
6995 * Under job control we have the problem that while a child process is
6996 * running interrupts generated by the user are sent to the child but not
6997 * to the shell. This means that an infinite loop started by an inter-
6998 * active user may be hard to kill. With job control turned off, an
6999 * interactive user may place an interactive program inside a loop. If
7000 * the interactive program catches interrupts, the user doesn't want
7001 * these interrupts to also abort the loop. The approach we take here
7002 * is to have the shell ignore interrupt signals while waiting for a
7003 * forground process to terminate, and then send itself an interrupt
7004 * signal if the child process was terminated by an interrupt signal.
7005 * Unfortunately, some programs want to do a bit of cleanup and then
7006 * exit on interrupt; unless these processes terminate themselves by
7007 * sending a signal to themselves (instead of calling exit) they will
7008 * confuse this approach.
7009 */
7010
7011static int
Eric Andersen62483552001-07-10 06:09:16 +00007012waitforjob(struct job *jp)
7013{
Eric Andersen2870d962001-07-02 17:27:21 +00007014#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007015 int mypgrp = getpgrp();
7016#endif
7017 int status;
7018 int st;
7019 struct sigaction act, oact;
7020
7021 INTOFF;
7022 intreceived = 0;
Eric Andersen2870d962001-07-02 17:27:21 +00007023#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007024 if (!jobctl) {
7025#else
7026 if (!iflag) {
7027#endif
7028 sigaction(SIGINT, 0, &act);
7029 act.sa_handler = waitonint;
7030 sigaction(SIGINT, &act, &oact);
7031 }
7032 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
7033 while (jp->state == 0) {
7034 dowait(1, jp);
7035 }
Eric Andersen2870d962001-07-02 17:27:21 +00007036#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007037 if (!jobctl) {
7038#else
7039 if (!iflag) {
7040#endif
7041 sigaction(SIGINT, &oact, 0);
7042 if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT);
7043 }
Eric Andersen2870d962001-07-02 17:27:21 +00007044#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007045 if (jp->jobctl) {
7046#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00007047 if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00007048 error("TIOCSPGRP failed, errno=%d\n", errno);
7049#else
Eric Andersen3102ac42001-07-06 04:26:23 +00007050 if (tcsetpgrp(2, mypgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00007051 error("tcsetpgrp failed, errno=%d\n", errno);
7052#endif
7053 }
7054 if (jp->state == JOBSTOPPED)
7055 curjob = jp - jobtab + 1;
7056#endif
7057 status = jp->ps[jp->nprocs - 1].status;
7058 /* convert to 8 bits */
7059 if (WIFEXITED(status))
7060 st = WEXITSTATUS(status);
Eric Andersen2870d962001-07-02 17:27:21 +00007061#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007062 else if (WIFSTOPPED(status))
7063 st = WSTOPSIG(status) + 128;
7064#endif
7065 else
7066 st = WTERMSIG(status) + 128;
Eric Andersen2870d962001-07-02 17:27:21 +00007067#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007068 if (jp->jobctl) {
7069 /*
7070 * This is truly gross.
7071 * If we're doing job control, then we did a TIOCSPGRP which
7072 * caused us (the shell) to no longer be in the controlling
7073 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
7074 * intuit from the subprocess exit status whether a SIGINT
7075 * occured, and if so interrupt ourselves. Yuck. - mycroft
7076 */
7077 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
7078 raise(SIGINT);
7079 }
Eric Andersen2870d962001-07-02 17:27:21 +00007080 if (jp->state == JOBDONE)
7081
Eric Andersencb57d552001-06-28 07:25:16 +00007082#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007083 freejob(jp);
7084 INTON;
7085 return st;
7086}
7087
7088
7089
7090/*
7091 * Wait for a process to terminate.
7092 */
7093
Eric Andersen62483552001-07-10 06:09:16 +00007094/*
7095 * Do a wait system call. If job control is compiled in, we accept
7096 * stopped processes. If block is zero, we return a value of zero
7097 * rather than blocking.
7098 *
7099 * System V doesn't have a non-blocking wait system call. It does
7100 * have a SIGCLD signal that is sent to a process when one of it's
7101 * children dies. The obvious way to use SIGCLD would be to install
7102 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7103 * was received, and have waitproc bump another counter when it got
7104 * the status of a process. Waitproc would then know that a wait
7105 * system call would not block if the two counters were different.
7106 * This approach doesn't work because if a process has children that
7107 * have not been waited for, System V will send it a SIGCLD when it
7108 * installs a signal handler for SIGCLD. What this means is that when
7109 * a child exits, the shell will be sent SIGCLD signals continuously
7110 * until is runs out of stack space, unless it does a wait call before
7111 * restoring the signal handler. The code below takes advantage of
7112 * this (mis)feature by installing a signal handler for SIGCLD and
7113 * then checking to see whether it was called. If there are any
7114 * children to be waited for, it will be.
7115 *
7116 */
7117
7118static inline int
7119waitproc(int block, int *status)
7120{
7121 int flags;
7122
7123 flags = 0;
7124#ifdef JOBS
7125 if (jobctl)
7126 flags |= WUNTRACED;
7127#endif
7128 if (block == 0)
7129 flags |= WNOHANG;
7130 return wait3(status, flags, (struct rusage *)NULL);
7131}
7132
Eric Andersencb57d552001-06-28 07:25:16 +00007133static int
Eric Andersen62483552001-07-10 06:09:16 +00007134dowait(int block, struct job *job)
Eric Andersencb57d552001-06-28 07:25:16 +00007135{
7136 int pid;
7137 int status;
7138 struct procstat *sp;
7139 struct job *jp;
7140 struct job *thisjob;
7141 int done;
7142 int stopped;
7143 int core;
7144 int sig;
7145
7146 TRACE(("dowait(%d) called\n", block));
7147 do {
7148 pid = waitproc(block, &status);
7149 TRACE(("wait returns %d, status=%d\n", pid, status));
7150 } while (!(block & 2) && pid == -1 && errno == EINTR);
7151 if (pid <= 0)
7152 return pid;
7153 INTOFF;
7154 thisjob = NULL;
7155 for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
7156 if (jp->used) {
7157 done = 1;
7158 stopped = 1;
7159 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
7160 if (sp->pid == -1)
7161 continue;
7162 if (sp->pid == pid) {
7163 TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
7164 sp->status = status;
7165 thisjob = jp;
7166 }
7167 if (sp->status == -1)
7168 stopped = 0;
7169 else if (WIFSTOPPED(sp->status))
7170 done = 0;
7171 }
Eric Andersen2870d962001-07-02 17:27:21 +00007172 if (stopped) { /* stopped or done */
Eric Andersencb57d552001-06-28 07:25:16 +00007173 int state = done? JOBDONE : JOBSTOPPED;
7174 if (jp->state != state) {
7175 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
7176 jp->state = state;
Eric Andersen2870d962001-07-02 17:27:21 +00007177#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007178 if (done && curjob == jp - jobtab + 1)
Eric Andersen2870d962001-07-02 17:27:21 +00007179 curjob = 0; /* no current job */
Eric Andersencb57d552001-06-28 07:25:16 +00007180#endif
7181 }
7182 }
7183 }
7184 }
7185 INTON;
7186 if (! rootshell || ! iflag || (job && thisjob == job)) {
7187 core = WCOREDUMP(status);
Eric Andersen2870d962001-07-02 17:27:21 +00007188#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007189 if (WIFSTOPPED(status)) sig = WSTOPSIG(status);
7190 else
7191#endif
7192 if (WIFEXITED(status)) sig = 0;
7193 else sig = WTERMSIG(status);
7194
7195 if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
7196 if (thisjob != job)
Eric Andersen3102ac42001-07-06 04:26:23 +00007197 out2fmt("%d: ", pid);
Eric Andersen2870d962001-07-02 17:27:21 +00007198#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +00007199 if (sig == SIGTSTP && rootshell && iflag)
Eric Andersen3102ac42001-07-06 04:26:23 +00007200 out2fmt("%%%ld ",
Eric Andersencb57d552001-06-28 07:25:16 +00007201 (long)(job - jobtab + 1));
7202#endif
7203 if (sig < NSIG && sys_siglist[sig])
7204 out2str(sys_siglist[sig]);
7205 else
Eric Andersen3102ac42001-07-06 04:26:23 +00007206 out2fmt("Signal %d", sig);
Eric Andersencb57d552001-06-28 07:25:16 +00007207 if (core)
7208 out2str(" - core dumped");
7209 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00007210 } else {
Eric Andersen2870d962001-07-02 17:27:21 +00007211 TRACE(("Not printing status: status=%d, sig=%d\n",
Eric Andersencb57d552001-06-28 07:25:16 +00007212 status, sig));
7213 }
7214 } else {
7215 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
7216 if (thisjob)
7217 thisjob->changed = 1;
7218 }
7219 return pid;
7220}
7221
7222
7223
Eric Andersencb57d552001-06-28 07:25:16 +00007224
7225/*
7226 * return 1 if there are stopped jobs, otherwise 0
7227 */
Eric Andersencb57d552001-06-28 07:25:16 +00007228static int
Eric Andersen2870d962001-07-02 17:27:21 +00007229stoppedjobs(void)
Eric Andersencb57d552001-06-28 07:25:16 +00007230{
7231 int jobno;
7232 struct job *jp;
7233
7234 if (job_warning)
7235 return (0);
7236 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
7237 if (jp->used == 0)
7238 continue;
7239 if (jp->state == JOBSTOPPED) {
7240 out2str("You have stopped jobs.\n");
7241 job_warning = 2;
7242 return (1);
7243 }
7244 }
7245
7246 return (0);
7247}
7248
7249/*
7250 * Return a string identifying a command (to be printed by the
7251 * jobs command.
7252 */
7253
7254static char *cmdnextc;
7255static int cmdnleft;
Eric Andersen2870d962001-07-02 17:27:21 +00007256#define MAXCMDTEXT 200
Eric Andersencb57d552001-06-28 07:25:16 +00007257
Eric Andersen2870d962001-07-02 17:27:21 +00007258static void
7259cmdputs(const char *s)
7260{
7261 const char *p;
7262 char *q;
7263 char c;
7264 int subtype = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007265
Eric Andersen2870d962001-07-02 17:27:21 +00007266 if (cmdnleft <= 0)
7267 return;
7268 p = s;
7269 q = cmdnextc;
7270 while ((c = *p++) != '\0') {
7271 if (c == CTLESC)
7272 *q++ = *p++;
7273 else if (c == CTLVAR) {
7274 *q++ = '$';
7275 if (--cmdnleft > 0)
7276 *q++ = '{';
7277 subtype = *p++;
7278 } else if (c == '=' && subtype != 0) {
7279 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
7280 subtype = 0;
7281 } else if (c == CTLENDVAR) {
7282 *q++ = '}';
7283 } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
7284 cmdnleft++; /* ignore it */
7285 else
7286 *q++ = c;
7287 if (--cmdnleft <= 0) {
7288 *q++ = '.';
7289 *q++ = '.';
7290 *q++ = '.';
7291 break;
7292 }
7293 }
7294 cmdnextc = q;
Eric Andersencb57d552001-06-28 07:25:16 +00007295}
7296
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00007297#define CMDTXT_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007298#ifdef CMDTXT_TABLE
7299/*
7300 * To collect a lot of redundant code in cmdtxt() case statements, we
7301 * implement a mini language here. Each type of node struct has an
7302 * associated instruction sequence that operates on its members via
7303 * their offsets. The instruction are pack in unsigned chars with
7304 * format IIDDDDDE where the bits are
7305 * I : part of the instruction opcode, which are
7306 * 00 : member is a pointer to another node -- process it recursively
7307 * 40 : member is a pointer to a char string -- output it
7308 * 80 : output the string whose index is stored in the data field
7309 * CC : flag signaling that this case needs external processing
7310 * D : data - either the (shifted) index of a fixed string to output or
7311 * the actual offset of the member to operate on in the struct
7312 * (since we assume bit 0 is set, the offset is not shifted)
7313 * E : flag signaling end of instruction sequence
7314 *
7315 * WARNING: In order to handle larger offsets for 64bit archs, this code
7316 * assumes that no offset can be an odd number and stores the
7317 * end-of-instructions flag in bit 0.
7318 */
Eric Andersencb57d552001-06-28 07:25:16 +00007319
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007320#define CMDTXT_NOMORE 0x01 /* NOTE: no offset should be odd */
7321#define CMDTXT_CHARPTR 0x40
7322#define CMDTXT_STRING 0x80
7323#define CMDTXT_SPECIAL 0xC0
7324#define CMDTXT_OFFSETMASK 0x3E
7325
7326static const char * const cmdtxt_strings[] = {
7327 /* 0 1 2 3 4 5 6 7 */
7328 "; ", "(", ")", " && ", " || ", "if ", "; then ", "...",
7329 /* 8 9 10 11 12 13 */
7330 "while ", "; do ", "; done", "until ", "for ", " in ...",
7331 /* 14 15 16 17 */
7332 "case ", "???", "() ...", "<<..."
7333};
7334
7335static const char * const redir_strings[] = {
7336 ">", "<", "<>", ">>", ">|", ">&", "<&"
7337};
7338
7339static const unsigned char cmdtxt_ops[] = {
7340#define CMDTXT_NSEMI 0
7341 offsetof(union node, nbinary.ch1),
7342 0|CMDTXT_STRING,
7343 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7344#define CMDTXT_NCMD (CMDTXT_NSEMI + 3)
7345#define CMDTXT_NPIPE (CMDTXT_NCMD)
7346#define CMDTXT_NCASE (CMDTXT_NCMD)
7347#define CMDTXT_NTO (CMDTXT_NCMD)
7348#define CMDTXT_NFROM (CMDTXT_NCMD)
7349#define CMDTXT_NFROMTO (CMDTXT_NCMD)
7350#define CMDTXT_NAPPEND (CMDTXT_NCMD)
7351#define CMDTXT_NTOOV (CMDTXT_NCMD)
7352#define CMDTXT_NTOFD (CMDTXT_NCMD)
7353#define CMDTXT_NFROMFD (CMDTXT_NCMD)
7354 CMDTXT_SPECIAL,
7355#define CMDTXT_NREDIR (CMDTXT_NPIPE + 1)
7356#define CMDTXT_NBACKGND (CMDTXT_NREDIR)
7357 offsetof(union node, nredir.n)|CMDTXT_NOMORE,
7358#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1)
7359 (1*2)|CMDTXT_STRING,
7360 offsetof(union node, nredir.n),
7361 (2*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7362#define CMDTXT_NAND (CMDTXT_NSUBSHELL + 3)
7363 offsetof(union node, nbinary.ch1),
7364 (3*2)|CMDTXT_STRING,
7365 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7366#define CMDTXT_NOR (CMDTXT_NAND + 3)
7367 offsetof(union node, nbinary.ch1),
7368 (4*2)|CMDTXT_STRING,
7369 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7370#define CMDTXT_NIF (CMDTXT_NOR + 3)
7371 (5*2)|CMDTXT_STRING,
7372 offsetof(union node, nif.test),
7373 (6*2)|CMDTXT_STRING,
7374 offsetof(union node, nif.ifpart),
7375 (7*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7376#define CMDTXT_NWHILE (CMDTXT_NIF + 5)
7377 (8*2)|CMDTXT_STRING,
7378 offsetof(union node, nbinary.ch1),
7379 (9*2)|CMDTXT_STRING,
7380 offsetof(union node, nbinary.ch2),
7381 (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7382#define CMDTXT_NUNTIL (CMDTXT_NWHILE + 5)
7383 (11*2)|CMDTXT_STRING,
7384 offsetof(union node, nbinary.ch1),
7385 (9*2)|CMDTXT_STRING,
7386 offsetof(union node, nbinary.ch2),
7387 (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7388#define CMDTXT_NFOR (CMDTXT_NUNTIL + 5)
7389 (12*2)|CMDTXT_STRING,
7390 offsetof(union node, nfor.var)|CMDTXT_CHARPTR,
7391 (13*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7392#define CMDTXT_NCLIST (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */
7393#define CMDTXT_NNOT (CMDTXT_NCLIST) /* TODO: IS THIS CORRECT??? */
7394 (15*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7395#define CMDTXT_NDEFUN (CMDTXT_NCLIST + 1)
7396 offsetof(union node, narg.text)|CMDTXT_CHARPTR,
7397 (16*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7398#define CMDTXT_NARG (CMDTXT_NDEFUN + 2)
7399 offsetof(union node, narg.text)|CMDTXT_CHARPTR|CMDTXT_NOMORE,
7400#define CMDTXT_NHERE (CMDTXT_NARG + 1)
7401#define CMDTXT_NXHERE (CMDTXT_NHERE)
7402 (17*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7403};
7404
7405#if CMDTXT_NXHERE != 36
7406#error CMDTXT_NXHERE
7407#endif
7408
7409static const unsigned char cmdtxt_ops_index[26] = {
7410 CMDTXT_NSEMI,
7411 CMDTXT_NCMD,
7412 CMDTXT_NPIPE,
7413 CMDTXT_NREDIR,
7414 CMDTXT_NBACKGND,
7415 CMDTXT_NSUBSHELL,
7416 CMDTXT_NAND,
7417 CMDTXT_NOR,
7418 CMDTXT_NIF,
7419 CMDTXT_NWHILE,
7420 CMDTXT_NUNTIL,
7421 CMDTXT_NFOR,
7422 CMDTXT_NCASE,
7423 CMDTXT_NCLIST,
7424 CMDTXT_NDEFUN,
7425 CMDTXT_NARG,
7426 CMDTXT_NTO,
7427 CMDTXT_NFROM,
7428 CMDTXT_NFROMTO,
7429 CMDTXT_NAPPEND,
7430 CMDTXT_NTOOV,
7431 CMDTXT_NTOFD,
7432 CMDTXT_NFROMFD,
7433 CMDTXT_NHERE,
7434 CMDTXT_NXHERE,
7435 CMDTXT_NNOT,
7436};
7437
7438static void
7439cmdtxt(const union node *n)
7440{
7441 const char *p;
7442
7443 if (n == NULL)
7444 return;
7445
7446 p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type];
7447 if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */
7448 do {
7449 if (*p & CMDTXT_STRING) { /* output fixed string */
7450 cmdputs(cmdtxt_strings[((int)(*p & CMDTXT_OFFSETMASK) >> 1)]);
Manuel Novoa III c639a352001-08-12 17:32:56 +00007451 } else {
7452 const char *pf = ((const char *) n)
7453 + ((int)(*p & CMDTXT_OFFSETMASK));
7454 if (*p & CMDTXT_CHARPTR) { /* output dynamic string */
7455 cmdputs(*((const char **) pf));
7456 } else { /* output field */
7457 cmdtxt(*((const union node **) pf));
7458 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007459 }
7460 } while (!(*p++ & CMDTXT_NOMORE));
7461 } else if (n->type == NCMD) {
7462 union node *np;
7463 for (np = n->ncmd.args ; np ; np = np->narg.next) {
7464 cmdtxt(np);
7465 if (np->narg.next)
7466 cmdputs(spcstr);
7467 }
7468 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
7469 cmdputs(spcstr);
7470 cmdtxt(np);
7471 }
7472 } else if (n->type == NPIPE) {
7473 struct nodelist *lp;
7474 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
7475 cmdtxt(lp->n);
7476 if (lp->next)
7477 cmdputs(" | ");
7478 }
7479 } else if (n->type == NCASE) {
7480 cmdputs(cmdtxt_strings[14]);
7481 cmdputs(n->ncase.expr->narg.text);
7482 cmdputs(cmdtxt_strings[13]);
7483 } else {
7484#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22)
7485#error Assumption violated regarding range and ordering of NTO ... NFROMFD!
7486#endif
7487 char s[2];
7488
7489#ifdef DEBUG
7490 assert((n->type >= NTO) && (n->type <= NFROMFD));
7491#endif
7492
7493 p = redir_strings[n->type - NTO];
7494 if (n->nfile.fd != ('>' == *p)) {
7495 s[0] = n->nfile.fd + '0';
7496 s[1] = '\0';
7497 cmdputs(s);
7498 }
7499 cmdputs(p);
7500 if (n->type >= NTOFD) {
7501 s[0] = n->ndup.dupfd + '0';
7502 s[1] = '\0';
7503 cmdputs(s);
7504 } else {
7505 cmdtxt(n->nfile.fname);
7506 }
7507 }
7508}
7509#else /* CMDTXT_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00007510static void
Eric Andersen2870d962001-07-02 17:27:21 +00007511cmdtxt(const union node *n)
7512{
Eric Andersencb57d552001-06-28 07:25:16 +00007513 union node *np;
7514 struct nodelist *lp;
7515 const char *p;
7516 int i;
7517 char s[2];
7518
7519 if (n == NULL)
7520 return;
7521 switch (n->type) {
7522 case NSEMI:
7523 cmdtxt(n->nbinary.ch1);
7524 cmdputs("; ");
7525 cmdtxt(n->nbinary.ch2);
7526 break;
7527 case NAND:
7528 cmdtxt(n->nbinary.ch1);
7529 cmdputs(" && ");
7530 cmdtxt(n->nbinary.ch2);
7531 break;
7532 case NOR:
7533 cmdtxt(n->nbinary.ch1);
7534 cmdputs(" || ");
7535 cmdtxt(n->nbinary.ch2);
7536 break;
7537 case NPIPE:
7538 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
7539 cmdtxt(lp->n);
7540 if (lp->next)
7541 cmdputs(" | ");
7542 }
7543 break;
7544 case NSUBSHELL:
7545 cmdputs("(");
7546 cmdtxt(n->nredir.n);
7547 cmdputs(")");
7548 break;
7549 case NREDIR:
7550 case NBACKGND:
7551 cmdtxt(n->nredir.n);
7552 break;
7553 case NIF:
7554 cmdputs("if ");
7555 cmdtxt(n->nif.test);
7556 cmdputs("; then ");
7557 cmdtxt(n->nif.ifpart);
7558 cmdputs("...");
7559 break;
7560 case NWHILE:
7561 cmdputs("while ");
7562 goto until;
7563 case NUNTIL:
7564 cmdputs("until ");
7565until:
7566 cmdtxt(n->nbinary.ch1);
7567 cmdputs("; do ");
7568 cmdtxt(n->nbinary.ch2);
7569 cmdputs("; done");
7570 break;
7571 case NFOR:
7572 cmdputs("for ");
7573 cmdputs(n->nfor.var);
7574 cmdputs(" in ...");
7575 break;
7576 case NCASE:
7577 cmdputs("case ");
7578 cmdputs(n->ncase.expr->narg.text);
7579 cmdputs(" in ...");
7580 break;
7581 case NDEFUN:
7582 cmdputs(n->narg.text);
7583 cmdputs("() ...");
7584 break;
7585 case NCMD:
7586 for (np = n->ncmd.args ; np ; np = np->narg.next) {
7587 cmdtxt(np);
7588 if (np->narg.next)
7589 cmdputs(spcstr);
7590 }
7591 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
7592 cmdputs(spcstr);
7593 cmdtxt(np);
7594 }
7595 break;
7596 case NARG:
7597 cmdputs(n->narg.text);
7598 break;
7599 case NTO:
7600 p = ">"; i = 1; goto redir;
7601 case NAPPEND:
7602 p = ">>"; i = 1; goto redir;
7603 case NTOFD:
7604 p = ">&"; i = 1; goto redir;
7605 case NTOOV:
7606 p = ">|"; i = 1; goto redir;
7607 case NFROM:
7608 p = "<"; i = 0; goto redir;
7609 case NFROMFD:
7610 p = "<&"; i = 0; goto redir;
7611 case NFROMTO:
7612 p = "<>"; i = 0; goto redir;
7613redir:
7614 if (n->nfile.fd != i) {
7615 s[0] = n->nfile.fd + '0';
7616 s[1] = '\0';
7617 cmdputs(s);
7618 }
7619 cmdputs(p);
7620 if (n->type == NTOFD || n->type == NFROMFD) {
7621 s[0] = n->ndup.dupfd + '0';
7622 s[1] = '\0';
7623 cmdputs(s);
7624 } else {
7625 cmdtxt(n->nfile.fname);
7626 }
7627 break;
7628 case NHERE:
7629 case NXHERE:
7630 cmdputs("<<...");
7631 break;
7632 default:
7633 cmdputs("???");
7634 break;
7635 }
7636}
Manuel Novoa III c639a352001-08-12 17:32:56 +00007637#endif /* CMDTXT_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00007638
Eric Andersen2870d962001-07-02 17:27:21 +00007639static char *
7640commandtext(const union node *n)
7641{
7642 char *name;
Eric Andersencb57d552001-06-28 07:25:16 +00007643
Eric Andersen2870d962001-07-02 17:27:21 +00007644 cmdnextc = name = ckmalloc(MAXCMDTEXT);
7645 cmdnleft = MAXCMDTEXT - 4;
7646 cmdtxt(n);
7647 *cmdnextc = '\0';
7648 return name;
Eric Andersencb57d552001-06-28 07:25:16 +00007649}
7650
Eric Andersen2870d962001-07-02 17:27:21 +00007651
Eric Andersencb57d552001-06-28 07:25:16 +00007652static void waitonint(int sig) {
7653 intreceived = 1;
7654 return;
7655}
Eric Andersencb57d552001-06-28 07:25:16 +00007656/*
7657 * Routines to check for mail. (Perhaps make part of main.c?)
7658 */
7659
7660
7661#define MAXMBOXES 10
7662
7663
Eric Andersen2870d962001-07-02 17:27:21 +00007664static int nmboxes; /* number of mailboxes */
7665static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
Eric Andersencb57d552001-06-28 07:25:16 +00007666
7667
7668
7669/*
7670 * Print appropriate message(s) if mail has arrived. If the argument is
7671 * nozero, then the value of MAIL has changed, so we just update the
7672 * values.
7673 */
7674
7675static void
Eric Andersen2870d962001-07-02 17:27:21 +00007676chkmail(int silent)
Eric Andersencb57d552001-06-28 07:25:16 +00007677{
7678 int i;
7679 const char *mpath;
7680 char *p;
7681 char *q;
7682 struct stackmark smark;
7683 struct stat statb;
7684
7685 if (silent)
7686 nmboxes = 10;
7687 if (nmboxes == 0)
7688 return;
7689 setstackmark(&smark);
7690 mpath = mpathset()? mpathval() : mailval();
7691 for (i = 0 ; i < nmboxes ; i++) {
7692 p = padvance(&mpath, nullstr);
7693 if (p == NULL)
7694 break;
7695 if (*p == '\0')
7696 continue;
7697 for (q = p ; *q ; q++);
7698#ifdef DEBUG
7699 if (q[-1] != '/')
7700 abort();
7701#endif
Eric Andersen2870d962001-07-02 17:27:21 +00007702 q[-1] = '\0'; /* delete trailing '/' */
Eric Andersencb57d552001-06-28 07:25:16 +00007703 if (stat(p, &statb) < 0)
7704 statb.st_size = 0;
7705 if (statb.st_size > mailtime[i] && ! silent) {
Eric Andersen3102ac42001-07-06 04:26:23 +00007706 out2fmt(snlfmt,
7707 pathopt? pathopt : "you have mail");
Eric Andersencb57d552001-06-28 07:25:16 +00007708 }
7709 mailtime[i] = statb.st_size;
Eric Andersencb57d552001-06-28 07:25:16 +00007710 }
7711 nmboxes = i;
7712 popstackmark(&smark);
7713}
Eric Andersencb57d552001-06-28 07:25:16 +00007714
7715#define PROFILE 0
7716
Eric Andersencb57d552001-06-28 07:25:16 +00007717#if PROFILE
Eric Andersen2870d962001-07-02 17:27:21 +00007718static short profile_buf[16384];
Eric Andersencb57d552001-06-28 07:25:16 +00007719extern int etext();
7720#endif
7721
Eric Andersen2870d962001-07-02 17:27:21 +00007722static void read_profile (const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00007723static void cmdloop (int);
7724static void options (int);
Eric Andersen2870d962001-07-02 17:27:21 +00007725static void setoption (int, int);
7726static void procargs (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00007727
Eric Andersen2870d962001-07-02 17:27:21 +00007728
Eric Andersencb57d552001-06-28 07:25:16 +00007729/*
7730 * Main routine. We initialize things, parse the arguments, execute
7731 * profiles if we're a login shell, and then call cmdloop to execute
7732 * commands. The setjmp call sets up the location to jump to when an
7733 * exception occurs. When an exception occurs the variable "state"
7734 * is used to figure out how far we had gotten.
7735 */
7736
7737int
Matt Kraai2d91deb2001-08-01 17:21:35 +00007738ash_main(argc, argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007739 int argc;
7740 char **argv;
7741{
7742 struct jmploc jmploc;
7743 struct stackmark smark;
7744 volatile int state;
Eric Andersen62483552001-07-10 06:09:16 +00007745 const char *shinit;
Eric Andersencb57d552001-06-28 07:25:16 +00007746
Eric Andersencb57d552001-06-28 07:25:16 +00007747 BLTINCMD = find_builtin("builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00007748 EXECCMD = find_builtin("exec");
7749 EVALCMD = find_builtin("eval");
7750
Eric Andersen1c039232001-07-07 00:05:55 +00007751#ifndef BB_FEATURE_SH_FANCY_PROMPT
7752 unsetenv("PS1");
7753 unsetenv("PS2");
7754#endif
7755
Eric Andersencb57d552001-06-28 07:25:16 +00007756#if PROFILE
7757 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7758#endif
7759#if defined(linux) || defined(__GNU__)
7760 signal(SIGCHLD, SIG_DFL);
7761#endif
7762 state = 0;
7763 if (setjmp(jmploc.loc)) {
7764 INTOFF;
7765 /*
7766 * When a shell procedure is executed, we raise the
7767 * exception EXSHELLPROC to clean up before executing
7768 * the shell procedure.
7769 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007770 if (exception == EXSHELLPROC) {
Eric Andersencb57d552001-06-28 07:25:16 +00007771 rootpid = getpid();
7772 rootshell = 1;
7773 minusc = NULL;
7774 state = 3;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007775 } else {
7776 if (exception == EXEXEC) {
7777 exitstatus = exerrno;
7778 } else if (exception == EXERROR) {
7779 exitstatus = 2;
7780 }
Eric Andersencb57d552001-06-28 07:25:16 +00007781 if (state == 0 || iflag == 0 || ! rootshell)
7782 exitshell(exitstatus);
7783 }
7784 reset();
Eric Andersen2870d962001-07-02 17:27:21 +00007785 if (exception == EXINT) {
Eric Andersencb57d552001-06-28 07:25:16 +00007786 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00007787 }
7788 popstackmark(&smark);
Eric Andersen2870d962001-07-02 17:27:21 +00007789 FORCEINTON; /* enable interrupts */
Eric Andersencb57d552001-06-28 07:25:16 +00007790 if (state == 1)
7791 goto state1;
7792 else if (state == 2)
7793 goto state2;
7794 else if (state == 3)
7795 goto state3;
7796 else
7797 goto state4;
7798 }
7799 handler = &jmploc;
7800#ifdef DEBUG
7801 opentrace();
7802 trputs("Shell args: "); trargs(argv);
7803#endif
7804 rootpid = getpid();
7805 rootshell = 1;
7806 init();
7807 setstackmark(&smark);
7808 procargs(argc, argv);
7809 if (argv[0] && argv[0][0] == '-') {
7810 state = 1;
7811 read_profile("/etc/profile");
7812state1:
7813 state = 2;
7814 read_profile(".profile");
7815 }
7816state2:
7817 state = 3;
7818#ifndef linux
7819 if (getuid() == geteuid() && getgid() == getegid()) {
7820#endif
7821 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7822 state = 3;
7823 read_profile(shinit);
7824 }
7825#ifndef linux
7826 }
7827#endif
7828state3:
7829 state = 4;
7830 if (sflag == 0 || minusc) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007831 static const char sigs[] = {
Eric Andersen2870d962001-07-02 17:27:21 +00007832 SIGINT, SIGQUIT, SIGHUP,
Eric Andersencb57d552001-06-28 07:25:16 +00007833#ifdef SIGTSTP
7834 SIGTSTP,
7835#endif
7836 SIGPIPE
7837 };
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007838#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])) - 1) /* trailing nul */
Eric Andersencb57d552001-06-28 07:25:16 +00007839 int i;
7840
7841 for (i = 0; i < SIGSSIZE; i++)
7842 setsignal(sigs[i]);
7843 }
7844
7845 if (minusc)
7846 evalstring(minusc, 0);
7847
7848 if (sflag || minusc == NULL) {
Eric Andersen2870d962001-07-02 17:27:21 +00007849state4: /* XXX ??? - why isn't this before the "if" statement */
Eric Andersencb57d552001-06-28 07:25:16 +00007850 cmdloop(1);
7851 }
7852#if PROFILE
7853 monitor(0);
7854#endif
7855 exitshell(exitstatus);
7856 /* NOTREACHED */
7857}
7858
7859
7860/*
7861 * Read and execute commands. "Top" is nonzero for the top level command
7862 * loop; it turns on prompting if the shell is interactive.
7863 */
7864
7865static void
Eric Andersen2870d962001-07-02 17:27:21 +00007866cmdloop(int top)
Eric Andersencb57d552001-06-28 07:25:16 +00007867{
7868 union node *n;
7869 struct stackmark smark;
7870 int inter;
7871 int numeof = 0;
7872
7873 TRACE(("cmdloop(%d) called\n", top));
7874 setstackmark(&smark);
7875 for (;;) {
7876 if (pendingsigs)
7877 dotrap();
7878 inter = 0;
7879 if (iflag && top) {
7880 inter++;
7881 showjobs(1);
7882 chkmail(0);
Eric Andersen3102ac42001-07-06 04:26:23 +00007883 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00007884 }
7885 n = parsecmd(inter);
7886 /* showtree(n); DEBUG */
7887 if (n == NEOF) {
7888 if (!top || numeof >= 50)
7889 break;
7890 if (!stoppedjobs()) {
7891 if (!Iflag)
7892 break;
7893 out2str("\nUse \"exit\" to leave shell.\n");
7894 }
7895 numeof++;
7896 } else if (n != NULL && nflag == 0) {
7897 job_warning = (job_warning == 2) ? 1 : 0;
7898 numeof = 0;
7899 evaltree(n, 0);
7900 }
7901 popstackmark(&smark);
7902 setstackmark(&smark);
7903 if (evalskip == SKIPFILE) {
7904 evalskip = 0;
7905 break;
7906 }
7907 }
7908 popstackmark(&smark);
7909}
7910
7911
7912
7913/*
7914 * Read /etc/profile or .profile. Return on error.
7915 */
7916
7917static void
7918read_profile(name)
7919 const char *name;
7920{
7921 int fd;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007922 int xflag_save;
7923 int vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007924
7925 INTOFF;
7926 if ((fd = open(name, O_RDONLY)) >= 0)
7927 setinputfd(fd, 1);
7928 INTON;
7929 if (fd < 0)
7930 return;
7931 /* -q turns off -x and -v just when executing init files */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007932 /* Note: Might do a little redundant work, but reduces code size. */
7933 xflag_save = xflag;
7934 vflag_save = vflag;
Eric Andersencb57d552001-06-28 07:25:16 +00007935 if (qflag) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007936 vflag = xflag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007937 }
7938 cmdloop(0);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007939 xflag = xflag_save;
7940 vflag = vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007941 popfile();
7942}
7943
7944
7945
7946/*
7947 * Read a file containing shell functions.
7948 */
7949
7950static void
Eric Andersen2870d962001-07-02 17:27:21 +00007951readcmdfile(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00007952{
7953 int fd;
7954
7955 INTOFF;
7956 if ((fd = open(name, O_RDONLY)) >= 0)
7957 setinputfd(fd, 1);
7958 else
7959 error("Can't open %s", name);
7960 INTON;
7961 cmdloop(0);
7962 popfile();
7963}
7964
7965
7966
7967/*
7968 * Take commands from a file. To be compatable we should do a path
7969 * search for the file, which is necessary to find sub-commands.
7970 */
7971
7972
Eric Andersen62483552001-07-10 06:09:16 +00007973static inline char *
Eric Andersencb57d552001-06-28 07:25:16 +00007974find_dot_file(mybasename)
7975 char *mybasename;
7976{
7977 char *fullname;
7978 const char *path = pathval();
7979 struct stat statb;
7980
7981 /* don't try this for absolute or relative paths */
7982 if (strchr(mybasename, '/'))
7983 return mybasename;
7984
7985 while ((fullname = padvance(&path, mybasename)) != NULL) {
7986 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
7987 /*
7988 * Don't bother freeing here, since it will
7989 * be freed by the caller.
7990 */
7991 return fullname;
7992 }
7993 stunalloc(fullname);
7994 }
7995
7996 /* not found in the PATH */
7997 error("%s: not found", mybasename);
7998 /* NOTREACHED */
7999}
8000
8001static int
8002dotcmd(argc, argv)
8003 int argc;
8004 char **argv;
8005{
8006 struct strlist *sp;
8007 exitstatus = 0;
8008
8009 for (sp = cmdenviron; sp ; sp = sp->next)
8010 setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
8011
Eric Andersen2870d962001-07-02 17:27:21 +00008012 if (argc >= 2) { /* That's what SVR2 does */
Eric Andersencb57d552001-06-28 07:25:16 +00008013 char *fullname;
8014 struct stackmark smark;
8015
8016 setstackmark(&smark);
8017 fullname = find_dot_file(argv[1]);
8018 setinputfile(fullname, 1);
8019 commandname = fullname;
8020 cmdloop(0);
8021 popfile();
8022 popstackmark(&smark);
8023 }
8024 return exitstatus;
8025}
8026
8027
8028static int
8029exitcmd(argc, argv)
8030 int argc;
8031 char **argv;
8032{
8033 if (stoppedjobs())
8034 return 0;
8035 if (argc > 1)
8036 exitstatus = number(argv[1]);
8037 else
8038 exitstatus = oexitstatus;
8039 exitshell(exitstatus);
8040 /* NOTREACHED */
8041}
Eric Andersen62483552001-07-10 06:09:16 +00008042
Eric Andersen2870d962001-07-02 17:27:21 +00008043static pointer
8044stalloc(int nbytes)
Eric Andersencb57d552001-06-28 07:25:16 +00008045{
8046 char *p;
8047
8048 nbytes = ALIGN(nbytes);
8049 if (nbytes > stacknleft) {
8050 int blocksize;
8051 struct stack_block *sp;
8052
8053 blocksize = nbytes;
8054 if (blocksize < MINSIZE)
8055 blocksize = MINSIZE;
8056 INTOFF;
8057 sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
8058 sp->prev = stackp;
8059 stacknxt = sp->space;
8060 stacknleft = blocksize;
8061 stackp = sp;
8062 INTON;
8063 }
8064 p = stacknxt;
8065 stacknxt += nbytes;
8066 stacknleft -= nbytes;
8067 return p;
8068}
8069
8070
8071static void
Eric Andersen2870d962001-07-02 17:27:21 +00008072stunalloc(pointer p)
8073{
Eric Andersencb57d552001-06-28 07:25:16 +00008074#ifdef DEBUG
Eric Andersen2870d962001-07-02 17:27:21 +00008075 if (p == NULL) { /*DEBUG */
Eric Andersencb57d552001-06-28 07:25:16 +00008076 write(2, "stunalloc\n", 10);
8077 abort();
8078 }
8079#endif
8080 if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) {
8081 p = stackp->space;
8082 }
8083 stacknleft += stacknxt - (char *)p;
8084 stacknxt = p;
8085}
8086
8087
Eric Andersencb57d552001-06-28 07:25:16 +00008088static void
Eric Andersen2870d962001-07-02 17:27:21 +00008089setstackmark(struct stackmark *mark)
8090{
Eric Andersencb57d552001-06-28 07:25:16 +00008091 mark->stackp = stackp;
8092 mark->stacknxt = stacknxt;
8093 mark->stacknleft = stacknleft;
8094 mark->marknext = markp;
8095 markp = mark;
8096}
8097
8098
8099static void
Eric Andersen2870d962001-07-02 17:27:21 +00008100popstackmark(struct stackmark *mark)
8101{
Eric Andersencb57d552001-06-28 07:25:16 +00008102 struct stack_block *sp;
8103
8104 INTOFF;
8105 markp = mark->marknext;
8106 while (stackp != mark->stackp) {
8107 sp = stackp;
8108 stackp = sp->prev;
8109 ckfree(sp);
8110 }
8111 stacknxt = mark->stacknxt;
8112 stacknleft = mark->stacknleft;
8113 INTON;
8114}
8115
8116
8117/*
8118 * When the parser reads in a string, it wants to stick the string on the
8119 * stack and only adjust the stack pointer when it knows how big the
8120 * string is. Stackblock (defined in stack.h) returns a pointer to a block
8121 * of space on top of the stack and stackblocklen returns the length of
8122 * this block. Growstackblock will grow this space by at least one byte,
8123 * possibly moving it (like realloc). Grabstackblock actually allocates the
8124 * part of the block that has been used.
8125 */
8126
8127static void
Eric Andersen2870d962001-07-02 17:27:21 +00008128growstackblock(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00008129 char *p;
8130 int newlen = ALIGN(stacknleft * 2 + 100);
8131 char *oldspace = stacknxt;
8132 int oldlen = stacknleft;
8133 struct stack_block *sp;
8134 struct stack_block *oldstackp;
8135
8136 if (stacknxt == stackp->space && stackp != &stackbase) {
8137 INTOFF;
8138 oldstackp = stackp;
8139 sp = stackp;
8140 stackp = sp->prev;
8141 sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
8142 sp->prev = stackp;
8143 stackp = sp;
8144 stacknxt = sp->space;
8145 stacknleft = newlen;
8146 {
8147 /* Stack marks pointing to the start of the old block
Eric Andersen2870d962001-07-02 17:27:21 +00008148 * must be relocated to point to the new block
Eric Andersencb57d552001-06-28 07:25:16 +00008149 */
8150 struct stackmark *xmark;
8151 xmark = markp;
8152 while (xmark != NULL && xmark->stackp == oldstackp) {
8153 xmark->stackp = stackp;
8154 xmark->stacknxt = stacknxt;
8155 xmark->stacknleft = stacknleft;
8156 xmark = xmark->marknext;
8157 }
8158 }
8159 INTON;
8160 } else {
8161 p = stalloc(newlen);
8162 memcpy(p, oldspace, oldlen);
Eric Andersen2870d962001-07-02 17:27:21 +00008163 stacknxt = p; /* free the space */
8164 stacknleft += newlen; /* we just allocated */
Eric Andersencb57d552001-06-28 07:25:16 +00008165 }
8166}
8167
8168
8169
Eric Andersen2870d962001-07-02 17:27:21 +00008170static inline void
8171grabstackblock(int len)
Eric Andersencb57d552001-06-28 07:25:16 +00008172{
8173 len = ALIGN(len);
8174 stacknxt += len;
8175 stacknleft -= len;
8176}
8177
8178
8179
8180/*
8181 * The following routines are somewhat easier to use that the above.
8182 * The user declares a variable of type STACKSTR, which may be declared
8183 * to be a register. The macro STARTSTACKSTR initializes things. Then
8184 * the user uses the macro STPUTC to add characters to the string. In
8185 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
8186 * grown as necessary. When the user is done, she can just leave the
8187 * string there and refer to it using stackblock(). Or she can allocate
8188 * the space for it using grabstackstr(). If it is necessary to allow
8189 * someone else to use the stack temporarily and then continue to grow
8190 * the string, the user should use grabstack to allocate the space, and
8191 * then call ungrabstr(p) to return to the previous mode of operation.
8192 *
8193 * USTPUTC is like STPUTC except that it doesn't check for overflow.
8194 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
8195 * is space for at least one character.
8196 */
8197
8198
8199static char *
Eric Andersen2870d962001-07-02 17:27:21 +00008200growstackstr(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00008201 int len = stackblocksize();
8202 if (herefd >= 0 && len >= 1024) {
8203 xwrite(herefd, stackblock(), len);
8204 sstrnleft = len - 1;
8205 return stackblock();
8206 }
8207 growstackblock();
8208 sstrnleft = stackblocksize() - len - 1;
8209 return stackblock() + len;
8210}
8211
8212
8213/*
8214 * Called from CHECKSTRSPACE.
8215 */
8216
8217static char *
8218makestrspace(size_t newlen) {
8219 int len = stackblocksize() - sstrnleft;
8220 do {
8221 growstackblock();
8222 sstrnleft = stackblocksize() - len;
8223 } while (sstrnleft < newlen);
8224 return stackblock() + len;
8225}
8226
8227
8228
8229static void
Eric Andersen2870d962001-07-02 17:27:21 +00008230ungrabstackstr(char *s, char *p)
8231{
Eric Andersencb57d552001-06-28 07:25:16 +00008232 stacknleft += stacknxt - s;
8233 stacknxt = s;
8234 sstrnleft = stacknleft - (p - s);
8235}
Eric Andersencb57d552001-06-28 07:25:16 +00008236/*
8237 * Miscelaneous builtins.
8238 */
8239
8240
8241#undef rflag
8242
Eric Andersencb57d552001-06-28 07:25:16 +00008243#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersen62483552001-07-10 06:09:16 +00008244typedef long rlim_t;
Eric Andersencb57d552001-06-28 07:25:16 +00008245#endif
8246
8247
8248
8249/*
8250 * The read builtin. The -e option causes backslashes to escape the
8251 * following character.
8252 *
8253 * This uses unbuffered input, which may be avoidable in some cases.
8254 */
8255
8256static int
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008257readcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008258{
8259 char **ap;
8260 int backslash;
8261 char c;
8262 int rflag;
8263 char *prompt;
8264 const char *ifs;
8265 char *p;
8266 int startword;
8267 int status;
8268 int i;
8269
8270 rflag = 0;
8271 prompt = NULL;
8272 while ((i = nextopt("p:r")) != '\0') {
8273 if (i == 'p')
8274 prompt = optionarg;
8275 else
8276 rflag = 1;
8277 }
8278 if (prompt && isatty(0)) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008279 out2str(prompt); /* read without cmdedit */
Eric Andersencb57d552001-06-28 07:25:16 +00008280 flushall();
8281 }
8282 if (*(ap = argptr) == NULL)
8283 error("arg count");
8284 if ((ifs = bltinlookup("IFS")) == NULL)
8285 ifs = defifs;
8286 status = 0;
8287 startword = 1;
8288 backslash = 0;
8289 STARTSTACKSTR(p);
8290 for (;;) {
8291 if (read(0, &c, 1) != 1) {
8292 status = 1;
8293 break;
8294 }
8295 if (c == '\0')
8296 continue;
8297 if (backslash) {
8298 backslash = 0;
8299 if (c != '\n')
8300 STPUTC(c, p);
8301 continue;
8302 }
8303 if (!rflag && c == '\\') {
8304 backslash++;
8305 continue;
8306 }
8307 if (c == '\n')
8308 break;
8309 if (startword && *ifs == ' ' && strchr(ifs, c)) {
8310 continue;
8311 }
8312 startword = 0;
8313 if (backslash && c == '\\') {
8314 if (read(0, &c, 1) != 1) {
8315 status = 1;
8316 break;
8317 }
8318 STPUTC(c, p);
8319 } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
8320 STACKSTRNUL(p);
8321 setvar(*ap, stackblock(), 0);
8322 ap++;
8323 startword = 1;
8324 STARTSTACKSTR(p);
8325 } else {
8326 STPUTC(c, p);
8327 }
8328 }
8329 STACKSTRNUL(p);
8330 /* Remove trailing blanks */
8331 while (stackblock() <= --p && strchr(ifs, *p) != NULL)
8332 *p = '\0';
8333 setvar(*ap, stackblock(), 0);
8334 while (*++ap != NULL)
8335 setvar(*ap, nullstr, 0);
8336 return status;
8337}
8338
8339
8340
8341static int
8342umaskcmd(argc, argv)
8343 int argc;
8344 char **argv;
8345{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008346 static const char permuser[3] = "ugo";
8347 static const char permmode[3] = "rwx";
8348 static const short int permmask[] = {
8349 S_IRUSR, S_IWUSR, S_IXUSR,
8350 S_IRGRP, S_IWGRP, S_IXGRP,
8351 S_IROTH, S_IWOTH, S_IXOTH
8352 };
8353
Eric Andersencb57d552001-06-28 07:25:16 +00008354 char *ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008355 mode_t mask;
Eric Andersencb57d552001-06-28 07:25:16 +00008356 int i;
8357 int symbolic_mode = 0;
8358
Eric Andersen62483552001-07-10 06:09:16 +00008359 while (nextopt("S") != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00008360 symbolic_mode = 1;
8361 }
8362
8363 INTOFF;
8364 mask = umask(0);
8365 umask(mask);
8366 INTON;
8367
8368 if ((ap = *argptr) == NULL) {
8369 if (symbolic_mode) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008370 char buf[18];
8371 char *p = buf;
8372 for (i=0 ; i<3 ; i++) {
8373 int j;
8374 *p++ = permuser[i];
8375 *p++ = '=';
8376 for (j=0 ; j<3 ; j++) {
8377 if ((mask & permmask[3*i+j]) == 0) {
8378 *p++ = permmode[j];
8379 }
8380 }
8381 *p++ = ',';
8382 }
8383 *--p = 0;
8384 puts(buf);
Eric Andersencb57d552001-06-28 07:25:16 +00008385 } else {
Eric Andersen62483552001-07-10 06:09:16 +00008386 printf("%.4o\n", mask);
Eric Andersencb57d552001-06-28 07:25:16 +00008387 }
8388 } else {
Eric Andersen62483552001-07-10 06:09:16 +00008389 if (is_digit((unsigned char)*ap)) {
Eric Andersencb57d552001-06-28 07:25:16 +00008390 mask = 0;
8391 do {
8392 if (*ap >= '8' || *ap < '0')
8393 error("Illegal number: %s", argv[1]);
8394 mask = (mask << 3) + (*ap - '0');
8395 } while (*++ap != '\0');
8396 umask(mask);
8397 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008398 mask = ~mask & 0777;
8399 if (parse_mode(ap, &mask) == FALSE) {
Eric Andersencb57d552001-06-28 07:25:16 +00008400 error("Illegal mode: %s", ap);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008401 }
Eric Andersencb57d552001-06-28 07:25:16 +00008402 umask(~mask & 0777);
8403 }
8404 }
8405 return 0;
8406}
8407
8408/*
8409 * ulimit builtin
8410 *
8411 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
8412 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
8413 * ash by J.T. Conklin.
8414 *
8415 * Public domain.
8416 */
8417
8418struct limits {
8419 const char *name;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008420 short cmd;
8421 short factor; /* multiply by to get rlim_{cur,max} values */
Eric Andersencb57d552001-06-28 07:25:16 +00008422};
8423
8424static const struct limits limits[] = {
8425#ifdef RLIMIT_CPU
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008426 { "time(seconds)", RLIMIT_CPU, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008427#endif
8428#ifdef RLIMIT_FSIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008429 { "file(blocks)", RLIMIT_FSIZE, 512 },
Eric Andersencb57d552001-06-28 07:25:16 +00008430#endif
8431#ifdef RLIMIT_DATA
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008432 { "data(kbytes)", RLIMIT_DATA, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008433#endif
8434#ifdef RLIMIT_STACK
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008435 { "stack(kbytes)", RLIMIT_STACK, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008436#endif
8437#ifdef RLIMIT_CORE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008438 { "coredump(blocks)", RLIMIT_CORE, 512 },
Eric Andersencb57d552001-06-28 07:25:16 +00008439#endif
8440#ifdef RLIMIT_RSS
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008441 { "memory(kbytes)", RLIMIT_RSS, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008442#endif
8443#ifdef RLIMIT_MEMLOCK
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008444 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008445#endif
8446#ifdef RLIMIT_NPROC
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008447 { "process(processes)", RLIMIT_NPROC, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008448#endif
8449#ifdef RLIMIT_NOFILE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008450 { "nofiles(descriptors)", RLIMIT_NOFILE, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008451#endif
8452#ifdef RLIMIT_VMEM
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008453 { "vmemory(kbytes)", RLIMIT_VMEM, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008454#endif
8455#ifdef RLIMIT_SWAP
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008456 { "swap(kbytes)", RLIMIT_SWAP, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008457#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008458 { NULL, 0, 0 }
Eric Andersencb57d552001-06-28 07:25:16 +00008459};
8460
8461static int
8462ulimitcmd(argc, argv)
8463 int argc;
8464 char **argv;
8465{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008466 static const char unlimited_string[] = "unlimited";
Eric Andersen2870d962001-07-02 17:27:21 +00008467 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008468 rlim_t val = 0;
8469 enum { SOFT = 0x1, HARD = 0x2 }
8470 how = SOFT | HARD;
Eric Andersen2870d962001-07-02 17:27:21 +00008471 const struct limits *l;
8472 int set, all = 0;
8473 int optc, what;
8474 struct rlimit limit;
Eric Andersencb57d552001-06-28 07:25:16 +00008475
8476 what = 'f';
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008477
8478 while ((optc = nextopt("HSa"
8479#ifdef RLIMIT_CPU
8480 "t"
8481#endif
8482#ifdef RLIMIT_FSIZE
8483 "f"
8484#endif
8485#ifdef RLIMIT_DATA
8486 "d"
8487#endif
8488#ifdef RLIMIT_STACK
8489 "s"
8490#endif
8491#ifdef RLIMIT_CORE
8492 "c"
8493#endif
8494#ifdef RLIMIT_RSS
8495 "m"
8496#endif
8497#ifdef RLIMIT_MEMLOCK
8498 "l"
8499#endif
8500#ifdef RLIMIT_NPROC
8501 "p"
8502#endif
8503#ifdef RLIMIT_NOFILE
8504 "n"
8505#endif
8506#ifdef RLIMIT_VMEM
8507 "v"
8508#endif
8509#ifdef RLIMIT_SWAP
8510 "w"
8511#endif
8512 )) != '\0') {
8513 if (optc == 'H') {
Eric Andersencb57d552001-06-28 07:25:16 +00008514 how = HARD;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008515 } else if (optc == 'S') {
Eric Andersencb57d552001-06-28 07:25:16 +00008516 how = SOFT;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008517 } else if (optc == 'a') {
Eric Andersencb57d552001-06-28 07:25:16 +00008518 all = 1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008519 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00008520 what = optc;
8521 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008522 }
Eric Andersencb57d552001-06-28 07:25:16 +00008523
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008524 for (l = limits; l->name; l++) {
8525 if(l->name[0] == what)
8526 break;
8527 if(l->name[1]=='w' && what=='w')
8528 break;
8529 }
Eric Andersencb57d552001-06-28 07:25:16 +00008530
8531 set = *argptr ? 1 : 0;
8532 if (set) {
8533 char *p = *argptr;
8534
8535 if (all || argptr[1])
8536 error("too many arguments");
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008537 if (strcmp(p, unlimited_string) == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00008538 val = RLIM_INFINITY;
8539 else {
8540 val = (rlim_t) 0;
8541
8542 while ((c = *p++) >= '0' && c <= '9')
8543 {
8544 val = (val * 10) + (long)(c - '0');
8545 if (val < (rlim_t) 0)
8546 break;
8547 }
8548 if (c)
8549 error("bad number");
8550 val *= l->factor;
8551 }
8552 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008553
Eric Andersencb57d552001-06-28 07:25:16 +00008554 if (all) {
8555 for (l = limits; l->name; l++) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008556 printf("%-20s ", l->name);
Eric Andersencb57d552001-06-28 07:25:16 +00008557 getrlimit(l->cmd, &limit);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008558 OUTPUT_LIMIT:
Eric Andersencb57d552001-06-28 07:25:16 +00008559 if (how & SOFT)
8560 val = limit.rlim_cur;
8561 else if (how & HARD)
8562 val = limit.rlim_max;
8563
Eric Andersencb57d552001-06-28 07:25:16 +00008564 if (val == RLIM_INFINITY)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008565 puts(unlimited_string);
Eric Andersencb57d552001-06-28 07:25:16 +00008566 else
8567 {
8568 val /= l->factor;
Eric Andersen62483552001-07-10 06:09:16 +00008569 printf("%lld\n", (long long) val);
Eric Andersencb57d552001-06-28 07:25:16 +00008570 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008571 if (!all) {
8572 break;
8573 }
Eric Andersencb57d552001-06-28 07:25:16 +00008574 }
8575 return 0;
8576 }
8577
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008578 if (!set) {
8579 goto OUTPUT_LIMIT;
Eric Andersencb57d552001-06-28 07:25:16 +00008580 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008581
8582 getrlimit(l->cmd, &limit);
8583 if (how & HARD)
8584 limit.rlim_max = val;
8585 if (how & SOFT)
8586 limit.rlim_cur = val;
8587 if (setrlimit(l->cmd, &limit) < 0)
8588 error("error setting limit (%m)");
Eric Andersencb57d552001-06-28 07:25:16 +00008589 return 0;
8590}
Eric Andersencb57d552001-06-28 07:25:16 +00008591/*
8592 * prefix -- see if pfx is a prefix of string.
8593 */
8594
8595static int
Eric Andersen62483552001-07-10 06:09:16 +00008596prefix(char const *pfx, char const *string)
8597{
Eric Andersencb57d552001-06-28 07:25:16 +00008598 while (*pfx) {
8599 if (*pfx++ != *string++)
8600 return 0;
8601 }
8602 return 1;
8603}
8604
Eric Andersen2870d962001-07-02 17:27:21 +00008605/*
8606 * Return true if s is a string of digits, and save munber in intptr
8607 * nagative is bad
8608 */
8609
8610static int
8611is_number(const char *p, int *intptr)
8612{
8613 int ret = 0;
8614
8615 do {
8616 if (! is_digit(*p))
8617 return 0;
8618 ret *= 10;
8619 ret += digit_val(*p);
8620 p++;
8621 } while (*p != '\0');
8622
8623 *intptr = ret;
8624 return 1;
8625}
Eric Andersencb57d552001-06-28 07:25:16 +00008626
8627/*
8628 * Convert a string of digits to an integer, printing an error message on
8629 * failure.
8630 */
8631
8632static int
Eric Andersen2870d962001-07-02 17:27:21 +00008633number(const char *s)
8634{
8635 int i;
8636 if (! is_number(s, &i))
Eric Andersencb57d552001-06-28 07:25:16 +00008637 error("Illegal number: %s", s);
Eric Andersen2870d962001-07-02 17:27:21 +00008638 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00008639}
8640
Eric Andersencb57d552001-06-28 07:25:16 +00008641/*
8642 * Produce a possibly single quoted string suitable as input to the shell.
8643 * The return string is allocated on the stack.
8644 */
8645
8646static char *
8647single_quote(const char *s) {
8648 char *p;
8649
8650 STARTSTACKSTR(p);
8651
8652 do {
8653 char *q = p;
8654 size_t len1, len1p, len2, len2p;
8655
8656 len1 = strcspn(s, "'");
8657 len2 = strspn(s + len1, "'");
8658
8659 len1p = len1 ? len1 + 2 : len1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008660 len2p = len2 + ((len2 < 2) ? len2 : 2);
Eric Andersencb57d552001-06-28 07:25:16 +00008661
8662 CHECKSTRSPACE(len1p + len2p + 1, p);
8663
8664 if (len1) {
8665 *p = '\'';
Eric Andersencb57d552001-06-28 07:25:16 +00008666 q = p + 1 + len1;
8667 memcpy(p + 1, s, len1);
Eric Andersencb57d552001-06-28 07:25:16 +00008668 *q++ = '\'';
8669 s += len1;
8670 }
8671
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008672 if (len2 > 1) {
Eric Andersencb57d552001-06-28 07:25:16 +00008673 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008674 q += 1 + len2;
8675 memcpy(q + 1, s, len2);
8676 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008677 s += len2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008678 } else if (len2 == 1) {
8679 *q++ = '\\';
8680 *q = '\'';
8681 s++;
Eric Andersencb57d552001-06-28 07:25:16 +00008682 }
8683
8684 STADJUST(len1p + len2p, p);
8685 } while (*s);
8686
8687 USTPUTC(0, p);
8688
8689 return grabstackstr(p);
8690}
8691
8692/*
8693 * Like strdup but works with the ash stack.
8694 */
8695
8696static char *
8697sstrdup(const char *p)
8698{
8699 size_t len = strlen(p) + 1;
8700 return memcpy(stalloc(len), p, len);
8701}
8702
Eric Andersencb57d552001-06-28 07:25:16 +00008703
8704/*
Eric Andersencb57d552001-06-28 07:25:16 +00008705 * Routine for dealing with parsed shell commands.
8706 */
8707
8708
Eric Andersen62483552001-07-10 06:09:16 +00008709static void sizenodelist (const struct nodelist *);
8710static struct nodelist *copynodelist (const struct nodelist *);
8711static char *nodesavestr (const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00008712
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00008713#define CALCSIZE_TABLE
8714#define COPYNODE_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008715#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE)
8716/*
8717 * To collect a lot of redundant code in case statements for copynode()
8718 * and calcsize(), we implement a mini language here. Each type of node
8719 * struct has an associated instruction sequence that operates on its
8720 * members via their offsets. The instruction are pack in unsigned chars
8721 * with format IIDDDDDE where the bits are
8722 * I : part of the instruction opcode, which are
8723 * 00 : member is a pointer to another node
8724 * 40 : member is an integer
8725 * 80 : member is a pointer to a nodelist
8726 * CC : member is a pointer to a char string
8727 * D : data - the actual offset of the member to operate on in the struct
8728 * (since we assume bit 0 is set, it is not shifted)
8729 * E : flag signaling end of instruction sequence
8730 *
8731 * WARNING: In order to handle larger offsets for 64bit archs, this code
8732 * assumes that no offset can be an odd number and stores the
8733 * end-of-instructions flag in bit 0.
8734 */
8735
8736#define NODE_INTEGER 0x40
8737#define NODE_NODELIST 0x80
8738#define NODE_CHARPTR 0xC0
8739#define NODE_NOMORE 0x01 /* Note: no offset should be odd (aligned)*/
8740#define NODE_MBRMASK 0xC0
8741#define NODE_OFFSETMASK 0x3E
8742
8743static const unsigned char copynode_ops[35] = {
8744#define COPYNODE_OPS0 0
8745 offsetof(union node, nbinary.ch2),
8746 offsetof(union node, nbinary.ch1)|NODE_NOMORE,
8747#define COPYNODE_OPS1 (COPYNODE_OPS0 + 2)
8748 offsetof(union node, ncmd.redirect),
8749 offsetof(union node, ncmd.args),
8750 offsetof(union node, ncmd.assign),
8751 offsetof(union node, ncmd.backgnd)|NODE_INTEGER|NODE_NOMORE,
8752#define COPYNODE_OPS2 (COPYNODE_OPS1 + 4)
8753 offsetof(union node, npipe.cmdlist)|NODE_NODELIST,
8754 offsetof(union node, npipe.backgnd)|NODE_INTEGER|NODE_NOMORE,
8755#define COPYNODE_OPS3 (COPYNODE_OPS2 + 2)
8756 offsetof(union node, nredir.redirect),
8757 offsetof(union node, nredir.n)|NODE_NOMORE,
8758#define COPYNODE_OPS4 (COPYNODE_OPS3 + 2)
8759 offsetof(union node, nif.elsepart),
8760 offsetof(union node, nif.ifpart),
8761 offsetof(union node, nif.test)|NODE_NOMORE,
8762#define COPYNODE_OPS5 (COPYNODE_OPS4 + 3)
8763 offsetof(union node, nfor.var)|NODE_CHARPTR,
8764 offsetof(union node, nfor.body),
8765 offsetof(union node, nfor.args)|NODE_NOMORE,
8766#define COPYNODE_OPS6 (COPYNODE_OPS5 + 3)
8767 offsetof(union node, ncase.cases),
8768 offsetof(union node, ncase.expr)|NODE_NOMORE,
8769#define COPYNODE_OPS7 (COPYNODE_OPS6 + 2)
8770 offsetof(union node, nclist.body),
8771 offsetof(union node, nclist.pattern),
8772 offsetof(union node, nclist.next)|NODE_NOMORE,
8773#define COPYNODE_OPS8 (COPYNODE_OPS7 + 3)
8774 offsetof(union node, narg.backquote)|NODE_NODELIST,
8775 offsetof(union node, narg.text)|NODE_CHARPTR,
8776 offsetof(union node, narg.next)|NODE_NOMORE,
8777#define COPYNODE_OPS9 (COPYNODE_OPS8 + 3)
8778 offsetof(union node, nfile.fname),
8779 offsetof(union node, nfile.fd)|NODE_INTEGER,
8780 offsetof(union node, nfile.next)|NODE_NOMORE,
8781#define COPYNODE_OPS10 (COPYNODE_OPS9 + 3)
8782 offsetof(union node, ndup.vname),
8783 offsetof(union node, ndup.dupfd)|NODE_INTEGER,
8784 offsetof(union node, ndup.fd)|NODE_INTEGER,
8785 offsetof(union node, ndup.next)|NODE_NOMORE,
8786#define COPYNODE_OPS11 (COPYNODE_OPS10 + 4)
8787 offsetof(union node, nhere.doc),
8788 offsetof(union node, nhere.fd)|NODE_INTEGER,
8789 offsetof(union node, nhere.next)|NODE_NOMORE,
8790#define COPYNODE_OPS12 (COPYNODE_OPS11 + 3)
8791 offsetof(union node, nnot.com)|NODE_NOMORE,
8792};
8793
8794#if COPYNODE_OPS12 != 34
8795#error COPYNODE_OPS12 is incorrect
8796#endif
8797
8798static const unsigned char copynode_ops_index[26] = {
8799 COPYNODE_OPS0, /* NSEMI */
8800 COPYNODE_OPS1, /* NCMD */
8801 COPYNODE_OPS2, /* NPIPE */
8802 COPYNODE_OPS3, /* NREDIR */
8803 COPYNODE_OPS3, /* NBACKGND */
8804 COPYNODE_OPS3, /* NSUBSHELL */
8805 COPYNODE_OPS0, /* NAND */
8806 COPYNODE_OPS0, /* NOR */
8807 COPYNODE_OPS4, /* NIF */
8808 COPYNODE_OPS0, /* NWHILE */
8809 COPYNODE_OPS0, /* NUNTIL */
8810 COPYNODE_OPS5, /* NFOR */
8811 COPYNODE_OPS6, /* NCASE */
8812 COPYNODE_OPS7, /* NCLIST */
8813 COPYNODE_OPS8, /* NDEFUN */
8814 COPYNODE_OPS8, /* NARG */
8815 COPYNODE_OPS9, /* NTO */
8816 COPYNODE_OPS9, /* NFROM */
8817 COPYNODE_OPS9, /* NFROMTO */
8818 COPYNODE_OPS9, /* NAPPEND */
8819 COPYNODE_OPS9, /* NTOOV */
8820 COPYNODE_OPS10, /* NTOFD */
8821 COPYNODE_OPS10, /* NFROMFD */
8822 COPYNODE_OPS11, /* NHERE */
8823 COPYNODE_OPS11, /* NXHERE */
8824 COPYNODE_OPS12, /* NNOT */
8825};
8826
8827#if NODE_CHARPTR != NODE_MBRMASK
8828#error NODE_CHARPTR != NODE_MBRMASK!!!
8829#endif
8830#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
8831
8832#ifdef COPYNODE_TABLE
8833static union node *
8834copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008835{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008836 union node *new;
8837 const unsigned char *p;
8838
Manuel Novoa III c639a352001-08-12 17:32:56 +00008839 if (n == NULL) {
8840 return NULL;
8841 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008842 new = funcblock;
8843 new->type = n->type;
8844 funcblock = (char *) funcblock + (int) nodesize[n->type];
8845 p = copynode_ops + (int) copynode_ops_index[n->type];
8846 do {
8847 char *nn = ((char *) new) + ((int)(*p & NODE_OFFSETMASK));
8848 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
8849
8850 if (!(*p & NODE_MBRMASK)) { /* standard node */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008851 *((union node **)nn) = copynode(*((const union node **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008852 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008853 *((const char **)nn) = nodesavestr(*((const char **)no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008854 } else if (*p & NODE_NODELIST) { /* nodelist */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008855 *((struct nodelist **)nn)
8856 = copynodelist(*((const struct nodelist **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008857 } else { /* integer */
8858 *((int *) nn) = *((int *) no);
8859 }
8860 } while (!(*p++ & NODE_NOMORE));
8861 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008862}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008863#else /* COPYNODE_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00008864static union node *
Eric Andersen62483552001-07-10 06:09:16 +00008865copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008866{
Eric Andersen62483552001-07-10 06:09:16 +00008867 union node *new;
Eric Andersencb57d552001-06-28 07:25:16 +00008868
8869 if (n == NULL)
Manuel Novoa III c639a352001-08-12 17:32:56 +00008870 return NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008871 new = funcblock;
8872 funcblock = (char *) funcblock + nodesize[n->type];
8873 switch (n->type) {
8874 case NSEMI:
8875 case NAND:
8876 case NOR:
8877 case NWHILE:
8878 case NUNTIL:
8879 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8880 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8881 break;
8882 case NCMD:
8883 new->ncmd.redirect = copynode(n->ncmd.redirect);
8884 new->ncmd.args = copynode(n->ncmd.args);
8885 new->ncmd.assign = copynode(n->ncmd.assign);
8886 new->ncmd.backgnd = n->ncmd.backgnd;
8887 break;
8888 case NPIPE:
8889 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8890 new->npipe.backgnd = n->npipe.backgnd;
8891 break;
8892 case NREDIR:
8893 case NBACKGND:
8894 case NSUBSHELL:
8895 new->nredir.redirect = copynode(n->nredir.redirect);
8896 new->nredir.n = copynode(n->nredir.n);
8897 break;
8898 case NIF:
8899 new->nif.elsepart = copynode(n->nif.elsepart);
8900 new->nif.ifpart = copynode(n->nif.ifpart);
8901 new->nif.test = copynode(n->nif.test);
8902 break;
8903 case NFOR:
8904 new->nfor.var = nodesavestr(n->nfor.var);
8905 new->nfor.body = copynode(n->nfor.body);
8906 new->nfor.args = copynode(n->nfor.args);
8907 break;
8908 case NCASE:
8909 new->ncase.cases = copynode(n->ncase.cases);
8910 new->ncase.expr = copynode(n->ncase.expr);
8911 break;
8912 case NCLIST:
8913 new->nclist.body = copynode(n->nclist.body);
8914 new->nclist.pattern = copynode(n->nclist.pattern);
8915 new->nclist.next = copynode(n->nclist.next);
8916 break;
8917 case NDEFUN:
8918 case NARG:
8919 new->narg.backquote = copynodelist(n->narg.backquote);
8920 new->narg.text = nodesavestr(n->narg.text);
8921 new->narg.next = copynode(n->narg.next);
8922 break;
8923 case NTO:
8924 case NFROM:
8925 case NFROMTO:
8926 case NAPPEND:
8927 case NTOOV:
8928 new->nfile.fname = copynode(n->nfile.fname);
8929 new->nfile.fd = n->nfile.fd;
8930 new->nfile.next = copynode(n->nfile.next);
8931 break;
8932 case NTOFD:
8933 case NFROMFD:
8934 new->ndup.vname = copynode(n->ndup.vname);
8935 new->ndup.dupfd = n->ndup.dupfd;
8936 new->ndup.fd = n->ndup.fd;
8937 new->ndup.next = copynode(n->ndup.next);
8938 break;
8939 case NHERE:
8940 case NXHERE:
8941 new->nhere.doc = copynode(n->nhere.doc);
8942 new->nhere.fd = n->nhere.fd;
8943 new->nhere.next = copynode(n->nhere.next);
8944 break;
8945 case NNOT:
8946 new->nnot.com = copynode(n->nnot.com);
8947 break;
8948 };
8949 new->type = n->type;
Eric Andersen62483552001-07-10 06:09:16 +00008950 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008951}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008952#endif /* COPYNODE_TABLE */
8953
8954#ifdef CALCSIZE_TABLE
8955static void
8956calcsize(const union node *n)
8957{
8958 const unsigned char *p;
8959
8960 if (n == NULL)
8961 return;
8962 funcblocksize += (int) nodesize[n->type];
8963
8964 p = copynode_ops + (int) copynode_ops_index[n->type];
8965 do {
8966 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
8967
8968 if (!(*p & NODE_MBRMASK)) { /* standard node */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008969 calcsize(*((const union node **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008970 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008971 funcstringsize += strlen(*((const char **)no)) + 1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008972 } else if (*p & NODE_NODELIST) { /* nodelist */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008973 sizenodelist(*((const struct nodelist **) no));
8974 } /* else integer -- ignore */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008975 } while (!(*p++ & NODE_NOMORE));
8976}
8977#else /* CALCSIZE_TABLE */
8978static void
8979calcsize(const union node *n)
8980{
8981 if (n == NULL)
8982 return;
8983 funcblocksize += nodesize[n->type];
8984 switch (n->type) {
8985 case NSEMI:
8986 case NAND:
8987 case NOR:
8988 case NWHILE:
8989 case NUNTIL:
8990 calcsize(n->nbinary.ch2);
8991 calcsize(n->nbinary.ch1);
8992 break;
8993 case NCMD:
8994 calcsize(n->ncmd.redirect);
8995 calcsize(n->ncmd.args);
8996 calcsize(n->ncmd.assign);
8997 break;
8998 case NPIPE:
8999 sizenodelist(n->npipe.cmdlist);
9000 break;
9001 case NREDIR:
9002 case NBACKGND:
9003 case NSUBSHELL:
9004 calcsize(n->nredir.redirect);
9005 calcsize(n->nredir.n);
9006 break;
9007 case NIF:
9008 calcsize(n->nif.elsepart);
9009 calcsize(n->nif.ifpart);
9010 calcsize(n->nif.test);
9011 break;
9012 case NFOR:
9013 funcstringsize += strlen(n->nfor.var) + 1;
9014 calcsize(n->nfor.body);
9015 calcsize(n->nfor.args);
9016 break;
9017 case NCASE:
9018 calcsize(n->ncase.cases);
9019 calcsize(n->ncase.expr);
9020 break;
9021 case NCLIST:
9022 calcsize(n->nclist.body);
9023 calcsize(n->nclist.pattern);
9024 calcsize(n->nclist.next);
9025 break;
9026 case NDEFUN:
9027 case NARG:
9028 sizenodelist(n->narg.backquote);
9029 funcstringsize += strlen(n->narg.text) + 1;
9030 calcsize(n->narg.next);
9031 break;
9032 case NTO:
9033 case NFROM:
9034 case NFROMTO:
9035 case NAPPEND:
9036 case NTOOV:
9037 calcsize(n->nfile.fname);
9038 calcsize(n->nfile.next);
9039 break;
9040 case NTOFD:
9041 case NFROMFD:
9042 calcsize(n->ndup.vname);
9043 calcsize(n->ndup.next);
9044 break;
9045 case NHERE:
9046 case NXHERE:
9047 calcsize(n->nhere.doc);
9048 calcsize(n->nhere.next);
9049 break;
9050 case NNOT:
9051 calcsize(n->nnot.com);
9052 break;
9053 };
9054}
9055#endif /* CALCSIZE_TABLE */
9056
9057static void
9058sizenodelist(const struct nodelist *lp)
9059{
9060 while (lp) {
9061 funcblocksize += ALIGN(sizeof(struct nodelist));
9062 calcsize(lp->n);
9063 lp = lp->next;
9064 }
9065}
Eric Andersencb57d552001-06-28 07:25:16 +00009066
9067
9068static struct nodelist *
Eric Andersen62483552001-07-10 06:09:16 +00009069copynodelist(const struct nodelist *lp)
Eric Andersencb57d552001-06-28 07:25:16 +00009070{
9071 struct nodelist *start;
9072 struct nodelist **lpp;
9073
9074 lpp = &start;
9075 while (lp) {
9076 *lpp = funcblock;
9077 funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist));
9078 (*lpp)->n = copynode(lp->n);
9079 lp = lp->next;
9080 lpp = &(*lpp)->next;
9081 }
9082 *lpp = NULL;
9083 return start;
9084}
9085
9086
Eric Andersencb57d552001-06-28 07:25:16 +00009087static char *
Eric Andersen62483552001-07-10 06:09:16 +00009088nodesavestr(const char *s)
Eric Andersencb57d552001-06-28 07:25:16 +00009089{
Eric Andersen62483552001-07-10 06:09:16 +00009090 const char *p = s;
9091 char *q = funcstring;
Eric Andersencb57d552001-06-28 07:25:16 +00009092 char *rtn = funcstring;
9093
9094 while ((*q++ = *p++) != '\0')
9095 continue;
9096 funcstring = q;
9097 return rtn;
Eric Andersencb57d552001-06-28 07:25:16 +00009098}
9099
Eric Andersencb57d552001-06-28 07:25:16 +00009100#ifdef ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00009101static int getopts (char *, char *, char **, int *, int *);
Eric Andersencb57d552001-06-28 07:25:16 +00009102#endif
9103
9104
9105/*
9106 * Process the shell command line arguments.
9107 */
9108
9109static void
9110procargs(argc, argv)
9111 int argc;
9112 char **argv;
9113{
9114 int i;
9115
9116 argptr = argv;
9117 if (argc > 0)
9118 argptr++;
9119 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00009120 optent_val(i) = 2;
Eric Andersencb57d552001-06-28 07:25:16 +00009121 options(1);
9122 if (*argptr == NULL && minusc == NULL)
9123 sflag = 1;
9124 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
9125 iflag = 1;
9126 if (mflag == 2)
9127 mflag = iflag;
9128 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00009129 if (optent_val(i) == 2)
9130 optent_val(i) = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009131 arg0 = argv[0];
9132 if (sflag == 0 && minusc == NULL) {
9133 commandname = argv[0];
9134 arg0 = *argptr++;
9135 setinputfile(arg0, 0);
9136 commandname = arg0;
9137 }
9138 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
9139 if (argptr && minusc && *argptr)
Eric Andersen2870d962001-07-02 17:27:21 +00009140 arg0 = *argptr++;
Eric Andersencb57d552001-06-28 07:25:16 +00009141
9142 shellparam.p = argptr;
9143 shellparam.optind = 1;
9144 shellparam.optoff = -1;
9145 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
9146 while (*argptr) {
9147 shellparam.nparam++;
9148 argptr++;
9149 }
9150 optschanged();
9151}
9152
9153
Eric Andersencb57d552001-06-28 07:25:16 +00009154
9155/*
9156 * Process shell options. The global variable argptr contains a pointer
9157 * to the argument list; we advance it past the options.
9158 */
9159
Eric Andersen62483552001-07-10 06:09:16 +00009160static inline void
9161minus_o(const char *name, int val)
9162{
9163 int i;
9164
9165 if (name == NULL) {
9166 out1str("Current option settings\n");
9167 for (i = 0; i < NOPTS; i++)
9168 printf("%-16s%s\n", optent_name(optlist[i]),
9169 optent_val(i) ? "on" : "off");
9170 } else {
9171 for (i = 0; i < NOPTS; i++)
9172 if (equal(name, optent_name(optlist[i]))) {
9173 setoption(optent_letter(optlist[i]), val);
9174 return;
9175 }
9176 error("Illegal option -o %s", name);
9177 }
9178}
9179
9180
Eric Andersencb57d552001-06-28 07:25:16 +00009181static void
Eric Andersen62483552001-07-10 06:09:16 +00009182options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009183{
9184 char *p;
9185 int val;
9186 int c;
9187
9188 if (cmdline)
9189 minusc = NULL;
9190 while ((p = *argptr) != NULL) {
9191 argptr++;
9192 if ((c = *p++) == '-') {
9193 val = 1;
Eric Andersen2870d962001-07-02 17:27:21 +00009194 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
9195 if (!cmdline) {
9196 /* "-" means turn off -x and -v */
9197 if (p[0] == '\0')
9198 xflag = vflag = 0;
9199 /* "--" means reset params */
9200 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009201 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009202 }
9203 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009204 }
9205 } else if (c == '+') {
9206 val = 0;
9207 } else {
9208 argptr--;
9209 break;
9210 }
9211 while ((c = *p++) != '\0') {
9212 if (c == 'c' && cmdline) {
9213 char *q;
Eric Andersen2870d962001-07-02 17:27:21 +00009214#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */
Eric Andersencb57d552001-06-28 07:25:16 +00009215 if (*p == '\0')
9216#endif
9217 q = *argptr++;
9218 if (q == NULL || minusc != NULL)
9219 error("Bad -c option");
9220 minusc = q;
9221#ifdef NOHACK
9222 break;
9223#endif
9224 } else if (c == 'o') {
9225 minus_o(*argptr, val);
9226 if (*argptr)
9227 argptr++;
9228 } else {
9229 setoption(c, val);
9230 }
9231 }
9232 }
9233}
9234
Eric Andersencb57d552001-06-28 07:25:16 +00009235
9236static void
Eric Andersen2870d962001-07-02 17:27:21 +00009237setoption(int flag, int val)
9238{
Eric Andersencb57d552001-06-28 07:25:16 +00009239 int i;
9240
9241 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00009242 if (optent_letter(optlist[i]) == flag) {
9243 optent_val(i) = val;
Eric Andersencb57d552001-06-28 07:25:16 +00009244 if (val) {
9245 /* #%$ hack for ksh semantics */
9246 if (flag == 'V')
9247 Eflag = 0;
9248 else if (flag == 'E')
9249 Vflag = 0;
9250 }
9251 return;
9252 }
9253 error("Illegal option -%c", flag);
9254 /* NOTREACHED */
9255}
9256
9257
9258
Eric Andersencb57d552001-06-28 07:25:16 +00009259/*
9260 * Set the shell parameters.
9261 */
9262
9263static void
Eric Andersen2870d962001-07-02 17:27:21 +00009264setparam(char **argv)
9265{
Eric Andersencb57d552001-06-28 07:25:16 +00009266 char **newparam;
9267 char **ap;
9268 int nparam;
9269
9270 for (nparam = 0 ; argv[nparam] ; nparam++);
9271 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
9272 while (*argv) {
9273 *ap++ = savestr(*argv++);
9274 }
9275 *ap = NULL;
9276 freeparam(&shellparam);
9277 shellparam.malloc = 1;
9278 shellparam.nparam = nparam;
9279 shellparam.p = newparam;
9280 shellparam.optind = 1;
9281 shellparam.optoff = -1;
9282}
9283
9284
9285/*
9286 * Free the list of positional parameters.
9287 */
9288
9289static void
Eric Andersen2870d962001-07-02 17:27:21 +00009290freeparam(volatile struct shparam *param)
9291{
Eric Andersencb57d552001-06-28 07:25:16 +00009292 char **ap;
9293
9294 if (param->malloc) {
9295 for (ap = param->p ; *ap ; ap++)
9296 ckfree(*ap);
9297 ckfree(param->p);
9298 }
9299}
9300
9301
9302
9303/*
9304 * The shift builtin command.
9305 */
9306
9307static int
9308shiftcmd(argc, argv)
9309 int argc;
9310 char **argv;
9311{
9312 int n;
9313 char **ap1, **ap2;
9314
9315 n = 1;
9316 if (argc > 1)
9317 n = number(argv[1]);
9318 if (n > shellparam.nparam)
9319 error("can't shift that many");
9320 INTOFF;
9321 shellparam.nparam -= n;
9322 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
9323 if (shellparam.malloc)
9324 ckfree(*ap1);
9325 }
9326 ap2 = shellparam.p;
9327 while ((*ap2++ = *ap1++) != NULL);
9328 shellparam.optind = 1;
9329 shellparam.optoff = -1;
9330 INTON;
9331 return 0;
9332}
9333
9334
9335
9336/*
9337 * The set command builtin.
9338 */
9339
9340static int
9341setcmd(argc, argv)
9342 int argc;
9343 char **argv;
9344{
9345 if (argc == 1)
9346 return showvarscmd(argc, argv);
9347 INTOFF;
9348 options(0);
9349 optschanged();
9350 if (*argptr != NULL) {
9351 setparam(argptr);
9352 }
9353 INTON;
9354 return 0;
9355}
9356
9357
9358static void
Eric Andersen2870d962001-07-02 17:27:21 +00009359getoptsreset(const char *value)
Eric Andersencb57d552001-06-28 07:25:16 +00009360{
9361 shellparam.optind = number(value);
9362 shellparam.optoff = -1;
9363}
9364
Eric Andersen2870d962001-07-02 17:27:21 +00009365#ifdef BB_LOCALE_SUPPORT
9366static void change_lc_all(const char *value)
9367{
9368 if(value != 0 && *value != 0)
9369 setlocale(LC_ALL, value);
9370}
9371
9372static void change_lc_ctype(const char *value)
9373{
9374 if(value != 0 && *value != 0)
9375 setlocale(LC_CTYPE, value);
9376}
9377
9378#endif
9379
Eric Andersencb57d552001-06-28 07:25:16 +00009380#ifdef ASH_GETOPTS
9381/*
9382 * The getopts builtin. Shellparam.optnext points to the next argument
9383 * to be processed. Shellparam.optptr points to the next character to
9384 * be processed in the current argument. If shellparam.optnext is NULL,
9385 * then it's the first time getopts has been called.
9386 */
9387
9388static int
9389getoptscmd(argc, argv)
9390 int argc;
9391 char **argv;
9392{
9393 char **optbase;
9394
9395 if (argc < 3)
9396 error("Usage: getopts optstring var [arg]");
9397 else if (argc == 3) {
9398 optbase = shellparam.p;
9399 if (shellparam.optind > shellparam.nparam + 1) {
9400 shellparam.optind = 1;
9401 shellparam.optoff = -1;
9402 }
9403 }
9404 else {
9405 optbase = &argv[3];
9406 if (shellparam.optind > argc - 2) {
9407 shellparam.optind = 1;
9408 shellparam.optoff = -1;
9409 }
9410 }
9411
9412 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
9413 &shellparam.optoff);
9414}
9415
9416/*
9417 * Safe version of setvar, returns 1 on success 0 on failure.
9418 */
9419
9420static int
9421setvarsafe(name, val, flags)
9422 const char *name, *val;
9423 int flags;
9424{
9425 struct jmploc jmploc;
9426 struct jmploc *volatile savehandler = handler;
9427 int err = 0;
9428#ifdef __GNUC__
9429 (void) &err;
9430#endif
9431
9432 if (setjmp(jmploc.loc))
9433 err = 1;
9434 else {
9435 handler = &jmploc;
9436 setvar(name, val, flags);
9437 }
9438 handler = savehandler;
9439 return err;
9440}
9441
9442static int
9443getopts(optstr, optvar, optfirst, myoptind, optoff)
9444 char *optstr;
9445 char *optvar;
9446 char **optfirst;
9447 int *myoptind;
9448 int *optoff;
9449{
9450 char *p, *q;
9451 char c = '?';
9452 int done = 0;
9453 int err = 0;
9454 char s[10];
9455 char **optnext = optfirst + *myoptind - 1;
9456
9457 if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
9458 strlen(*(optnext - 1)) < *optoff)
9459 p = NULL;
9460 else
9461 p = *(optnext - 1) + *optoff;
9462 if (p == NULL || *p == '\0') {
9463 /* Current word is done, advance */
9464 if (optnext == NULL)
9465 return 1;
9466 p = *optnext;
9467 if (p == NULL || *p != '-' || *++p == '\0') {
9468atend:
9469 *myoptind = optnext - optfirst + 1;
9470 p = NULL;
9471 done = 1;
9472 goto out;
9473 }
9474 optnext++;
Eric Andersen2870d962001-07-02 17:27:21 +00009475 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009476 goto atend;
9477 }
9478
9479 c = *p++;
9480 for (q = optstr; *q != c; ) {
9481 if (*q == '\0') {
9482 if (optstr[0] == ':') {
9483 s[0] = c;
9484 s[1] = '\0';
9485 err |= setvarsafe("OPTARG", s, 0);
9486 }
9487 else {
Eric Andersen3102ac42001-07-06 04:26:23 +00009488 out2fmt("Illegal option -%c\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00009489 (void) unsetvar("OPTARG");
9490 }
9491 c = '?';
9492 goto bad;
9493 }
9494 if (*++q == ':')
9495 q++;
9496 }
9497
9498 if (*++q == ':') {
9499 if (*p == '\0' && (p = *optnext) == NULL) {
9500 if (optstr[0] == ':') {
9501 s[0] = c;
9502 s[1] = '\0';
9503 err |= setvarsafe("OPTARG", s, 0);
9504 c = ':';
9505 }
9506 else {
Eric Andersen3102ac42001-07-06 04:26:23 +00009507 out2fmt("No arg for -%c option\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00009508 (void) unsetvar("OPTARG");
9509 c = '?';
9510 }
9511 goto bad;
9512 }
9513
9514 if (p == *optnext)
9515 optnext++;
9516 setvarsafe("OPTARG", p, 0);
9517 p = NULL;
9518 }
9519 else
9520 setvarsafe("OPTARG", "", 0);
9521 *myoptind = optnext - optfirst + 1;
9522 goto out;
9523
9524bad:
9525 *myoptind = 1;
9526 p = NULL;
9527out:
9528 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersen3102ac42001-07-06 04:26:23 +00009529 snprintf(s, sizeof(s), "%d", *myoptind);
Eric Andersencb57d552001-06-28 07:25:16 +00009530 err |= setvarsafe("OPTIND", s, VNOFUNC);
9531 s[0] = c;
9532 s[1] = '\0';
9533 err |= setvarsafe(optvar, s, 0);
9534 if (err) {
9535 *myoptind = 1;
9536 *optoff = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00009537 exraise(EXERROR);
9538 }
9539 return done;
9540}
Eric Andersen2870d962001-07-02 17:27:21 +00009541#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009542
9543/*
9544 * XXX - should get rid of. have all builtins use getopt(3). the
9545 * library getopt must have the BSD extension static variable "optreset"
9546 * otherwise it can't be used within the shell safely.
9547 *
9548 * Standard option processing (a la getopt) for builtin routines. The
9549 * only argument that is passed to nextopt is the option string; the
9550 * other arguments are unnecessary. It return the character, or '\0' on
9551 * end of input.
9552 */
9553
9554static int
Eric Andersen62483552001-07-10 06:09:16 +00009555nextopt(const char *optstring)
9556{
Eric Andersencb57d552001-06-28 07:25:16 +00009557 char *p;
9558 const char *q;
9559 char c;
9560
9561 if ((p = optptr) == NULL || *p == '\0') {
9562 p = *argptr;
9563 if (p == NULL || *p != '-' || *++p == '\0')
9564 return '\0';
9565 argptr++;
Eric Andersen2870d962001-07-02 17:27:21 +00009566 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009567 return '\0';
9568 }
9569 c = *p++;
9570 for (q = optstring ; *q != c ; ) {
9571 if (*q == '\0')
9572 error("Illegal option -%c", c);
9573 if (*++q == ':')
9574 q++;
9575 }
9576 if (*++q == ':') {
9577 if (*p == '\0' && (p = *argptr++) == NULL)
9578 error("No arg for -%c option", c);
9579 optionarg = p;
9580 p = NULL;
9581 }
9582 optptr = p;
9583 return c;
9584}
9585
Eric Andersencb57d552001-06-28 07:25:16 +00009586static void
9587flushall() {
Eric Andersencb57d552001-06-28 07:25:16 +00009588 INTOFF;
Eric Andersen3102ac42001-07-06 04:26:23 +00009589 fflush(stdout);
Eric Andersencb57d552001-06-28 07:25:16 +00009590 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00009591}
9592
9593
9594static void
Eric Andersen3102ac42001-07-06 04:26:23 +00009595out2fmt(const char *fmt, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00009596{
9597 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009598 va_start(ap, fmt);
Eric Andersen3102ac42001-07-06 04:26:23 +00009599 vfprintf(stderr, fmt, ap);
Eric Andersencb57d552001-06-28 07:25:16 +00009600 va_end(ap);
9601}
9602
Eric Andersencb57d552001-06-28 07:25:16 +00009603/*
9604 * Version of write which resumes after a signal is caught.
9605 */
9606
9607static int
Eric Andersen2870d962001-07-02 17:27:21 +00009608xwrite(int fd, const char *buf, int nbytes)
9609{
Eric Andersencb57d552001-06-28 07:25:16 +00009610 int ntry;
9611 int i;
9612 int n;
9613
9614 n = nbytes;
9615 ntry = 0;
9616 for (;;) {
9617 i = write(fd, buf, n);
9618 if (i > 0) {
9619 if ((n -= i) <= 0)
9620 return nbytes;
9621 buf += i;
9622 ntry = 0;
9623 } else if (i == 0) {
9624 if (++ntry > 10)
9625 return nbytes - n;
9626 } else if (errno != EINTR) {
9627 return -1;
9628 }
9629 }
9630}
9631
9632
Eric Andersencb57d552001-06-28 07:25:16 +00009633/*
9634 * Shell command parser.
9635 */
9636
9637#define EOFMARKLEN 79
9638
9639
9640
9641struct heredoc {
Eric Andersen2870d962001-07-02 17:27:21 +00009642 struct heredoc *next; /* next here document in list */
9643 union node *here; /* redirection node */
9644 char *eofmark; /* string indicating end of input */
9645 int striptabs; /* if set, strip leading tabs */
Eric Andersencb57d552001-06-28 07:25:16 +00009646};
9647
Eric Andersen2870d962001-07-02 17:27:21 +00009648static struct heredoc *heredoclist; /* list of here documents to read */
9649static int parsebackquote; /* nonzero if we are inside backquotes */
9650static int doprompt; /* if set, prompt the user */
9651static int needprompt; /* true if interactive and at start of line */
9652static int lasttoken; /* last token read */
Eric Andersencb57d552001-06-28 07:25:16 +00009653
Eric Andersen2870d962001-07-02 17:27:21 +00009654static char *wordtext; /* text of last word returned by readtoken */
Eric Andersencb57d552001-06-28 07:25:16 +00009655
Eric Andersen2870d962001-07-02 17:27:21 +00009656static struct nodelist *backquotelist;
9657static union node *redirnode;
Eric Andersen044228d2001-07-17 01:12:36 +00009658static struct heredoc *heredoc;
Eric Andersen2870d962001-07-02 17:27:21 +00009659static int quoteflag; /* set if (part of) last token was quoted */
9660static int startlinno; /* line # where last token started */
Eric Andersencb57d552001-06-28 07:25:16 +00009661
9662
Eric Andersen2870d962001-07-02 17:27:21 +00009663static union node *list (int);
9664static union node *andor (void);
9665static union node *pipeline (void);
9666static union node *command (void);
9667static union node *simplecmd (void);
9668static void parsefname (void);
9669static void parseheredoc (void);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009670static char peektoken (void);
Eric Andersen2870d962001-07-02 17:27:21 +00009671static int readtoken (void);
9672static int xxreadtoken (void);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009673static int readtoken1 (int, int, const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00009674static int noexpand (char *);
9675static void synexpect (int) __attribute__((noreturn));
9676static void synerror (const char *) __attribute__((noreturn));
9677static void setprompt (int);
Eric Andersencb57d552001-06-28 07:25:16 +00009678
9679
9680/*
9681 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9682 * valid parse tree indicating a blank line.)
9683 */
9684
Eric Andersen2870d962001-07-02 17:27:21 +00009685static union node *
Eric Andersencb57d552001-06-28 07:25:16 +00009686parsecmd(int interact)
9687{
9688 int t;
9689
9690 tokpushback = 0;
9691 doprompt = interact;
9692 if (doprompt)
9693 setprompt(1);
9694 else
9695 setprompt(0);
9696 needprompt = 0;
9697 t = readtoken();
9698 if (t == TEOF)
9699 return NEOF;
9700 if (t == TNL)
9701 return NULL;
9702 tokpushback++;
9703 return list(1);
9704}
9705
9706
9707static union node *
9708list(nlflag)
9709 int nlflag;
9710{
9711 union node *n1, *n2, *n3;
9712 int tok;
9713
9714 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009715 if (nlflag == 0 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009716 return NULL;
9717 n1 = NULL;
9718 for (;;) {
9719 n2 = andor();
9720 tok = readtoken();
9721 if (tok == TBACKGND) {
9722 if (n2->type == NCMD || n2->type == NPIPE) {
9723 n2->ncmd.backgnd = 1;
9724 } else if (n2->type == NREDIR) {
9725 n2->type = NBACKGND;
9726 } else {
9727 n3 = (union node *)stalloc(sizeof (struct nredir));
9728 n3->type = NBACKGND;
9729 n3->nredir.n = n2;
9730 n3->nredir.redirect = NULL;
9731 n2 = n3;
9732 }
9733 }
9734 if (n1 == NULL) {
9735 n1 = n2;
9736 }
9737 else {
9738 n3 = (union node *)stalloc(sizeof (struct nbinary));
9739 n3->type = NSEMI;
9740 n3->nbinary.ch1 = n1;
9741 n3->nbinary.ch2 = n2;
9742 n1 = n3;
9743 }
9744 switch (tok) {
9745 case TBACKGND:
9746 case TSEMI:
9747 tok = readtoken();
9748 /* fall through */
9749 case TNL:
9750 if (tok == TNL) {
9751 parseheredoc();
9752 if (nlflag)
9753 return n1;
9754 } else {
9755 tokpushback++;
9756 }
9757 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009758 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009759 return n1;
9760 break;
9761 case TEOF:
9762 if (heredoclist)
9763 parseheredoc();
9764 else
Eric Andersen2870d962001-07-02 17:27:21 +00009765 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009766 return n1;
9767 default:
9768 if (nlflag)
9769 synexpect(-1);
9770 tokpushback++;
9771 return n1;
9772 }
9773 }
9774}
9775
9776
9777
9778static union node *
9779andor() {
9780 union node *n1, *n2, *n3;
9781 int t;
9782
9783 checkkwd = 1;
9784 n1 = pipeline();
9785 for (;;) {
9786 if ((t = readtoken()) == TAND) {
9787 t = NAND;
9788 } else if (t == TOR) {
9789 t = NOR;
9790 } else {
9791 tokpushback++;
9792 return n1;
9793 }
9794 checkkwd = 2;
9795 n2 = pipeline();
9796 n3 = (union node *)stalloc(sizeof (struct nbinary));
9797 n3->type = t;
9798 n3->nbinary.ch1 = n1;
9799 n3->nbinary.ch2 = n2;
9800 n1 = n3;
9801 }
9802}
9803
9804
9805
9806static union node *
9807pipeline() {
9808 union node *n1, *n2, *pipenode;
9809 struct nodelist *lp, *prev;
9810 int negate;
9811
9812 negate = 0;
9813 TRACE(("pipeline: entered\n"));
9814 if (readtoken() == TNOT) {
9815 negate = !negate;
9816 checkkwd = 1;
9817 } else
9818 tokpushback++;
9819 n1 = command();
9820 if (readtoken() == TPIPE) {
9821 pipenode = (union node *)stalloc(sizeof (struct npipe));
9822 pipenode->type = NPIPE;
9823 pipenode->npipe.backgnd = 0;
9824 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9825 pipenode->npipe.cmdlist = lp;
9826 lp->n = n1;
9827 do {
9828 prev = lp;
9829 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9830 checkkwd = 2;
9831 lp->n = command();
9832 prev->next = lp;
9833 } while (readtoken() == TPIPE);
9834 lp->next = NULL;
9835 n1 = pipenode;
9836 }
9837 tokpushback++;
9838 if (negate) {
9839 n2 = (union node *)stalloc(sizeof (struct nnot));
9840 n2->type = NNOT;
9841 n2->nnot.com = n1;
9842 return n2;
9843 } else
9844 return n1;
9845}
9846
9847
9848
9849static union node *
9850command() {
9851 union node *n1, *n2;
9852 union node *ap, **app;
9853 union node *cp, **cpp;
9854 union node *redir, **rpp;
9855 int t;
9856
9857 redir = NULL;
9858 n1 = NULL;
9859 rpp = &redir;
9860
Eric Andersen88cec252001-09-06 17:35:20 +00009861 /* Check for redirection which may precede command */
9862 while (readtoken() == TREDIR) {
9863 *rpp = n2 = redirnode;
9864 rpp = &n2->nfile.next;
9865 parsefname();
9866 }
9867 tokpushback++;
9868
Eric Andersencb57d552001-06-28 07:25:16 +00009869 switch (readtoken()) {
9870 case TIF:
9871 n1 = (union node *)stalloc(sizeof (struct nif));
9872 n1->type = NIF;
9873 n1->nif.test = list(0);
9874 if (readtoken() != TTHEN)
9875 synexpect(TTHEN);
9876 n1->nif.ifpart = list(0);
9877 n2 = n1;
9878 while (readtoken() == TELIF) {
9879 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9880 n2 = n2->nif.elsepart;
9881 n2->type = NIF;
9882 n2->nif.test = list(0);
9883 if (readtoken() != TTHEN)
9884 synexpect(TTHEN);
9885 n2->nif.ifpart = list(0);
9886 }
9887 if (lasttoken == TELSE)
9888 n2->nif.elsepart = list(0);
9889 else {
9890 n2->nif.elsepart = NULL;
9891 tokpushback++;
9892 }
9893 if (readtoken() != TFI)
9894 synexpect(TFI);
9895 checkkwd = 1;
9896 break;
9897 case TWHILE:
9898 case TUNTIL: {
9899 int got;
9900 n1 = (union node *)stalloc(sizeof (struct nbinary));
9901 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9902 n1->nbinary.ch1 = list(0);
9903 if ((got=readtoken()) != TDO) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009904TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00009905 synexpect(TDO);
9906 }
9907 n1->nbinary.ch2 = list(0);
9908 if (readtoken() != TDONE)
9909 synexpect(TDONE);
9910 checkkwd = 1;
9911 break;
9912 }
9913 case TFOR:
9914 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9915 synerror("Bad for loop variable");
9916 n1 = (union node *)stalloc(sizeof (struct nfor));
9917 n1->type = NFOR;
9918 n1->nfor.var = wordtext;
9919 checkkwd = 1;
9920 if (readtoken() == TIN) {
9921 app = &ap;
9922 while (readtoken() == TWORD) {
9923 n2 = (union node *)stalloc(sizeof (struct narg));
9924 n2->type = NARG;
9925 n2->narg.text = wordtext;
9926 n2->narg.backquote = backquotelist;
9927 *app = n2;
9928 app = &n2->narg.next;
9929 }
9930 *app = NULL;
9931 n1->nfor.args = ap;
9932 if (lasttoken != TNL && lasttoken != TSEMI)
9933 synexpect(-1);
9934 } else {
9935 static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
9936 '@', '=', '\0'};
9937 n2 = (union node *)stalloc(sizeof (struct narg));
9938 n2->type = NARG;
9939 n2->narg.text = argvars;
9940 n2->narg.backquote = NULL;
9941 n2->narg.next = NULL;
9942 n1->nfor.args = n2;
9943 /*
9944 * Newline or semicolon here is optional (but note
9945 * that the original Bourne shell only allowed NL).
9946 */
9947 if (lasttoken != TNL && lasttoken != TSEMI)
9948 tokpushback++;
9949 }
9950 checkkwd = 2;
9951 if (readtoken() != TDO)
9952 synexpect(TDO);
9953 n1->nfor.body = list(0);
9954 if (readtoken() != TDONE)
9955 synexpect(TDONE);
9956 checkkwd = 1;
9957 break;
9958 case TCASE:
9959 n1 = (union node *)stalloc(sizeof (struct ncase));
9960 n1->type = NCASE;
9961 if (readtoken() != TWORD)
9962 synexpect(TWORD);
9963 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9964 n2->type = NARG;
9965 n2->narg.text = wordtext;
9966 n2->narg.backquote = backquotelist;
9967 n2->narg.next = NULL;
9968 do {
9969 checkkwd = 1;
9970 } while (readtoken() == TNL);
9971 if (lasttoken != TIN)
9972 synerror("expecting \"in\"");
9973 cpp = &n1->ncase.cases;
9974 checkkwd = 2, readtoken();
9975 do {
9976 if (lasttoken == TLP)
9977 readtoken();
9978 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9979 cp->type = NCLIST;
9980 app = &cp->nclist.pattern;
9981 for (;;) {
9982 *app = ap = (union node *)stalloc(sizeof (struct narg));
9983 ap->type = NARG;
9984 ap->narg.text = wordtext;
9985 ap->narg.backquote = backquotelist;
9986 if (checkkwd = 2, readtoken() != TPIPE)
9987 break;
9988 app = &ap->narg.next;
9989 readtoken();
9990 }
9991 ap->narg.next = NULL;
9992 if (lasttoken != TRP)
9993 synexpect(TRP);
9994 cp->nclist.body = list(0);
9995
9996 checkkwd = 2;
9997 if ((t = readtoken()) != TESAC) {
9998 if (t != TENDCASE)
9999 synexpect(TENDCASE);
10000 else
10001 checkkwd = 2, readtoken();
10002 }
10003 cpp = &cp->nclist.next;
10004 } while(lasttoken != TESAC);
10005 *cpp = NULL;
10006 checkkwd = 1;
10007 break;
10008 case TLP:
10009 n1 = (union node *)stalloc(sizeof (struct nredir));
10010 n1->type = NSUBSHELL;
10011 n1->nredir.n = list(0);
10012 n1->nredir.redirect = NULL;
10013 if (readtoken() != TRP)
10014 synexpect(TRP);
10015 checkkwd = 1;
10016 break;
10017 case TBEGIN:
10018 n1 = list(0);
10019 if (readtoken() != TEND)
10020 synexpect(TEND);
10021 checkkwd = 1;
10022 break;
10023 /* Handle an empty command like other simple commands. */
10024 case TSEMI:
10025 case TAND:
10026 case TOR:
10027 case TNL:
10028 case TEOF:
10029 case TRP:
10030 case TBACKGND:
10031 /*
10032 * An empty command before a ; doesn't make much sense, and
10033 * should certainly be disallowed in the case of `if ;'.
10034 */
10035 if (!redir)
10036 synexpect(-1);
10037 case TWORD:
Eric Andersencb57d552001-06-28 07:25:16 +000010038 tokpushback++;
10039 n1 = simplecmd();
10040 return n1;
10041 default:
10042 synexpect(-1);
10043 /* NOTREACHED */
10044 }
10045
10046 /* Now check for redirection which may follow command */
10047 while (readtoken() == TREDIR) {
10048 *rpp = n2 = redirnode;
10049 rpp = &n2->nfile.next;
10050 parsefname();
10051 }
10052 tokpushback++;
10053 *rpp = NULL;
10054 if (redir) {
10055 if (n1->type != NSUBSHELL) {
10056 n2 = (union node *)stalloc(sizeof (struct nredir));
10057 n2->type = NREDIR;
10058 n2->nredir.n = n1;
10059 n1 = n2;
10060 }
10061 n1->nredir.redirect = redir;
10062 }
10063
10064 return n1;
10065}
10066
10067
10068static union node *
10069simplecmd() {
10070 union node *args, **app;
10071 union node *n = NULL;
10072 union node *vars, **vpp;
10073 union node **rpp, *redir;
10074
10075 args = NULL;
10076 app = &args;
10077 vars = NULL;
10078 vpp = &vars;
10079 redir = NULL;
10080 rpp = &redir;
10081
10082 checkalias = 2;
10083 for (;;) {
10084 switch (readtoken()) {
10085 case TWORD:
10086 case TASSIGN:
10087 n = (union node *)stalloc(sizeof (struct narg));
10088 n->type = NARG;
10089 n->narg.text = wordtext;
10090 n->narg.backquote = backquotelist;
10091 if (lasttoken == TWORD) {
10092 *app = n;
10093 app = &n->narg.next;
10094 } else {
10095 *vpp = n;
10096 vpp = &n->narg.next;
10097 }
10098 break;
10099 case TREDIR:
10100 *rpp = n = redirnode;
10101 rpp = &n->nfile.next;
Eric Andersen2870d962001-07-02 17:27:21 +000010102 parsefname(); /* read name of redirection file */
Eric Andersencb57d552001-06-28 07:25:16 +000010103 break;
10104 case TLP:
10105 if (
10106 args && app == &args->narg.next &&
10107 !vars && !redir
10108 ) {
10109 /* We have a function */
10110 if (readtoken() != TRP)
10111 synexpect(TRP);
Eric Andersencb57d552001-06-28 07:25:16 +000010112 n->type = NDEFUN;
10113 checkkwd = 2;
10114 n->narg.next = command();
10115 return n;
10116 }
10117 /* fall through */
10118 default:
10119 tokpushback++;
10120 goto out;
10121 }
10122 }
10123out:
10124 *app = NULL;
10125 *vpp = NULL;
10126 *rpp = NULL;
10127 n = (union node *)stalloc(sizeof (struct ncmd));
10128 n->type = NCMD;
10129 n->ncmd.backgnd = 0;
10130 n->ncmd.args = args;
10131 n->ncmd.assign = vars;
10132 n->ncmd.redirect = redir;
10133 return n;
10134}
10135
10136static union node *
Eric Andersen2870d962001-07-02 17:27:21 +000010137makename(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000010138 union node *n;
10139
10140 n = (union node *)stalloc(sizeof (struct narg));
10141 n->type = NARG;
10142 n->narg.next = NULL;
10143 n->narg.text = wordtext;
10144 n->narg.backquote = backquotelist;
10145 return n;
10146}
10147
10148static void fixredir(union node *n, const char *text, int err)
Eric Andersen2870d962001-07-02 17:27:21 +000010149{
Eric Andersencb57d552001-06-28 07:25:16 +000010150 TRACE(("Fix redir %s %d\n", text, err));
10151 if (!err)
10152 n->ndup.vname = NULL;
10153
10154 if (is_digit(text[0]) && text[1] == '\0')
10155 n->ndup.dupfd = digit_val(text[0]);
10156 else if (text[0] == '-' && text[1] == '\0')
10157 n->ndup.dupfd = -1;
10158 else {
10159
10160 if (err)
10161 synerror("Bad fd number");
10162 else
10163 n->ndup.vname = makename();
10164 }
10165}
10166
10167
10168static void
Eric Andersen2870d962001-07-02 17:27:21 +000010169parsefname(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000010170 union node *n = redirnode;
10171
10172 if (readtoken() != TWORD)
10173 synexpect(-1);
10174 if (n->type == NHERE) {
10175 struct heredoc *here = heredoc;
10176 struct heredoc *p;
10177 int i;
10178
10179 if (quoteflag == 0)
10180 n->type = NXHERE;
10181 TRACE(("Here document %d\n", n->type));
10182 if (here->striptabs) {
10183 while (*wordtext == '\t')
10184 wordtext++;
10185 }
10186 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10187 synerror("Illegal eof marker for << redirection");
10188 rmescapes(wordtext);
10189 here->eofmark = wordtext;
10190 here->next = NULL;
10191 if (heredoclist == NULL)
10192 heredoclist = here;
10193 else {
10194 for (p = heredoclist ; p->next ; p = p->next);
10195 p->next = here;
10196 }
10197 } else if (n->type == NTOFD || n->type == NFROMFD) {
10198 fixredir(n, wordtext, 0);
10199 } else {
10200 n->nfile.fname = makename();
10201 }
10202}
10203
10204
10205/*
10206 * Input any here documents.
10207 */
10208
10209static void
10210parseheredoc() {
10211 struct heredoc *here;
10212 union node *n;
10213
10214 while (heredoclist) {
10215 here = heredoclist;
10216 heredoclist = here->next;
10217 if (needprompt) {
10218 setprompt(2);
10219 needprompt = 0;
10220 }
10221 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10222 here->eofmark, here->striptabs);
10223 n = (union node *)stalloc(sizeof (struct narg));
10224 n->narg.type = NARG;
10225 n->narg.next = NULL;
10226 n->narg.text = wordtext;
10227 n->narg.backquote = backquotelist;
10228 here->here->nhere.doc = n;
10229 }
10230}
10231
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010232static char
Eric Andersencb57d552001-06-28 07:25:16 +000010233peektoken() {
10234 int t;
10235
10236 t = readtoken();
10237 tokpushback++;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010238 return tokname_array[t][0];
Eric Andersencb57d552001-06-28 07:25:16 +000010239}
10240
10241static int
10242readtoken() {
10243 int t;
Eric Andersen7467c8d2001-07-12 20:26:32 +000010244
Eric Andersen2870d962001-07-02 17:27:21 +000010245#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010246 int savecheckalias = checkalias;
Eric Andersen7467c8d2001-07-12 20:26:32 +000010247 int savecheckkwd = checkkwd;
Eric Andersencb57d552001-06-28 07:25:16 +000010248 struct alias *ap;
Eric Andersen2870d962001-07-02 17:27:21 +000010249#endif
10250
Eric Andersencb57d552001-06-28 07:25:16 +000010251#ifdef DEBUG
10252 int alreadyseen = tokpushback;
10253#endif
10254
Eric Andersen2870d962001-07-02 17:27:21 +000010255#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010256top:
Eric Andersen2870d962001-07-02 17:27:21 +000010257#endif
10258
Eric Andersencb57d552001-06-28 07:25:16 +000010259 t = xxreadtoken();
Eric Andersen2870d962001-07-02 17:27:21 +000010260
10261#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010262 checkalias = savecheckalias;
Eric Andersen2870d962001-07-02 17:27:21 +000010263#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010264
10265 if (checkkwd) {
10266 /*
10267 * eat newlines
10268 */
10269 if (checkkwd == 2) {
10270 checkkwd = 0;
10271 while (t == TNL) {
10272 parseheredoc();
10273 t = xxreadtoken();
10274 }
10275 }
10276 checkkwd = 0;
10277 /*
10278 * check for keywords
10279 */
10280 if (t == TWORD && !quoteflag)
10281 {
10282 const char *const *pp;
10283
10284 if ((pp = findkwd(wordtext))) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010285 lasttoken = t = pp - tokname_array;
10286 TRACE(("keyword %s recognized\n", tokname(t)));
Eric Andersencb57d552001-06-28 07:25:16 +000010287 goto out;
10288 }
10289 }
10290 }
10291
Eric Andersen7467c8d2001-07-12 20:26:32 +000010292
Eric Andersencb57d552001-06-28 07:25:16 +000010293 if (t != TWORD) {
10294 if (t != TREDIR) {
10295 checkalias = 0;
10296 }
10297 } else if (checkalias == 2 && isassignment(wordtext)) {
10298 lasttoken = t = TASSIGN;
Eric Andersen7467c8d2001-07-12 20:26:32 +000010299#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010300 } else if (checkalias) {
10301 if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) {
10302 if (*ap->val) {
10303 pushstring(ap->val, strlen(ap->val), ap);
10304 }
10305 checkkwd = savecheckkwd;
10306 goto top;
10307 }
10308 checkalias = 0;
Eric Andersen2870d962001-07-02 17:27:21 +000010309#endif
Eric Andersen7467c8d2001-07-12 20:26:32 +000010310 }
Eric Andersencb57d552001-06-28 07:25:16 +000010311out:
10312#ifdef DEBUG
10313 if (!alreadyseen)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010314 TRACE(("token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +000010315 else
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010316 TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +000010317#endif
10318 return (t);
10319}
10320
10321
10322/*
10323 * Read the next input token.
10324 * If the token is a word, we set backquotelist to the list of cmds in
Eric Andersen2870d962001-07-02 17:27:21 +000010325 * backquotes. We set quoteflag to true if any part of the word was
10326 * quoted.
Eric Andersencb57d552001-06-28 07:25:16 +000010327 * If the token is TREDIR, then we set redirnode to a structure containing
Eric Andersen2870d962001-07-02 17:27:21 +000010328 * the redirection.
Eric Andersencb57d552001-06-28 07:25:16 +000010329 * In all cases, the variable startlinno is set to the number of the line
Eric Andersen2870d962001-07-02 17:27:21 +000010330 * on which the token starts.
Eric Andersencb57d552001-06-28 07:25:16 +000010331 *
10332 * [Change comment: here documents and internal procedures]
10333 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10334 * word parsing code into a separate routine. In this case, readtoken
10335 * doesn't need to have any internal procedures, but parseword does.
10336 * We could also make parseoperator in essence the main routine, and
10337 * have parseword (readtoken1?) handle both words and redirection.]
10338 */
10339
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000010340#define NEW_xxreadtoken
10341#ifdef NEW_xxreadtoken
10342
10343static const char xxreadtoken_chars[] = "\n()&|;"; /* singles must be first! */
10344static const char xxreadtoken_tokens[] = {
10345 TNL, TLP, TRP, /* only single occurrence allowed */
10346 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10347 TEOF, /* corresponds to trailing nul */
10348 TAND, TOR, TENDCASE, /* if double occurrence */
10349};
10350
10351#define xxreadtoken_doubles \
10352 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10353#define xxreadtoken_singles \
10354 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10355
10356static int
10357xxreadtoken() {
10358 int c;
10359
10360 if (tokpushback) {
10361 tokpushback = 0;
10362 return lasttoken;
10363 }
10364 if (needprompt) {
10365 setprompt(2);
10366 needprompt = 0;
10367 }
10368 startlinno = plinno;
10369 for (;;) { /* until token or start of word found */
10370 c = pgetc_macro();
10371
10372 if ((c!=' ') && (c!='\t')
10373#ifdef ASH_ALIAS
10374 && (c!=PEOA)
10375#endif
10376 ) {
10377 if (c=='#') {
10378 while ((c = pgetc()) != '\n' && c != PEOF);
10379 pungetc();
10380 } else if (c=='\\') {
10381 if (pgetc() != '\n') {
10382 pungetc();
10383 goto READTOKEN1;
10384 }
10385 startlinno = ++plinno;
10386 setprompt(doprompt ? 2 : 0);
10387 } else {
10388 const char *p
10389 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10390
10391 if (c!=PEOF) {
10392 if (c=='\n') {
10393 plinno++;
10394 needprompt = doprompt;
10395 }
10396
10397 p = strchr(xxreadtoken_chars, c);
10398 if (p == NULL) {
10399 READTOKEN1:
10400 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10401 }
10402
10403 if (p-xxreadtoken_chars >= xxreadtoken_singles) {
10404 if (pgetc() == *p) { /* double occurrence? */
10405 p += xxreadtoken_doubles + 1;
10406 } else {
10407 pungetc();
10408 }
10409 }
10410 }
10411
10412 return lasttoken = xxreadtoken_tokens[p-xxreadtoken_chars];
10413 }
10414 }
10415 }
10416}
10417
10418
10419#else
Eric Andersen2870d962001-07-02 17:27:21 +000010420#define RETURN(token) return lasttoken = token
Eric Andersencb57d552001-06-28 07:25:16 +000010421
10422static int
10423xxreadtoken() {
10424 int c;
10425
10426 if (tokpushback) {
10427 tokpushback = 0;
10428 return lasttoken;
10429 }
10430 if (needprompt) {
10431 setprompt(2);
10432 needprompt = 0;
10433 }
10434 startlinno = plinno;
Eric Andersen2870d962001-07-02 17:27:21 +000010435 for (;;) { /* until token or start of word found */
Eric Andersencb57d552001-06-28 07:25:16 +000010436 c = pgetc_macro();
10437 switch (c) {
10438 case ' ': case '\t':
Eric Andersen3102ac42001-07-06 04:26:23 +000010439#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010440 case PEOA:
Eric Andersen3102ac42001-07-06 04:26:23 +000010441#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010442 continue;
10443 case '#':
10444 while ((c = pgetc()) != '\n' && c != PEOF);
10445 pungetc();
10446 continue;
10447 case '\\':
10448 if (pgetc() == '\n') {
10449 startlinno = ++plinno;
10450 if (doprompt)
10451 setprompt(2);
10452 else
10453 setprompt(0);
10454 continue;
10455 }
10456 pungetc();
10457 goto breakloop;
10458 case '\n':
10459 plinno++;
10460 needprompt = doprompt;
10461 RETURN(TNL);
10462 case PEOF:
10463 RETURN(TEOF);
10464 case '&':
10465 if (pgetc() == '&')
10466 RETURN(TAND);
10467 pungetc();
10468 RETURN(TBACKGND);
10469 case '|':
10470 if (pgetc() == '|')
10471 RETURN(TOR);
10472 pungetc();
10473 RETURN(TPIPE);
10474 case ';':
10475 if (pgetc() == ';')
10476 RETURN(TENDCASE);
10477 pungetc();
10478 RETURN(TSEMI);
10479 case '(':
10480 RETURN(TLP);
10481 case ')':
10482 RETURN(TRP);
10483 default:
10484 goto breakloop;
10485 }
10486 }
10487breakloop:
10488 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10489#undef RETURN
10490}
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000010491#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010492
10493
10494/*
10495 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10496 * is not NULL, read a here document. In the latter case, eofmark is the
10497 * word which marks the end of the document and striptabs is true if
10498 * leading tabs should be stripped from the document. The argument firstc
10499 * is the first character of the input token or document.
10500 *
10501 * Because C does not have internal subroutines, I have simulated them
10502 * using goto's to implement the subroutine linkage. The following macros
10503 * will run code that appears at the end of readtoken1.
10504 */
10505
Eric Andersen2870d962001-07-02 17:27:21 +000010506#define CHECKEND() {goto checkend; checkend_return:;}
10507#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10508#define PARSESUB() {goto parsesub; parsesub_return:;}
10509#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10510#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10511#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010512
10513static int
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010514readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
10515{
Eric Andersencb57d552001-06-28 07:25:16 +000010516 int c = firstc;
10517 char *out;
10518 int len;
10519 char line[EOFMARKLEN + 1];
10520 struct nodelist *bqlist;
10521 int quotef;
10522 int dblquote;
Eric Andersen2870d962001-07-02 17:27:21 +000010523 int varnest; /* levels of variables expansion */
10524 int arinest; /* levels of arithmetic expansion */
10525 int parenlevel; /* levels of parens in arithmetic */
10526 int dqvarnest; /* levels of variables expansion within double quotes */
Eric Andersencb57d552001-06-28 07:25:16 +000010527 int oldstyle;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010528 int prevsyntax; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010529#if __GNUC__
10530 /* Avoid longjmp clobbering */
10531 (void) &out;
10532 (void) &quotef;
10533 (void) &dblquote;
10534 (void) &varnest;
10535 (void) &arinest;
10536 (void) &parenlevel;
10537 (void) &dqvarnest;
10538 (void) &oldstyle;
10539 (void) &prevsyntax;
10540 (void) &syntax;
10541#endif
10542
10543 startlinno = plinno;
10544 dblquote = 0;
10545 if (syntax == DQSYNTAX)
10546 dblquote = 1;
10547 quotef = 0;
10548 bqlist = NULL;
10549 varnest = 0;
10550 arinest = 0;
10551 parenlevel = 0;
10552 dqvarnest = 0;
10553
10554 STARTSTACKSTR(out);
Eric Andersen2870d962001-07-02 17:27:21 +000010555 loop: { /* for each line, until end of word */
10556 CHECKEND(); /* set c to PEOF if at end of here document */
10557 for (;;) { /* until end of line or end of word */
10558 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010559 switch(SIT(c,syntax)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010560 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010561 if (syntax == BASESYNTAX)
Eric Andersen2870d962001-07-02 17:27:21 +000010562 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010563 USTPUTC(c, out);
10564 plinno++;
10565 if (doprompt)
10566 setprompt(2);
10567 else
10568 setprompt(0);
10569 c = pgetc();
Eric Andersen2870d962001-07-02 17:27:21 +000010570 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010571 case CWORD:
10572 USTPUTC(c, out);
10573 break;
10574 case CCTL:
10575 if ((eofmark == NULL || dblquote) &&
10576 dqvarnest == 0)
10577 USTPUTC(CTLESC, out);
10578 USTPUTC(c, out);
10579 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010580 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010581 c = pgetc2();
10582 if (c == PEOF) {
10583 USTPUTC('\\', out);
10584 pungetc();
10585 } else if (c == '\n') {
10586 if (doprompt)
10587 setprompt(2);
10588 else
10589 setprompt(0);
10590 } else {
10591 if (dblquote && c != '\\' && c != '`' && c != '$'
10592 && (c != '"' || eofmark != NULL))
10593 USTPUTC('\\', out);
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010594 if (SIT(c,SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010595 USTPUTC(CTLESC, out);
10596 else if (eofmark == NULL)
10597 USTPUTC(CTLQUOTEMARK, out);
10598 USTPUTC(c, out);
10599 quotef++;
10600 }
10601 break;
10602 case CSQUOTE:
10603 if (eofmark == NULL)
10604 USTPUTC(CTLQUOTEMARK, out);
10605 syntax = SQSYNTAX;
10606 break;
10607 case CDQUOTE:
10608 if (eofmark == NULL)
10609 USTPUTC(CTLQUOTEMARK, out);
10610 syntax = DQSYNTAX;
10611 dblquote = 1;
10612 break;
10613 case CENDQUOTE:
10614 if (eofmark != NULL && arinest == 0 &&
10615 varnest == 0) {
10616 USTPUTC(c, out);
10617 } else {
10618 if (arinest) {
10619 syntax = ARISYNTAX;
10620 dblquote = 0;
10621 } else if (eofmark == NULL &&
10622 dqvarnest == 0) {
10623 syntax = BASESYNTAX;
10624 dblquote = 0;
10625 }
10626 quotef++;
10627 }
10628 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010629 case CVAR: /* '$' */
10630 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010631 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010632 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010633 if (varnest > 0) {
10634 varnest--;
10635 if (dqvarnest > 0) {
10636 dqvarnest--;
10637 }
10638 USTPUTC(CTLENDVAR, out);
10639 } else {
10640 USTPUTC(c, out);
10641 }
10642 break;
10643#ifdef ASH_MATH_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +000010644 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010645 parenlevel++;
10646 USTPUTC(c, out);
10647 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010648 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010649 if (parenlevel > 0) {
10650 USTPUTC(c, out);
10651 --parenlevel;
10652 } else {
10653 if (pgetc() == ')') {
10654 if (--arinest == 0) {
10655 USTPUTC(CTLENDARI, out);
10656 syntax = prevsyntax;
10657 if (syntax == DQSYNTAX)
10658 dblquote = 1;
10659 else
10660 dblquote = 0;
10661 } else
10662 USTPUTC(')', out);
10663 } else {
10664 /*
10665 * unbalanced parens
10666 * (don't 2nd guess - no error)
10667 */
10668 pungetc();
10669 USTPUTC(')', out);
10670 }
10671 }
10672 break;
10673#endif
Eric Andersen2870d962001-07-02 17:27:21 +000010674 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010675 PARSEBACKQOLD();
10676 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010677 case CENDFILE:
10678 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010679 case CIGN:
10680 break;
10681 default:
10682 if (varnest == 0)
Eric Andersen2870d962001-07-02 17:27:21 +000010683 goto endword; /* exit outer loop */
Eric Andersen3102ac42001-07-06 04:26:23 +000010684#ifdef ASH_ALIAS
10685 if (c != PEOA)
10686#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010687 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010688
Eric Andersencb57d552001-06-28 07:25:16 +000010689 }
10690 c = pgetc_macro();
10691 }
10692 }
10693endword:
10694 if (syntax == ARISYNTAX)
10695 synerror("Missing '))'");
10696 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10697 synerror("Unterminated quoted string");
10698 if (varnest != 0) {
10699 startlinno = plinno;
10700 synerror("Missing '}'");
10701 }
10702 USTPUTC('\0', out);
10703 len = out - stackblock();
10704 out = stackblock();
10705 if (eofmark == NULL) {
10706 if ((c == '>' || c == '<')
10707 && quotef == 0
10708 && len <= 2
10709 && (*out == '\0' || is_digit(*out))) {
10710 PARSEREDIR();
10711 return lasttoken = TREDIR;
10712 } else {
10713 pungetc();
10714 }
10715 }
10716 quoteflag = quotef;
10717 backquotelist = bqlist;
10718 grabstackblock(len);
10719 wordtext = out;
10720 return lasttoken = TWORD;
10721/* end of readtoken routine */
10722
10723
10724
10725/*
10726 * Check to see whether we are at the end of the here document. When this
10727 * is called, c is set to the first character of the next input line. If
10728 * we are at the end of the here document, this routine sets the c to PEOF.
10729 */
10730
10731checkend: {
10732 if (eofmark) {
Eric Andersen2870d962001-07-02 17:27:21 +000010733#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010734 if (c == PEOA) {
10735 c = pgetc2();
10736 }
Eric Andersen2870d962001-07-02 17:27:21 +000010737#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010738 if (striptabs) {
10739 while (c == '\t') {
10740 c = pgetc2();
10741 }
10742 }
10743 if (c == *eofmark) {
10744 if (pfgets(line, sizeof line) != NULL) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010745 const char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010746
10747 p = line;
10748 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10749 if (*p == '\n' && *q == '\0') {
10750 c = PEOF;
10751 plinno++;
10752 needprompt = doprompt;
10753 } else {
10754 pushstring(line, strlen(line), NULL);
10755 }
10756 }
10757 }
10758 }
10759 goto checkend_return;
10760}
10761
10762
10763/*
10764 * Parse a redirection operator. The variable "out" points to a string
10765 * specifying the fd to be redirected. The variable "c" contains the
10766 * first character of the redirection operator.
10767 */
10768
10769parseredir: {
10770 char fd = *out;
10771 union node *np;
10772
10773 np = (union node *)stalloc(sizeof (struct nfile));
10774 if (c == '>') {
10775 np->nfile.fd = 1;
10776 c = pgetc();
10777 if (c == '>')
10778 np->type = NAPPEND;
10779 else if (c == '&')
10780 np->type = NTOFD;
10781 else if (c == '|')
10782 np->type = NTOOV;
10783 else {
10784 np->type = NTO;
10785 pungetc();
10786 }
Eric Andersen2870d962001-07-02 17:27:21 +000010787 } else { /* c == '<' */
Eric Andersencb57d552001-06-28 07:25:16 +000010788 np->nfile.fd = 0;
10789 switch (c = pgetc()) {
10790 case '<':
10791 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10792 np = (union node *)stalloc(sizeof (struct nhere));
10793 np->nfile.fd = 0;
10794 }
10795 np->type = NHERE;
10796 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10797 heredoc->here = np;
10798 if ((c = pgetc()) == '-') {
10799 heredoc->striptabs = 1;
10800 } else {
10801 heredoc->striptabs = 0;
10802 pungetc();
10803 }
10804 break;
10805
10806 case '&':
10807 np->type = NFROMFD;
10808 break;
10809
10810 case '>':
10811 np->type = NFROMTO;
10812 break;
10813
10814 default:
10815 np->type = NFROM;
10816 pungetc();
10817 break;
10818 }
10819 }
10820 if (fd != '\0')
10821 np->nfile.fd = digit_val(fd);
10822 redirnode = np;
10823 goto parseredir_return;
10824}
10825
10826
10827/*
10828 * Parse a substitution. At this point, we have read the dollar sign
10829 * and nothing else.
10830 */
10831
10832parsesub: {
10833 int subtype;
10834 int typeloc;
10835 int flags;
10836 char *p;
10837 static const char types[] = "}-+?=";
10838
10839 c = pgetc();
10840 if (
10841 c <= PEOA ||
10842 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10843 ) {
10844 USTPUTC('$', out);
10845 pungetc();
Eric Andersen2870d962001-07-02 17:27:21 +000010846 } else if (c == '(') { /* $(command) or $((arith)) */
Eric Andersencb57d552001-06-28 07:25:16 +000010847 if (pgetc() == '(') {
10848 PARSEARITH();
10849 } else {
10850 pungetc();
10851 PARSEBACKQNEW();
10852 }
10853 } else {
10854 USTPUTC(CTLVAR, out);
10855 typeloc = out - stackblock();
10856 USTPUTC(VSNORMAL, out);
10857 subtype = VSNORMAL;
10858 if (c == '{') {
10859 c = pgetc();
10860 if (c == '#') {
10861 if ((c = pgetc()) == '}')
10862 c = '#';
10863 else
10864 subtype = VSLENGTH;
10865 }
10866 else
10867 subtype = 0;
10868 }
10869 if (c > PEOA && is_name(c)) {
10870 do {
10871 STPUTC(c, out);
10872 c = pgetc();
10873 } while (c > PEOA && is_in_name(c));
10874 } else if (is_digit(c)) {
10875 do {
10876 USTPUTC(c, out);
10877 c = pgetc();
10878 } while (is_digit(c));
10879 }
10880 else if (is_special(c)) {
10881 USTPUTC(c, out);
10882 c = pgetc();
10883 }
10884 else
Eric Andersen2870d962001-07-02 17:27:21 +000010885badsub: synerror("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010886
10887 STPUTC('=', out);
10888 flags = 0;
10889 if (subtype == 0) {
10890 switch (c) {
10891 case ':':
10892 flags = VSNUL;
10893 c = pgetc();
10894 /*FALLTHROUGH*/
10895 default:
10896 p = strchr(types, c);
10897 if (p == NULL)
10898 goto badsub;
10899 subtype = p - types + VSNORMAL;
10900 break;
10901 case '%':
10902 case '#':
10903 {
10904 int cc = c;
10905 subtype = c == '#' ? VSTRIMLEFT :
10906 VSTRIMRIGHT;
10907 c = pgetc();
10908 if (c == cc)
10909 subtype++;
10910 else
10911 pungetc();
10912 break;
10913 }
10914 }
10915 } else {
10916 pungetc();
10917 }
10918 if (dblquote || arinest)
10919 flags |= VSQUOTE;
10920 *(stackblock() + typeloc) = subtype | flags;
10921 if (subtype != VSNORMAL) {
10922 varnest++;
10923 if (dblquote) {
10924 dqvarnest++;
10925 }
10926 }
10927 }
10928 goto parsesub_return;
10929}
10930
10931
10932/*
10933 * Called to parse command substitutions. Newstyle is set if the command
10934 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10935 * list of commands (passed by reference), and savelen is the number of
10936 * characters on the top of the stack which must be preserved.
10937 */
10938
10939parsebackq: {
10940 struct nodelist **nlpp;
10941 int savepbq;
10942 union node *n;
10943 char *volatile str;
10944 struct jmploc jmploc;
10945 struct jmploc *volatile savehandler;
10946 int savelen;
10947 int saveprompt;
10948#ifdef __GNUC__
10949 (void) &saveprompt;
10950#endif
10951
10952 savepbq = parsebackquote;
10953 if (setjmp(jmploc.loc)) {
10954 if (str)
10955 ckfree(str);
10956 parsebackquote = 0;
10957 handler = savehandler;
10958 longjmp(handler->loc, 1);
10959 }
10960 INTOFF;
10961 str = NULL;
10962 savelen = out - stackblock();
10963 if (savelen > 0) {
10964 str = ckmalloc(savelen);
10965 memcpy(str, stackblock(), savelen);
10966 }
10967 savehandler = handler;
10968 handler = &jmploc;
10969 INTON;
Eric Andersen2870d962001-07-02 17:27:21 +000010970 if (oldstyle) {
10971 /* We must read until the closing backquote, giving special
10972 treatment to some slashes, and then push the string and
10973 reread it as input, interpreting it normally. */
10974 char *pout;
10975 int pc;
10976 int psavelen;
10977 char *pstr;
Eric Andersencb57d552001-06-28 07:25:16 +000010978
10979
Eric Andersen2870d962001-07-02 17:27:21 +000010980 STARTSTACKSTR(pout);
Eric Andersencb57d552001-06-28 07:25:16 +000010981 for (;;) {
10982 if (needprompt) {
10983 setprompt(2);
10984 needprompt = 0;
10985 }
10986 switch (pc = pgetc()) {
10987 case '`':
10988 goto done;
10989
10990 case '\\':
Eric Andersen2870d962001-07-02 17:27:21 +000010991 if ((pc = pgetc()) == '\n') {
Eric Andersencb57d552001-06-28 07:25:16 +000010992 plinno++;
10993 if (doprompt)
10994 setprompt(2);
10995 else
10996 setprompt(0);
10997 /*
10998 * If eating a newline, avoid putting
10999 * the newline into the new character
11000 * stream (via the STPUTC after the
11001 * switch).
11002 */
11003 continue;
11004 }
Eric Andersen2870d962001-07-02 17:27:21 +000011005 if (pc != '\\' && pc != '`' && pc != '$'
11006 && (!dblquote || pc != '"'))
11007 STPUTC('\\', pout);
Eric Andersencb57d552001-06-28 07:25:16 +000011008 if (pc > PEOA) {
11009 break;
11010 }
11011 /* fall through */
11012
11013 case PEOF:
Eric Andersen2870d962001-07-02 17:27:21 +000011014#ifdef ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000011015 case PEOA:
Eric Andersen2870d962001-07-02 17:27:21 +000011016#endif
11017 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000011018 synerror("EOF in backquote substitution");
11019
11020 case '\n':
11021 plinno++;
11022 needprompt = doprompt;
11023 break;
11024
11025 default:
11026 break;
11027 }
11028 STPUTC(pc, pout);
Eric Andersen2870d962001-07-02 17:27:21 +000011029 }
Eric Andersencb57d552001-06-28 07:25:16 +000011030done:
Eric Andersen2870d962001-07-02 17:27:21 +000011031 STPUTC('\0', pout);
11032 psavelen = pout - stackblock();
11033 if (psavelen > 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000011034 pstr = grabstackstr(pout);
11035 setinputstring(pstr);
Eric Andersen2870d962001-07-02 17:27:21 +000011036 }
11037 }
Eric Andersencb57d552001-06-28 07:25:16 +000011038 nlpp = &bqlist;
11039 while (*nlpp)
11040 nlpp = &(*nlpp)->next;
11041 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
11042 (*nlpp)->next = NULL;
11043 parsebackquote = oldstyle;
11044
11045 if (oldstyle) {
11046 saveprompt = doprompt;
11047 doprompt = 0;
11048 }
11049
11050 n = list(0);
11051
11052 if (oldstyle)
11053 doprompt = saveprompt;
11054 else {
11055 if (readtoken() != TRP)
11056 synexpect(TRP);
11057 }
11058
11059 (*nlpp)->n = n;
Eric Andersen2870d962001-07-02 17:27:21 +000011060 if (oldstyle) {
Eric Andersencb57d552001-06-28 07:25:16 +000011061 /*
11062 * Start reading from old file again, ignoring any pushed back
11063 * tokens left from the backquote parsing
11064 */
Eric Andersen2870d962001-07-02 17:27:21 +000011065 popfile();
Eric Andersencb57d552001-06-28 07:25:16 +000011066 tokpushback = 0;
11067 }
11068 while (stackblocksize() <= savelen)
11069 growstackblock();
11070 STARTSTACKSTR(out);
11071 if (str) {
11072 memcpy(out, str, savelen);
11073 STADJUST(savelen, out);
11074 INTOFF;
11075 ckfree(str);
11076 str = NULL;
11077 INTON;
11078 }
11079 parsebackquote = savepbq;
11080 handler = savehandler;
11081 if (arinest || dblquote)
11082 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11083 else
11084 USTPUTC(CTLBACKQ, out);
11085 if (oldstyle)
11086 goto parsebackq_oldreturn;
11087 else
11088 goto parsebackq_newreturn;
11089}
11090
11091/*
11092 * Parse an arithmetic expansion (indicate start of one and set state)
11093 */
11094parsearith: {
11095
11096 if (++arinest == 1) {
11097 prevsyntax = syntax;
11098 syntax = ARISYNTAX;
11099 USTPUTC(CTLARI, out);
11100 if (dblquote)
11101 USTPUTC('"',out);
11102 else
11103 USTPUTC(' ',out);
11104 } else {
11105 /*
11106 * we collapse embedded arithmetic expansion to
11107 * parenthesis, which should be equivalent
11108 */
11109 USTPUTC('(', out);
11110 }
11111 goto parsearith_return;
11112}
11113
11114} /* end of readtoken */
11115
11116
Eric Andersencb57d552001-06-28 07:25:16 +000011117/*
11118 * Returns true if the text contains nothing to expand (no dollar signs
11119 * or backquotes).
11120 */
11121
11122static int
11123noexpand(text)
11124 char *text;
11125 {
11126 char *p;
11127 char c;
11128
11129 p = text;
11130 while ((c = *p++) != '\0') {
11131 if (c == CTLQUOTEMARK)
11132 continue;
11133 if (c == CTLESC)
11134 p++;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011135 else if (SIT(c,BASESYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000011136 return 0;
11137 }
11138 return 1;
11139}
11140
11141
11142/*
11143 * Return true if the argument is a legal variable name (a letter or
11144 * underscore followed by zero or more letters, underscores, and digits).
11145 */
11146
11147static int
Eric Andersen2870d962001-07-02 17:27:21 +000011148goodname(const char *name)
11149{
11150 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +000011151
11152 p = name;
11153 if (! is_name(*p))
11154 return 0;
11155 while (*++p) {
11156 if (! is_in_name(*p))
11157 return 0;
11158 }
11159 return 1;
11160}
11161
11162
11163/*
11164 * Called when an unexpected token is read during the parse. The argument
11165 * is the token that is expected, or -1 if more than one type of token can
11166 * occur at this point.
11167 */
11168
11169static void
11170synexpect(token)
11171 int token;
11172{
11173 char msg[64];
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011174 int l;
Eric Andersencb57d552001-06-28 07:25:16 +000011175
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011176 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
11177 if (token >= 0)
11178 sprintf(msg+l, " (expecting %s)", tokname(token));
Eric Andersencb57d552001-06-28 07:25:16 +000011179 synerror(msg);
11180 /* NOTREACHED */
11181}
11182
11183
11184static void
Eric Andersen2870d962001-07-02 17:27:21 +000011185synerror(const char *msg)
11186{
Eric Andersencb57d552001-06-28 07:25:16 +000011187 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +000011188 out2fmt("%s: %d: ", commandname, startlinno);
11189 out2fmt("Syntax error: %s\n", msg);
Eric Andersencb57d552001-06-28 07:25:16 +000011190 error((char *)NULL);
11191 /* NOTREACHED */
11192}
11193
Eric Andersencb57d552001-06-28 07:25:16 +000011194
11195/*
11196 * called by editline -- any expansions to the prompt
11197 * should be added here.
11198 */
Eric Andersen2870d962001-07-02 17:27:21 +000011199static void
Eric Andersen62483552001-07-10 06:09:16 +000011200setprompt(int whichprompt)
Eric Andersen2870d962001-07-02 17:27:21 +000011201{
Eric Andersen62483552001-07-10 06:09:16 +000011202 char *prompt;
11203 switch (whichprompt) {
11204 case 1:
11205 prompt = ps1val();
11206 break;
11207 case 2:
11208 prompt = ps2val();
11209 break;
11210 default: /* 0 */
11211 prompt = "";
11212 }
11213 putprompt(prompt);
Eric Andersencb57d552001-06-28 07:25:16 +000011214}
11215
Eric Andersencb57d552001-06-28 07:25:16 +000011216
Eric Andersencb57d552001-06-28 07:25:16 +000011217/*
11218 * Code for dealing with input/output redirection.
11219 */
11220
Eric Andersen2870d962001-07-02 17:27:21 +000011221#define EMPTY -2 /* marks an unused slot in redirtab */
Eric Andersencb57d552001-06-28 07:25:16 +000011222#ifndef PIPE_BUF
Eric Andersen2870d962001-07-02 17:27:21 +000011223# define PIPESIZE 4096 /* amount of buffering in a pipe */
Eric Andersencb57d552001-06-28 07:25:16 +000011224#else
11225# define PIPESIZE PIPE_BUF
11226#endif
11227
11228
Eric Andersen62483552001-07-10 06:09:16 +000011229/*
11230 * Open a file in noclobber mode.
11231 * The code was copied from bash.
11232 */
11233static inline int
11234noclobberopen(const char *fname)
11235{
11236 int r, fd;
11237 struct stat finfo, finfo2;
11238
11239 /*
11240 * If the file exists and is a regular file, return an error
11241 * immediately.
11242 */
11243 r = stat(fname, &finfo);
11244 if (r == 0 && S_ISREG(finfo.st_mode)) {
11245 errno = EEXIST;
11246 return -1;
11247 }
11248
11249 /*
11250 * If the file was not present (r != 0), make sure we open it
11251 * exclusively so that if it is created before we open it, our open
11252 * will fail. Make sure that we do not truncate an existing file.
11253 * Note that we don't turn on O_EXCL unless the stat failed -- if the
11254 * file was not a regular file, we leave O_EXCL off.
11255 */
11256 if (r != 0)
11257 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
11258 fd = open(fname, O_WRONLY|O_CREAT, 0666);
11259
11260 /* If the open failed, return the file descriptor right away. */
11261 if (fd < 0)
11262 return fd;
11263
11264 /*
11265 * OK, the open succeeded, but the file may have been changed from a
11266 * non-regular file to a regular file between the stat and the open.
11267 * We are assuming that the O_EXCL open handles the case where FILENAME
11268 * did not exist and is symlinked to an existing file between the stat
11269 * and open.
11270 */
11271
11272 /*
11273 * If we can open it and fstat the file descriptor, and neither check
11274 * revealed that it was a regular file, and the file has not been
11275 * replaced, return the file descriptor.
11276 */
11277 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
11278 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
11279 return fd;
11280
11281 /* The file has been replaced. badness. */
11282 close(fd);
11283 errno = EEXIST;
11284 return -1;
11285}
Eric Andersencb57d552001-06-28 07:25:16 +000011286
11287/*
Eric Andersen62483552001-07-10 06:09:16 +000011288 * Handle here documents. Normally we fork off a process to write the
11289 * data to a pipe. If the document is short, we can stuff the data in
11290 * the pipe without forking.
Eric Andersencb57d552001-06-28 07:25:16 +000011291 */
11292
Eric Andersen62483552001-07-10 06:09:16 +000011293static inline int
11294openhere(const union node *redir)
11295{
11296 int pip[2];
11297 int len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011298
Eric Andersen62483552001-07-10 06:09:16 +000011299 if (pipe(pip) < 0)
11300 error("Pipe call failed");
11301 if (redir->type == NHERE) {
11302 len = strlen(redir->nhere.doc->narg.text);
11303 if (len <= PIPESIZE) {
11304 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11305 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011306 }
Eric Andersencb57d552001-06-28 07:25:16 +000011307 }
Eric Andersen62483552001-07-10 06:09:16 +000011308 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
11309 close(pip[0]);
11310 signal(SIGINT, SIG_IGN);
11311 signal(SIGQUIT, SIG_IGN);
11312 signal(SIGHUP, SIG_IGN);
11313#ifdef SIGTSTP
11314 signal(SIGTSTP, SIG_IGN);
11315#endif
11316 signal(SIGPIPE, SIG_DFL);
11317 if (redir->type == NHERE)
11318 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11319 else
11320 expandhere(redir->nhere.doc, pip[1]);
11321 _exit(0);
11322 }
11323out:
11324 close(pip[1]);
11325 return pip[0];
Eric Andersencb57d552001-06-28 07:25:16 +000011326}
11327
11328
Eric Andersen62483552001-07-10 06:09:16 +000011329static inline int
11330openredirect(const union node *redir)
11331{
Eric Andersencb57d552001-06-28 07:25:16 +000011332 char *fname;
11333 int f;
11334
11335 switch (redir->nfile.type) {
11336 case NFROM:
11337 fname = redir->nfile.expfname;
11338 if ((f = open(fname, O_RDONLY)) < 0)
11339 goto eopen;
11340 break;
11341 case NFROMTO:
11342 fname = redir->nfile.expfname;
11343 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
11344 goto ecreate;
11345 break;
11346 case NTO:
11347 /* Take care of noclobber mode. */
11348 if (Cflag) {
11349 fname = redir->nfile.expfname;
11350 if ((f = noclobberopen(fname)) < 0)
11351 goto ecreate;
11352 break;
11353 }
11354 case NTOOV:
11355 fname = redir->nfile.expfname;
11356#ifdef O_CREAT
11357 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
11358 goto ecreate;
11359#else
11360 if ((f = creat(fname, 0666)) < 0)
11361 goto ecreate;
11362#endif
11363 break;
11364 case NAPPEND:
11365 fname = redir->nfile.expfname;
11366#ifdef O_APPEND
11367 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
11368 goto ecreate;
11369#else
11370 if ((f = open(fname, O_WRONLY)) < 0
11371 && (f = creat(fname, 0666)) < 0)
11372 goto ecreate;
11373 lseek(f, (off_t)0, 2);
11374#endif
11375 break;
11376 default:
11377#ifdef DEBUG
11378 abort();
11379#endif
11380 /* Fall through to eliminate warning. */
11381 case NTOFD:
11382 case NFROMFD:
11383 f = -1;
11384 break;
11385 case NHERE:
11386 case NXHERE:
11387 f = openhere(redir);
11388 break;
11389 }
11390
11391 return f;
11392ecreate:
11393 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11394eopen:
11395 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11396}
11397
11398
Eric Andersen62483552001-07-10 06:09:16 +000011399/*
11400 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11401 * old file descriptors are stashed away so that the redirection can be
11402 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11403 * standard output, and the standard error if it becomes a duplicate of
11404 * stdout.
11405 */
11406
Eric Andersencb57d552001-06-28 07:25:16 +000011407static void
Eric Andersen62483552001-07-10 06:09:16 +000011408redirect(union node *redir, int flags)
11409{
11410 union node *n;
11411 struct redirtab *sv = NULL;
11412 int i;
11413 int fd;
11414 int newfd;
11415 int try;
11416 int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
11417
11418 if (flags & REDIR_PUSH) {
11419 sv = ckmalloc(sizeof (struct redirtab));
11420 for (i = 0 ; i < 10 ; i++)
11421 sv->renamed[i] = EMPTY;
11422 sv->next = redirlist;
11423 redirlist = sv;
11424 }
11425 for (n = redir ; n ; n = n->nfile.next) {
11426 fd = n->nfile.fd;
11427 try = 0;
11428 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
11429 n->ndup.dupfd == fd)
11430 continue; /* redirect from/to same file descriptor */
11431
11432 INTOFF;
11433 newfd = openredirect(n);
11434 if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
11435 if (newfd == fd) {
11436 try++;
11437 } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
11438 switch (errno) {
11439 case EBADF:
11440 if (!try) {
11441 dupredirect(n, newfd, fd1dup);
11442 try++;
11443 break;
11444 }
11445 /* FALLTHROUGH*/
11446 default:
11447 if (newfd >= 0) {
11448 close(newfd);
11449 }
11450 INTON;
11451 error("%d: %m", fd);
11452 /* NOTREACHED */
11453 }
11454 }
11455 if (!try) {
11456 close(fd);
11457 if (flags & REDIR_PUSH) {
11458 sv->renamed[fd] = i;
11459 }
11460 }
11461 } else if (fd != newfd) {
11462 close(fd);
11463 }
11464 if (fd == 0)
11465 fd0_redirected++;
11466 if (!try)
11467 dupredirect(n, newfd, fd1dup);
11468 INTON;
11469 }
11470}
11471
11472
11473static void
11474dupredirect(const union node *redir, int f, int fd1dup)
Eric Andersen2870d962001-07-02 17:27:21 +000011475{
Eric Andersencb57d552001-06-28 07:25:16 +000011476 int fd = redir->nfile.fd;
11477
Eric Andersen62483552001-07-10 06:09:16 +000011478 if(fd==1)
11479 fd1dup = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011480 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Eric Andersen2870d962001-07-02 17:27:21 +000011481 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
Eric Andersen62483552001-07-10 06:09:16 +000011482 if (redir->ndup.dupfd!=1 || fd1dup!=1)
Eric Andersencb57d552001-06-28 07:25:16 +000011483 dup_as_newfd(redir->ndup.dupfd, fd);
11484 }
11485 return;
11486 }
11487
11488 if (f != fd) {
11489 dup_as_newfd(f, fd);
11490 close(f);
11491 }
11492 return;
11493}
11494
11495
Eric Andersencb57d552001-06-28 07:25:16 +000011496
Eric Andersencb57d552001-06-28 07:25:16 +000011497/*
11498 * Undo the effects of the last redirection.
11499 */
11500
11501static void
Eric Andersen2870d962001-07-02 17:27:21 +000011502popredir(void)
11503{
Eric Andersencb57d552001-06-28 07:25:16 +000011504 struct redirtab *rp = redirlist;
11505 int i;
11506
11507 INTOFF;
11508 for (i = 0 ; i < 10 ; i++) {
11509 if (rp->renamed[i] != EMPTY) {
Eric Andersen2870d962001-07-02 17:27:21 +000011510 if (i == 0)
11511 fd0_redirected--;
Eric Andersencb57d552001-06-28 07:25:16 +000011512 close(i);
11513 if (rp->renamed[i] >= 0) {
11514 dup_as_newfd(rp->renamed[i], i);
11515 close(rp->renamed[i]);
11516 }
Eric Andersencb57d552001-06-28 07:25:16 +000011517 }
11518 }
11519 redirlist = rp->next;
11520 ckfree(rp);
11521 INTON;
11522}
11523
11524/*
Eric Andersencb57d552001-06-28 07:25:16 +000011525 * Discard all saved file descriptors.
11526 */
11527
11528static void
Eric Andersen2870d962001-07-02 17:27:21 +000011529clearredir(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000011530 struct redirtab *rp;
11531 int i;
11532
11533 for (rp = redirlist ; rp ; rp = rp->next) {
11534 for (i = 0 ; i < 10 ; i++) {
11535 if (rp->renamed[i] >= 0) {
11536 close(rp->renamed[i]);
Eric Andersencb57d552001-06-28 07:25:16 +000011537 }
11538 rp->renamed[i] = EMPTY;
11539 }
11540 }
Eric Andersencb57d552001-06-28 07:25:16 +000011541}
11542
11543
Eric Andersencb57d552001-06-28 07:25:16 +000011544/*
11545 * Copy a file descriptor to be >= to. Returns -1
11546 * if the source file descriptor is closed, EMPTY if there are no unused
11547 * file descriptors left.
11548 */
11549
11550static int
11551dup_as_newfd(from, to)
11552 int from;
11553 int to;
11554{
11555 int newfd;
11556
11557 newfd = fcntl(from, F_DUPFD, to);
11558 if (newfd < 0) {
11559 if (errno == EMFILE)
11560 return EMPTY;
11561 else
Eric Andersen2870d962001-07-02 17:27:21 +000011562 error("%d: %m", from);
Eric Andersencb57d552001-06-28 07:25:16 +000011563 }
11564 return newfd;
11565}
11566
Eric Andersencb57d552001-06-28 07:25:16 +000011567#ifdef DEBUG
Eric Andersen2870d962001-07-02 17:27:21 +000011568static void shtree (union node *, int, char *, FILE*);
11569static void shcmd (union node *, FILE *);
11570static void sharg (union node *, FILE *);
11571static void indent (int, char *, FILE *);
11572static void trstring (char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011573
11574
11575static void
11576showtree(n)
11577 union node *n;
11578{
11579 trputs("showtree called\n");
11580 shtree(n, 1, NULL, stdout);
11581}
11582
11583
11584static void
11585shtree(n, ind, pfx, fp)
11586 union node *n;
11587 int ind;
11588 char *pfx;
11589 FILE *fp;
11590{
11591 struct nodelist *lp;
11592 const char *s;
11593
11594 if (n == NULL)
11595 return;
11596
11597 indent(ind, pfx, fp);
11598 switch(n->type) {
11599 case NSEMI:
11600 s = "; ";
11601 goto binop;
11602 case NAND:
11603 s = " && ";
11604 goto binop;
11605 case NOR:
11606 s = " || ";
11607binop:
11608 shtree(n->nbinary.ch1, ind, NULL, fp);
11609 /* if (ind < 0) */
11610 fputs(s, fp);
11611 shtree(n->nbinary.ch2, ind, NULL, fp);
11612 break;
11613 case NCMD:
11614 shcmd(n, fp);
11615 if (ind >= 0)
11616 putc('\n', fp);
11617 break;
11618 case NPIPE:
11619 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11620 shcmd(lp->n, fp);
11621 if (lp->next)
11622 fputs(" | ", fp);
11623 }
11624 if (n->npipe.backgnd)
11625 fputs(" &", fp);
11626 if (ind >= 0)
11627 putc('\n', fp);
11628 break;
11629 default:
11630 fprintf(fp, "<node type %d>", n->type);
11631 if (ind >= 0)
11632 putc('\n', fp);
11633 break;
11634 }
11635}
11636
11637
11638
11639static void
11640shcmd(cmd, fp)
11641 union node *cmd;
11642 FILE *fp;
11643{
11644 union node *np;
11645 int first;
11646 const char *s;
11647 int dftfd;
11648
11649 first = 1;
11650 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11651 if (! first)
11652 putchar(' ');
11653 sharg(np, fp);
11654 first = 0;
11655 }
11656 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11657 if (! first)
11658 putchar(' ');
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011659#if 1
11660 s = "*error*";
11661 dftfd = 0;
11662 if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) {
11663 s = redir_strings[np->nfile.type - NTO];
11664 if (*s == '>') {
11665 dftfd = 1;
11666 }
11667 }
11668#else
Eric Andersencb57d552001-06-28 07:25:16 +000011669 switch (np->nfile.type) {
Eric Andersen2870d962001-07-02 17:27:21 +000011670 case NTO: s = ">"; dftfd = 1; break;
11671 case NAPPEND: s = ">>"; dftfd = 1; break;
11672 case NTOFD: s = ">&"; dftfd = 1; break;
11673 case NTOOV: s = ">|"; dftfd = 1; break;
11674 case NFROM: s = "<"; dftfd = 0; break;
11675 case NFROMFD: s = "<&"; dftfd = 0; break;
11676 case NFROMTO: s = "<>"; dftfd = 0; break;
11677 default: s = "*error*"; dftfd = 0; break;
Eric Andersencb57d552001-06-28 07:25:16 +000011678 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011679#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011680 if (np->nfile.fd != dftfd)
11681 fprintf(fp, "%d", np->nfile.fd);
11682 fputs(s, fp);
11683 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11684 fprintf(fp, "%d", np->ndup.dupfd);
11685 } else {
11686 sharg(np->nfile.fname, fp);
11687 }
11688 first = 0;
11689 }
11690}
11691
11692
11693
11694static void
11695sharg(arg, fp)
11696 union node *arg;
11697 FILE *fp;
11698 {
11699 char *p;
11700 struct nodelist *bqlist;
11701 int subtype;
11702
11703 if (arg->type != NARG) {
11704 printf("<node type %d>\n", arg->type);
11705 fflush(stdout);
11706 abort();
11707 }
11708 bqlist = arg->narg.backquote;
11709 for (p = arg->narg.text ; *p ; p++) {
11710 switch (*p) {
11711 case CTLESC:
11712 putc(*++p, fp);
11713 break;
11714 case CTLVAR:
11715 putc('$', fp);
11716 putc('{', fp);
11717 subtype = *++p;
11718 if (subtype == VSLENGTH)
11719 putc('#', fp);
11720
11721 while (*p != '=')
11722 putc(*p++, fp);
11723
11724 if (subtype & VSNUL)
11725 putc(':', fp);
11726
11727 switch (subtype & VSTYPE) {
11728 case VSNORMAL:
11729 putc('}', fp);
11730 break;
11731 case VSMINUS:
11732 putc('-', fp);
11733 break;
11734 case VSPLUS:
11735 putc('+', fp);
11736 break;
11737 case VSQUESTION:
11738 putc('?', fp);
11739 break;
11740 case VSASSIGN:
11741 putc('=', fp);
11742 break;
11743 case VSTRIMLEFT:
11744 putc('#', fp);
11745 break;
11746 case VSTRIMLEFTMAX:
11747 putc('#', fp);
11748 putc('#', fp);
11749 break;
11750 case VSTRIMRIGHT:
11751 putc('%', fp);
11752 break;
11753 case VSTRIMRIGHTMAX:
11754 putc('%', fp);
11755 putc('%', fp);
11756 break;
11757 case VSLENGTH:
11758 break;
11759 default:
11760 printf("<subtype %d>", subtype);
11761 }
11762 break;
11763 case CTLENDVAR:
11764 putc('}', fp);
11765 break;
11766 case CTLBACKQ:
11767 case CTLBACKQ|CTLQUOTE:
11768 putc('$', fp);
11769 putc('(', fp);
11770 shtree(bqlist->n, -1, NULL, fp);
11771 putc(')', fp);
11772 break;
11773 default:
11774 putc(*p, fp);
11775 break;
11776 }
11777 }
11778}
11779
11780
11781static void
11782indent(amount, pfx, fp)
11783 int amount;
11784 char *pfx;
11785 FILE *fp;
11786{
11787 int i;
11788
11789 for (i = 0 ; i < amount ; i++) {
11790 if (pfx && i == amount - 1)
11791 fputs(pfx, fp);
11792 putc('\t', fp);
11793 }
11794}
11795#endif
11796
11797
11798
11799/*
11800 * Debugging stuff.
11801 */
11802
11803
11804#ifdef DEBUG
11805FILE *tracefile;
11806
11807#if DEBUG == 2
11808static int debug = 1;
11809#else
11810static int debug = 0;
11811#endif
11812
11813
11814static void
11815trputc(c)
11816 int c;
11817{
11818 if (tracefile == NULL)
11819 return;
11820 putc(c, tracefile);
11821 if (c == '\n')
11822 fflush(tracefile);
11823}
11824
11825static void
11826trace(const char *fmt, ...)
11827{
11828 va_list va;
Eric Andersencb57d552001-06-28 07:25:16 +000011829 va_start(va, fmt);
Eric Andersencb57d552001-06-28 07:25:16 +000011830 if (tracefile != NULL) {
11831 (void) vfprintf(tracefile, fmt, va);
11832 if (strchr(fmt, '\n'))
11833 (void) fflush(tracefile);
11834 }
11835 va_end(va);
11836}
11837
11838
11839static void
11840trputs(s)
11841 const char *s;
11842{
11843 if (tracefile == NULL)
11844 return;
11845 fputs(s, tracefile);
11846 if (strchr(s, '\n'))
11847 fflush(tracefile);
11848}
11849
11850
11851static void
11852trstring(s)
11853 char *s;
11854{
11855 char *p;
11856 char c;
11857
11858 if (tracefile == NULL)
11859 return;
11860 putc('"', tracefile);
11861 for (p = s ; *p ; p++) {
11862 switch (*p) {
11863 case '\n': c = 'n'; goto backslash;
11864 case '\t': c = 't'; goto backslash;
11865 case '\r': c = 'r'; goto backslash;
11866 case '"': c = '"'; goto backslash;
11867 case '\\': c = '\\'; goto backslash;
11868 case CTLESC: c = 'e'; goto backslash;
11869 case CTLVAR: c = 'v'; goto backslash;
11870 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11871 case CTLBACKQ: c = 'q'; goto backslash;
11872 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Eric Andersen2870d962001-07-02 17:27:21 +000011873backslash: putc('\\', tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011874 putc(c, tracefile);
11875 break;
11876 default:
11877 if (*p >= ' ' && *p <= '~')
11878 putc(*p, tracefile);
11879 else {
11880 putc('\\', tracefile);
11881 putc(*p >> 6 & 03, tracefile);
11882 putc(*p >> 3 & 07, tracefile);
11883 putc(*p & 07, tracefile);
11884 }
11885 break;
11886 }
11887 }
11888 putc('"', tracefile);
11889}
11890
11891
11892static void
11893trargs(ap)
11894 char **ap;
11895{
11896 if (tracefile == NULL)
11897 return;
11898 while (*ap) {
11899 trstring(*ap++);
11900 if (*ap)
11901 putc(' ', tracefile);
11902 else
11903 putc('\n', tracefile);
11904 }
11905 fflush(tracefile);
11906}
11907
11908
11909static void
11910opentrace() {
11911 char s[100];
11912#ifdef O_APPEND
11913 int flags;
11914#endif
11915
11916 if (!debug)
11917 return;
11918#ifdef not_this_way
11919 {
11920 char *p;
11921 if ((p = getenv("HOME")) == NULL) {
11922 if (geteuid() == 0)
11923 p = "/";
11924 else
11925 p = "/tmp";
11926 }
Eric Andersen2870d962001-07-02 17:27:21 +000011927 strcpy(s, p);
Eric Andersencb57d552001-06-28 07:25:16 +000011928 strcat(s, "/trace");
11929 }
11930#else
Eric Andersen2870d962001-07-02 17:27:21 +000011931 strcpy(s, "./trace");
Eric Andersencb57d552001-06-28 07:25:16 +000011932#endif /* not_this_way */
11933 if ((tracefile = fopen(s, "a")) == NULL) {
11934 fprintf(stderr, "Can't open %s\n", s);
11935 return;
11936 }
11937#ifdef O_APPEND
11938 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11939 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11940#endif
11941 fputs("\nTracing started.\n", tracefile);
11942 fflush(tracefile);
11943}
11944#endif /* DEBUG */
11945
11946
11947/*
Eric Andersencb57d552001-06-28 07:25:16 +000011948 * The trap builtin.
11949 */
11950
11951static int
11952trapcmd(argc, argv)
11953 int argc;
11954 char **argv;
11955{
11956 char *action;
11957 char **ap;
11958 int signo;
11959
11960 if (argc <= 1) {
11961 for (signo = 0 ; signo < NSIG ; signo++) {
11962 if (trap[signo] != NULL) {
11963 char *p;
Eric Andersen34506362001-08-02 05:02:46 +000011964 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011965
11966 p = single_quote(trap[signo]);
Eric Andersen34506362001-08-02 05:02:46 +000011967 sn = sys_siglist[signo];
11968 if(sn==NULL)
11969 sn = u_signal_names(0, &signo, 0);
11970 if(sn==NULL)
11971 sn = "???";
11972 printf("trap -- %s %s\n", p, sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011973 stunalloc(p);
11974 }
11975 }
11976 return 0;
11977 }
11978 ap = argv + 1;
11979 if (argc == 2)
11980 action = NULL;
11981 else
11982 action = *ap++;
11983 while (*ap) {
11984 if ((signo = decode_signal(*ap, 0)) < 0)
11985 error("%s: bad trap", *ap);
11986 INTOFF;
11987 if (action) {
11988 if (action[0] == '-' && action[1] == '\0')
11989 action = NULL;
11990 else
11991 action = savestr(action);
11992 }
11993 if (trap[signo])
11994 ckfree(trap[signo]);
11995 trap[signo] = action;
11996 if (signo != 0)
11997 setsignal(signo);
11998 INTON;
11999 ap++;
12000 }
12001 return 0;
12002}
12003
12004
12005
Eric Andersencb57d552001-06-28 07:25:16 +000012006
12007
12008
12009/*
12010 * Set the signal handler for the specified signal. The routine figures
12011 * out what it should be set to.
12012 */
12013
12014static void
Eric Andersen2870d962001-07-02 17:27:21 +000012015setsignal(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000012016{
12017 int action;
12018 char *t;
12019 struct sigaction act;
12020
12021 if ((t = trap[signo]) == NULL)
12022 action = S_DFL;
12023 else if (*t != '\0')
12024 action = S_CATCH;
12025 else
12026 action = S_IGN;
12027 if (rootshell && action == S_DFL) {
12028 switch (signo) {
12029 case SIGINT:
12030 if (iflag || minusc || sflag == 0)
12031 action = S_CATCH;
12032 break;
12033 case SIGQUIT:
12034#ifdef DEBUG
12035 {
Eric Andersencb57d552001-06-28 07:25:16 +000012036
12037 if (debug)
12038 break;
12039 }
12040#endif
12041 /* FALLTHROUGH */
12042 case SIGTERM:
12043 if (iflag)
12044 action = S_IGN;
12045 break;
Eric Andersen2870d962001-07-02 17:27:21 +000012046#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +000012047 case SIGTSTP:
12048 case SIGTTOU:
12049 if (mflag)
12050 action = S_IGN;
12051 break;
12052#endif
12053 }
12054 }
12055
12056 t = &sigmode[signo - 1];
12057 if (*t == 0) {
12058 /*
12059 * current setting unknown
12060 */
12061 if (sigaction(signo, 0, &act) == -1) {
12062 /*
12063 * Pretend it worked; maybe we should give a warning
12064 * here, but other shells don't. We don't alter
12065 * sigmode, so that we retry every time.
12066 */
12067 return;
12068 }
12069 if (act.sa_handler == SIG_IGN) {
12070 if (mflag && (signo == SIGTSTP ||
12071 signo == SIGTTIN || signo == SIGTTOU)) {
Eric Andersen2870d962001-07-02 17:27:21 +000012072 *t = S_IGN; /* don't hard ignore these */
Eric Andersencb57d552001-06-28 07:25:16 +000012073 } else
12074 *t = S_HARD_IGN;
12075 } else {
Eric Andersen2870d962001-07-02 17:27:21 +000012076 *t = S_RESET; /* force to be set */
Eric Andersencb57d552001-06-28 07:25:16 +000012077 }
12078 }
12079 if (*t == S_HARD_IGN || *t == action)
12080 return;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012081 act.sa_handler = ((action == S_CATCH) ? onsig
12082 : ((action == S_IGN) ? SIG_IGN : SIG_DFL));
Eric Andersencb57d552001-06-28 07:25:16 +000012083 *t = action;
12084 act.sa_flags = 0;
12085 sigemptyset(&act.sa_mask);
12086 sigaction(signo, &act, 0);
12087}
12088
12089/*
12090 * Ignore a signal.
12091 */
12092
12093static void
12094ignoresig(signo)
12095 int signo;
12096{
12097 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
12098 signal(signo, SIG_IGN);
12099 }
12100 sigmode[signo - 1] = S_HARD_IGN;
12101}
12102
12103
Eric Andersencb57d552001-06-28 07:25:16 +000012104/*
12105 * Signal handler.
12106 */
12107
12108static void
Eric Andersen2870d962001-07-02 17:27:21 +000012109onsig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000012110{
12111 if (signo == SIGINT && trap[SIGINT] == NULL) {
12112 onint();
12113 return;
12114 }
12115 gotsig[signo - 1] = 1;
12116 pendingsigs++;
12117}
12118
12119
Eric Andersencb57d552001-06-28 07:25:16 +000012120/*
12121 * Called to execute a trap. Perhaps we should avoid entering new trap
12122 * handlers while we are executing a trap handler.
12123 */
12124
12125static void
Eric Andersen2870d962001-07-02 17:27:21 +000012126dotrap(void)
12127{
Eric Andersencb57d552001-06-28 07:25:16 +000012128 int i;
12129 int savestatus;
12130
12131 for (;;) {
12132 for (i = 1 ; ; i++) {
12133 if (gotsig[i - 1])
12134 break;
12135 if (i >= NSIG - 1)
12136 goto done;
12137 }
12138 gotsig[i - 1] = 0;
12139 savestatus=exitstatus;
12140 evalstring(trap[i], 0);
12141 exitstatus=savestatus;
12142 }
12143done:
12144 pendingsigs = 0;
12145}
12146
Eric Andersencb57d552001-06-28 07:25:16 +000012147/*
12148 * Called to exit the shell.
12149 */
12150
12151static void
Eric Andersen2870d962001-07-02 17:27:21 +000012152exitshell(int status)
Eric Andersencb57d552001-06-28 07:25:16 +000012153{
12154 struct jmploc loc1, loc2;
12155 char *p;
12156
12157 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
12158 if (setjmp(loc1.loc)) {
12159 goto l1;
12160 }
12161 if (setjmp(loc2.loc)) {
12162 goto l2;
12163 }
12164 handler = &loc1;
12165 if ((p = trap[0]) != NULL && *p != '\0') {
12166 trap[0] = NULL;
12167 evalstring(p, 0);
12168 }
Eric Andersen2870d962001-07-02 17:27:21 +000012169l1: handler = &loc2; /* probably unnecessary */
Eric Andersencb57d552001-06-28 07:25:16 +000012170 flushall();
Eric Andersen2870d962001-07-02 17:27:21 +000012171#ifdef JOBS
Eric Andersencb57d552001-06-28 07:25:16 +000012172 setjobctl(0);
12173#endif
12174l2: _exit(status);
12175 /* NOTREACHED */
12176}
12177
12178static int decode_signal(const char *string, int minsig)
12179{
12180 int signo;
Eric Andersen34506362001-08-02 05:02:46 +000012181 const char *name = u_signal_names(string, &signo, minsig);
Eric Andersencb57d552001-06-28 07:25:16 +000012182
Eric Andersen34506362001-08-02 05:02:46 +000012183 return name ? signo : -1;
Eric Andersencb57d552001-06-28 07:25:16 +000012184}
Eric Andersen34506362001-08-02 05:02:46 +000012185
Eric Andersen2870d962001-07-02 17:27:21 +000012186static struct var **hashvar (const char *);
12187static void showvars (const char *, int, int);
12188static struct var **findvar (struct var **, const char *);
Eric Andersencb57d552001-06-28 07:25:16 +000012189
12190/*
12191 * Initialize the varable symbol tables and import the environment
12192 */
12193
Eric Andersencb57d552001-06-28 07:25:16 +000012194/*
12195 * This routine initializes the builtin variables. It is called when the
12196 * shell is initialized and again when a shell procedure is spawned.
12197 */
12198
12199static void
12200initvar() {
12201 const struct varinit *ip;
12202 struct var *vp;
12203 struct var **vpp;
12204
12205 for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
12206 if ((vp->flags & VEXPORT) == 0) {
12207 vpp = hashvar(ip->text);
12208 vp->next = *vpp;
12209 *vpp = vp;
12210 vp->text = strdup(ip->text);
12211 vp->flags = ip->flags;
12212 vp->func = ip->func;
12213 }
12214 }
12215 /*
12216 * PS1 depends on uid
12217 */
12218 if ((vps1.flags & VEXPORT) == 0) {
12219 vpp = hashvar("PS1=");
12220 vps1.next = *vpp;
12221 *vpp = &vps1;
12222 vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# ");
12223 vps1.flags = VSTRFIXED|VTEXTFIXED;
12224 }
12225}
12226
12227/*
12228 * Set the value of a variable. The flags argument is ored with the
12229 * flags of the variable. If val is NULL, the variable is unset.
12230 */
12231
12232static void
12233setvar(name, val, flags)
12234 const char *name, *val;
12235 int flags;
12236{
12237 const char *p;
12238 int len;
12239 int namelen;
12240 char *nameeq;
12241 int isbad;
12242 int vallen = 0;
12243
12244 isbad = 0;
12245 p = name;
12246 if (! is_name(*p))
12247 isbad = 1;
12248 p++;
12249 for (;;) {
12250 if (! is_in_name(*p)) {
12251 if (*p == '\0' || *p == '=')
12252 break;
12253 isbad = 1;
12254 }
12255 p++;
12256 }
12257 namelen = p - name;
12258 if (isbad)
12259 error("%.*s: bad variable name", namelen, name);
Eric Andersen2870d962001-07-02 17:27:21 +000012260 len = namelen + 2; /* 2 is space for '=' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +000012261 if (val == NULL) {
12262 flags |= VUNSET;
12263 } else {
12264 len += vallen = strlen(val);
12265 }
12266 INTOFF;
12267 nameeq = ckmalloc(len);
12268 memcpy(nameeq, name, namelen);
12269 nameeq[namelen] = '=';
12270 if (val) {
12271 memcpy(nameeq + namelen + 1, val, vallen + 1);
12272 } else {
12273 nameeq[namelen + 1] = '\0';
12274 }
12275 setvareq(nameeq, flags);
12276 INTON;
12277}
12278
12279
12280
12281/*
12282 * Same as setvar except that the variable and value are passed in
12283 * the first argument as name=value. Since the first argument will
12284 * be actually stored in the table, it should not be a string that
12285 * will go away.
12286 */
12287
12288static void
12289setvareq(s, flags)
12290 char *s;
12291 int flags;
12292{
12293 struct var *vp, **vpp;
12294
12295 vpp = hashvar(s);
12296 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
12297 if ((vp = *findvar(vpp, s))) {
12298 if (vp->flags & VREADONLY) {
12299 size_t len = strchr(s, '=') - s;
12300 error("%.*s: is read only", len, s);
12301 }
12302 INTOFF;
12303
12304 if (vp->func && (flags & VNOFUNC) == 0)
12305 (*vp->func)(strchr(s, '=') + 1);
12306
12307 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12308 ckfree(vp->text);
12309
12310 vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
12311 vp->flags |= flags;
12312 vp->text = s;
12313
12314 /*
12315 * We could roll this to a function, to handle it as
12316 * a regular variable function callback, but why bother?
12317 */
12318 if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset())))
12319 chkmail(1);
12320 INTON;
12321 return;
12322 }
12323 /* not found */
12324 vp = ckmalloc(sizeof (*vp));
12325 vp->flags = flags;
12326 vp->text = s;
12327 vp->next = *vpp;
12328 vp->func = NULL;
12329 *vpp = vp;
12330}
12331
12332
12333
12334/*
12335 * Process a linked list of variable assignments.
12336 */
12337
12338static void
12339listsetvar(mylist)
12340 struct strlist *mylist;
12341 {
12342 struct strlist *lp;
12343
12344 INTOFF;
12345 for (lp = mylist ; lp ; lp = lp->next) {
12346 setvareq(savestr(lp->text), 0);
12347 }
12348 INTON;
12349}
12350
12351
12352
12353/*
12354 * Find the value of a variable. Returns NULL if not set.
12355 */
12356
Eric Andersen62483552001-07-10 06:09:16 +000012357static const char *
Eric Andersencb57d552001-06-28 07:25:16 +000012358lookupvar(name)
12359 const char *name;
12360 {
12361 struct var *v;
12362
12363 if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
12364 return strchr(v->text, '=') + 1;
12365 }
12366 return NULL;
12367}
12368
12369
12370
12371/*
12372 * Search the environment of a builtin command.
12373 */
12374
Eric Andersen62483552001-07-10 06:09:16 +000012375static const char *
12376bltinlookup(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +000012377{
Eric Andersen62483552001-07-10 06:09:16 +000012378 const struct strlist *sp;
Eric Andersencb57d552001-06-28 07:25:16 +000012379
12380 for (sp = cmdenviron ; sp ; sp = sp->next) {
12381 if (varequal(sp->text, name))
12382 return strchr(sp->text, '=') + 1;
12383 }
12384 return lookupvar(name);
12385}
12386
12387
12388
12389/*
12390 * Generate a list of exported variables. This routine is used to construct
12391 * the third argument to execve when executing a program.
12392 */
12393
12394static char **
12395environment() {
12396 int nenv;
12397 struct var **vpp;
12398 struct var *vp;
12399 char **env;
12400 char **ep;
12401
12402 nenv = 0;
12403 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12404 for (vp = *vpp ; vp ; vp = vp->next)
12405 if (vp->flags & VEXPORT)
12406 nenv++;
12407 }
12408 ep = env = stalloc((nenv + 1) * sizeof *env);
12409 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12410 for (vp = *vpp ; vp ; vp = vp->next)
12411 if (vp->flags & VEXPORT)
12412 *ep++ = vp->text;
12413 }
12414 *ep = NULL;
12415 return env;
12416}
12417
12418
12419/*
12420 * Called when a shell procedure is invoked to clear out nonexported
12421 * variables. It is also necessary to reallocate variables of with
12422 * VSTACK set since these are currently allocated on the stack.
12423 */
12424
Eric Andersencb57d552001-06-28 07:25:16 +000012425static void
Eric Andersen2870d962001-07-02 17:27:21 +000012426shprocvar(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000012427 struct var **vpp;
12428 struct var *vp, **prev;
12429
12430 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12431 for (prev = vpp ; (vp = *prev) != NULL ; ) {
12432 if ((vp->flags & VEXPORT) == 0) {
12433 *prev = vp->next;
12434 if ((vp->flags & VTEXTFIXED) == 0)
12435 ckfree(vp->text);
12436 if ((vp->flags & VSTRFIXED) == 0)
12437 ckfree(vp);
12438 } else {
12439 if (vp->flags & VSTACK) {
12440 vp->text = savestr(vp->text);
12441 vp->flags &=~ VSTACK;
12442 }
12443 prev = &vp->next;
12444 }
12445 }
12446 }
12447 initvar();
12448}
12449
12450
12451
12452/*
12453 * Command to list all variables which are set. Currently this command
12454 * is invoked from the set command when the set command is called without
12455 * any variables.
12456 */
12457
12458static int
12459showvarscmd(argc, argv)
12460 int argc;
12461 char **argv;
12462{
12463 showvars(nullstr, VUNSET, VUNSET);
12464 return 0;
12465}
12466
12467
12468
12469/*
12470 * The export and readonly commands.
12471 */
12472
12473static int
12474exportcmd(argc, argv)
12475 int argc;
12476 char **argv;
12477{
12478 struct var *vp;
12479 char *name;
12480 const char *p;
12481 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
12482 int pflag;
12483
12484 listsetvar(cmdenviron);
12485 pflag = (nextopt("p") == 'p');
12486 if (argc > 1 && !pflag) {
12487 while ((name = *argptr++) != NULL) {
12488 if ((p = strchr(name, '=')) != NULL) {
12489 p++;
12490 } else {
12491 if ((vp = *findvar(hashvar(name), name))) {
12492 vp->flags |= flag;
12493 goto found;
12494 }
12495 }
12496 setvar(name, p, flag);
12497found:;
12498 }
12499 } else {
12500 showvars(argv[0], flag, 0);
12501 }
12502 return 0;
12503}
12504
Eric Andersen34506362001-08-02 05:02:46 +000012505
Eric Andersencb57d552001-06-28 07:25:16 +000012506/*
12507 * The "local" command.
12508 */
12509
Eric Andersen2870d962001-07-02 17:27:21 +000012510/* funcnest nonzero if we are currently evaluating a function */
12511
Eric Andersencb57d552001-06-28 07:25:16 +000012512static int
12513localcmd(argc, argv)
12514 int argc;
12515 char **argv;
12516{
12517 char *name;
12518
Eric Andersen2870d962001-07-02 17:27:21 +000012519 if (! funcnest)
Eric Andersencb57d552001-06-28 07:25:16 +000012520 error("Not in a function");
12521 while ((name = *argptr++) != NULL) {
12522 mklocal(name);
12523 }
12524 return 0;
12525}
12526
12527
12528/*
12529 * Make a variable a local variable. When a variable is made local, it's
12530 * value and flags are saved in a localvar structure. The saved values
12531 * will be restored when the shell function returns. We handle the name
12532 * "-" as a special case.
12533 */
12534
12535static void
12536mklocal(name)
12537 char *name;
12538 {
12539 struct localvar *lvp;
12540 struct var **vpp;
12541 struct var *vp;
12542
12543 INTOFF;
12544 lvp = ckmalloc(sizeof (struct localvar));
12545 if (name[0] == '-' && name[1] == '\0') {
12546 char *p;
Eric Andersen2870d962001-07-02 17:27:21 +000012547 p = ckmalloc(sizeof optet_vals);
12548 lvp->text = memcpy(p, optet_vals, sizeof optet_vals);
Eric Andersencb57d552001-06-28 07:25:16 +000012549 vp = NULL;
12550 } else {
12551 vpp = hashvar(name);
12552 vp = *findvar(vpp, name);
12553 if (vp == NULL) {
12554 if (strchr(name, '='))
12555 setvareq(savestr(name), VSTRFIXED);
12556 else
12557 setvar(name, NULL, VSTRFIXED);
Eric Andersen2870d962001-07-02 17:27:21 +000012558 vp = *vpp; /* the new variable */
Eric Andersencb57d552001-06-28 07:25:16 +000012559 lvp->text = NULL;
12560 lvp->flags = VUNSET;
12561 } else {
12562 lvp->text = vp->text;
12563 lvp->flags = vp->flags;
12564 vp->flags |= VSTRFIXED|VTEXTFIXED;
12565 if (strchr(name, '='))
12566 setvareq(savestr(name), 0);
12567 }
12568 }
12569 lvp->vp = vp;
12570 lvp->next = localvars;
12571 localvars = lvp;
12572 INTON;
12573}
12574
12575
12576/*
12577 * Called after a function returns.
12578 */
12579
12580static void
12581poplocalvars() {
12582 struct localvar *lvp;
12583 struct var *vp;
12584
12585 while ((lvp = localvars) != NULL) {
12586 localvars = lvp->next;
12587 vp = lvp->vp;
Eric Andersen2870d962001-07-02 17:27:21 +000012588 if (vp == NULL) { /* $- saved */
12589 memcpy(optet_vals, lvp->text, sizeof optet_vals);
Eric Andersencb57d552001-06-28 07:25:16 +000012590 ckfree(lvp->text);
12591 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
12592 (void)unsetvar(vp->text);
12593 } else {
12594 if ((vp->flags & VTEXTFIXED) == 0)
12595 ckfree(vp->text);
12596 vp->flags = lvp->flags;
12597 vp->text = lvp->text;
12598 }
12599 ckfree(lvp);
12600 }
12601}
12602
12603
12604static int
12605setvarcmd(argc, argv)
12606 int argc;
12607 char **argv;
12608{
12609 if (argc <= 2)
12610 return unsetcmd(argc, argv);
12611 else if (argc == 3)
12612 setvar(argv[1], argv[2], 0);
12613 else
12614 error("List assignment not implemented");
12615 return 0;
12616}
12617
12618
12619/*
12620 * The unset builtin command. We unset the function before we unset the
12621 * variable to allow a function to be unset when there is a readonly variable
12622 * with the same name.
12623 */
12624
12625static int
12626unsetcmd(argc, argv)
12627 int argc;
12628 char **argv;
12629{
12630 char **ap;
12631 int i;
12632 int flg_func = 0;
12633 int flg_var = 0;
12634 int ret = 0;
12635
12636 while ((i = nextopt("vf")) != '\0') {
12637 if (i == 'f')
12638 flg_func = 1;
12639 else
12640 flg_var = 1;
12641 }
12642 if (flg_func == 0 && flg_var == 0)
12643 flg_var = 1;
12644
12645 for (ap = argptr; *ap ; ap++) {
12646 if (flg_func)
12647 unsetfunc(*ap);
12648 if (flg_var)
12649 ret |= unsetvar(*ap);
12650 }
12651 return ret;
12652}
12653
12654
12655/*
12656 * Unset the specified variable.
12657 */
12658
12659static int
Eric Andersen62483552001-07-10 06:09:16 +000012660unsetvar(const char *s)
12661{
Eric Andersencb57d552001-06-28 07:25:16 +000012662 struct var **vpp;
12663 struct var *vp;
12664
12665 vpp = findvar(hashvar(s), s);
12666 vp = *vpp;
12667 if (vp) {
12668 if (vp->flags & VREADONLY)
12669 return (1);
12670 INTOFF;
12671 if (*(strchr(vp->text, '=') + 1) != '\0')
12672 setvar(s, nullstr, 0);
12673 vp->flags &= ~VEXPORT;
12674 vp->flags |= VUNSET;
12675 if ((vp->flags & VSTRFIXED) == 0) {
12676 if ((vp->flags & VTEXTFIXED) == 0)
12677 ckfree(vp->text);
12678 *vpp = vp->next;
12679 ckfree(vp);
12680 }
12681 INTON;
12682 return (0);
12683 }
12684
12685 return (0);
12686}
12687
12688
12689
12690/*
12691 * Find the appropriate entry in the hash table from the name.
12692 */
12693
12694static struct var **
Eric Andersen62483552001-07-10 06:09:16 +000012695hashvar(const char *p)
12696{
Eric Andersencb57d552001-06-28 07:25:16 +000012697 unsigned int hashval;
12698
12699 hashval = ((unsigned char) *p) << 4;
12700 while (*p && *p != '=')
12701 hashval += (unsigned char) *p++;
12702 return &vartab[hashval % VTABSIZE];
12703}
12704
12705
12706
12707/*
12708 * Returns true if the two strings specify the same varable. The first
12709 * variable name is terminated by '='; the second may be terminated by
12710 * either '=' or '\0'.
12711 */
12712
12713static int
Eric Andersen62483552001-07-10 06:09:16 +000012714varequal(const char *p, const char *q)
12715{
Eric Andersencb57d552001-06-28 07:25:16 +000012716 while (*p == *q++) {
12717 if (*p++ == '=')
12718 return 1;
12719 }
12720 if (*p == '=' && *(q - 1) == '\0')
12721 return 1;
12722 return 0;
12723}
12724
12725static void
12726showvars(const char *myprefix, int mask, int xor)
12727{
12728 struct var **vpp;
12729 struct var *vp;
12730 const char *sep = myprefix == nullstr ? myprefix : spcstr;
12731
12732 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12733 for (vp = *vpp ; vp ; vp = vp->next) {
12734 if ((vp->flags & mask) ^ xor) {
12735 char *p;
12736 int len;
12737
12738 p = strchr(vp->text, '=') + 1;
12739 len = p - vp->text;
12740 p = single_quote(p);
12741
Eric Andersen62483552001-07-10 06:09:16 +000012742 printf("%s%s%.*s%s\n", myprefix, sep, len,
12743 vp->text, p);
Eric Andersencb57d552001-06-28 07:25:16 +000012744 stunalloc(p);
12745 }
12746 }
12747 }
12748}
12749
12750static struct var **
12751findvar(struct var **vpp, const char *name)
12752{
12753 for (; *vpp; vpp = &(*vpp)->next) {
12754 if (varequal((*vpp)->text, name)) {
12755 break;
12756 }
12757 }
12758 return vpp;
12759}
12760
12761/*
12762 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
12763 * This file contains code for the times builtin.
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000012764 * $Id: ash.c,v 1.25 2001/09/11 01:14:02 mjn3 Exp $
Eric Andersencb57d552001-06-28 07:25:16 +000012765 */
12766static int timescmd (int argc, char **argv)
12767{
12768 struct tms buf;
12769 long int clk_tck = sysconf(_SC_CLK_TCK);
12770
12771 times(&buf);
12772 printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
12773 (int) (buf.tms_utime / clk_tck / 60),
12774 ((double) buf.tms_utime) / clk_tck,
12775 (int) (buf.tms_stime / clk_tck / 60),
12776 ((double) buf.tms_stime) / clk_tck,
12777 (int) (buf.tms_cutime / clk_tck / 60),
12778 ((double) buf.tms_cutime) / clk_tck,
12779 (int) (buf.tms_cstime / clk_tck / 60),
12780 ((double) buf.tms_cstime) / clk_tck);
12781 return 0;
12782}
12783
Eric Andersen74bcd162001-07-30 21:41:37 +000012784#ifdef ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012785/* The let builtin. */
12786int letcmd(int argc, char **argv)
Eric Andersen74bcd162001-07-30 21:41:37 +000012787{
Eric Andersen34506362001-08-02 05:02:46 +000012788 int errcode;
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012789 long result=0;
12790 if (argc == 2) {
12791 char *tmp, *expression, p[13];
12792 expression = strchr(argv[1], '=');
12793 if (!expression) {
Eric Andersen34506362001-08-02 05:02:46 +000012794 /* Cannot use 'error()' here, or the return code
12795 * will be incorrect */
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012796 out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]);
12797 return 0;
Eric Andersen74bcd162001-07-30 21:41:37 +000012798 }
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012799 *expression = '\0';
12800 tmp = ++expression;
Eric Andersen34506362001-08-02 05:02:46 +000012801 result = arith(tmp, &errcode);
12802 if (errcode < 0) {
12803 /* Cannot use 'error()' here, or the return code
12804 * will be incorrect */
12805 out2fmt("sh: let: ");
12806 if(errcode == -2)
12807 out2fmt("divide by zero");
12808 else
12809 out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression);
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012810 return 0;
12811 }
12812 snprintf(p, 12, "%ld", result);
12813 setvar(argv[1], savestr(p), 0);
12814 } else if (argc >= 3)
12815 synerror("invalid operand");
12816 return !result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012817}
12818#endif
12819
12820
12821
Eric Andersendf82f612001-06-28 07:46:40 +000012822/*-
12823 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012824 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012825 *
12826 * This code is derived from software contributed to Berkeley by
12827 * Kenneth Almquist.
12828 *
12829 * Redistribution and use in source and binary forms, with or without
12830 * modification, are permitted provided that the following conditions
12831 * are met:
12832 * 1. Redistributions of source code must retain the above copyright
12833 * notice, this list of conditions and the following disclaimer.
12834 * 2. Redistributions in binary form must reproduce the above copyright
12835 * notice, this list of conditions and the following disclaimer in the
12836 * documentation and/or other materials provided with the distribution.
12837 *
Eric Andersen2870d962001-07-02 17:27:21 +000012838 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
12839 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
Eric Andersendf82f612001-06-28 07:46:40 +000012840 *
12841 * 4. Neither the name of the University nor the names of its contributors
12842 * may be used to endorse or promote products derived from this software
12843 * without specific prior written permission.
12844 *
12845 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12846 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12847 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12848 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12849 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12850 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12851 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12852 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12853 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12854 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12855 * SUCH DAMAGE.
12856 */