source: MondoRescue/branches/stable/mindi-busybox/shell/msh.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 99.4 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Minix shell port for busybox
4 *
5 * This version of the Minix shell was adapted for use in busybox
6 * by Erik Andersen <andersen@codepoet.org>
7 *
8 * - backtick expansion did not work properly
9 *   Jonas Holmberg <jonas.holmberg@axis.com>
10 *   Robert Schwebel <r.schwebel@pengutronix.de>
11 *   Erik Andersen <andersen@codepoet.org>
12 *
13 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
14 */
15
16#include <sys/times.h>
17#include <setjmp.h>
18
19#ifdef STANDALONE
20# ifndef _GNU_SOURCE
21#  define _GNU_SOURCE
22# endif
23# include <sys/types.h>
24# include <sys/stat.h>
25# include <sys/wait.h>
26# include <signal.h>
27# include <stdio.h>
28# include <stdlib.h>
29# include <unistd.h>
30# include <string.h>
31# include <errno.h>
32# include <dirent.h>
33# include <fcntl.h>
34# include <ctype.h>
35# include <assert.h>
36# define bb_dev_null "/dev/null"
37# define DEFAULT_SHELL "/proc/self/exe"
38# define CONFIG_BUSYBOX_EXEC_PATH "/proc/self/exe"
39# define bb_banner "busybox standalone"
40# define ENABLE_FEATURE_SH_STANDALONE 0
41# define bb_msg_memory_exhausted "memory exhausted"
42# define xmalloc(size) malloc(size)
43# define msh_main(argc,argv) main(argc,argv)
44# define safe_read(fd,buf,count) read(fd,buf,count)
45# define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1])
46# define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1])
47# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
48static char *find_applet_by_name(const char *applet)
49{
50    return NULL;
51}
52static char *utoa_to_buf(unsigned n, char *buf, unsigned buflen)
53{
54    unsigned i, out, res;
55    assert(sizeof(unsigned) == 4);
56    if (buflen) {
57        out = 0;
58        for (i = 1000000000; i; i /= 10) {
59            res = n / i;
60            if (res || out || i == 1) {
61                if (!--buflen) break;
62                out++;
63                n -= res*i;
64                *buf++ = '0' + res;
65            }
66        }
67    }
68    return buf;
69}
70static char *itoa_to_buf(int n, char *buf, unsigned buflen)
71{
72    if (buflen && n < 0) {
73        n = -n;
74        *buf++ = '-';
75        buflen--;
76    }
77    return utoa_to_buf((unsigned)n, buf, buflen);
78}
79static char local_buf[12];
80static char *itoa(int n)
81{
82    *(itoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0';
83    return local_buf;
84}
85#else
86# include "busybox.h"
87extern char **environ;
88#endif
89
90/*#define MSHDEBUG 1*/
91
92#ifdef MSHDEBUG
93int mshdbg = MSHDEBUG;
94
95#define DBGPRINTF(x)    if (mshdbg>0) printf x
96#define DBGPRINTF0(x)   if (mshdbg>0) printf x
97#define DBGPRINTF1(x)   if (mshdbg>1) printf x
98#define DBGPRINTF2(x)   if (mshdbg>2) printf x
99#define DBGPRINTF3(x)   if (mshdbg>3) printf x
100#define DBGPRINTF4(x)   if (mshdbg>4) printf x
101#define DBGPRINTF5(x)   if (mshdbg>5) printf x
102#define DBGPRINTF6(x)   if (mshdbg>6) printf x
103#define DBGPRINTF7(x)   if (mshdbg>7) printf x
104#define DBGPRINTF8(x)   if (mshdbg>8) printf x
105#define DBGPRINTF9(x)   if (mshdbg>9) printf x
106
107int mshdbg_rc = 0;
108
109#define RCPRINTF(x) if (mshdbg_rc) printf x
110
111#else
112
113#define DBGPRINTF(x)
114#define DBGPRINTF0(x) ((void)0)
115#define DBGPRINTF1(x) ((void)0)
116#define DBGPRINTF2(x) ((void)0)
117#define DBGPRINTF3(x) ((void)0)
118#define DBGPRINTF4(x) ((void)0)
119#define DBGPRINTF5(x) ((void)0)
120#define DBGPRINTF6(x) ((void)0)
121#define DBGPRINTF7(x) ((void)0)
122#define DBGPRINTF8(x) ((void)0)
123#define DBGPRINTF9(x) ((void)0)
124
125#define RCPRINTF(x) ((void)0)
126
127#endif                          /* MSHDEBUG */
128
129
130#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
131# define DEFAULT_ROOT_PROMPT "\\u:\\w> "
132# define DEFAULT_USER_PROMPT "\\u:\\w$ "
133#else
134# define DEFAULT_ROOT_PROMPT "# "
135# define DEFAULT_USER_PROMPT "$ "
136#endif
137
138
139/* -------- sh.h -------- */
140/*
141 * shell
142 */
143
144#define LINELIM   2100
145#define NPUSH     8             /* limit to input nesting */
146
147#undef NOFILE
148#define NOFILE    20            /* Number of open files */
149#define NUFILE    10            /* Number of user-accessible files */
150#define FDBASE    10            /* First file usable by Shell */
151
152/*
153 * values returned by wait
154 */
155#define WAITSIG(s)  ((s) & 0177)
156#define WAITVAL(s)  (((s) >> 8) & 0377)
157#define WAITCORE(s) (((s) & 0200) != 0)
158
159/*
160 * library and system definitions
161 */
162typedef void xint;              /* base type of jmp_buf, for not broken compilers */
163
164/*
165 * shell components
166 */
167#define NOBLOCK ((struct op *)NULL)
168#define NOWORD  ((char *)NULL)
169#define NOWORDS ((char **)NULL)
170#define NOPIPE  ((int *)NULL)
171
172/*
173 * redirection
174 */
175struct ioword {
176    short io_unit;              /* unit affected */
177    short io_flag;              /* action (below) */
178    char *io_name;              /* file name */
179};
180
181#define IOREAD   1              /* < */
182#define IOHERE   2              /* << (here file) */
183#define IOWRITE  4              /* > */
184#define IOCAT    8              /* >> */
185#define IOXHERE  16             /* ${}, ` in << */
186#define IODUP    32             /* >&digit */
187#define IOCLOSE  64             /* >&- */
188
189#define IODEFAULT (-1)          /* token for default IO unit */
190
191
192/*
193 * Description of a command or an operation on commands.
194 * Might eventually use a union.
195 */
196struct op {
197    int type;                   /* operation type, see below */
198    char **words;               /* arguments to a command */
199    struct ioword **ioact;      /* IO actions (eg, < > >>) */
200    struct op *left;
201    struct op *right;
202    char *str;                  /* identifier for case and for */
203};
204
205#define TCOM    1               /* command */
206#define TPAREN  2               /* (c-list) */
207#define TPIPE   3               /* a | b */
208#define TLIST   4               /* a [&;] b */
209#define TOR     5               /* || */
210#define TAND    6               /* && */
211#define TFOR    7
212#define TDO     8
213#define TCASE   9
214#define TIF     10
215#define TWHILE  11
216#define TUNTIL  12
217#define TELIF   13
218#define TPAT    14              /* pattern in case */
219#define TBRACE  15              /* {c-list} */
220#define TASYNC  16              /* c & */
221/* Added to support "." file expansion */
222#define TDOT    17
223
224/* Strings for names to make debug easier */
225#ifdef MSHDEBUG
226static const char *const T_CMD_NAMES[] = {
227    "PLACEHOLDER",
228    "TCOM",
229    "TPAREN",
230    "TPIPE",
231    "TLIST",
232    "TOR",
233    "TAND",
234    "TFOR",
235    "TDO",
236    "TCASE",
237    "TIF",
238    "TWHILE",
239    "TUNTIL",
240    "TELIF",
241    "TPAT",
242    "TBRACE",
243    "TASYNC",
244    "TDOT",
245};
246#endif
247
248/*
249 * actions determining the environment of a process
250 */
251#define FEXEC    1      /* execute without forking */
252
253#define AREASIZE (90000)
254
255/*
256 * flags to control evaluation of words
257 */
258#define DOSUB    1      /* interpret $, `, and quotes */
259#define DOBLANK  2      /* perform blank interpretation */
260#define DOGLOB   4      /* interpret [?* */
261#define DOKEY    8      /* move words with `=' to 2nd arg. list */
262#define DOTRIM   16     /* trim resulting string */
263
264#define DOALL    (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
265
266
267struct brkcon {
268    jmp_buf brkpt;
269    struct brkcon *nextlev;
270};
271
272
273/*
274 * flags:
275 * -e: quit on error
276 * -k: look for name=value everywhere on command line
277 * -n: no execution
278 * -t: exit after reading and executing one command
279 * -v: echo as read
280 * -x: trace
281 * -u: unset variables net diagnostic
282 */
283static char flags['z' - 'a' + 1] ALIGN1;
284/* this looks weird, but is OK ... we index FLAG with 'a'...'z' */
285#define FLAG (flags - 'a')
286
287/* moved to G: static char *trap[_NSIG + 1]; */
288/* moved to G: static char ourtrap[_NSIG + 1]; */
289static int trapset;             /* trap pending */
290
291static int yynerrs;             /* yacc */
292
293/* moved to G: static char line[LINELIM]; */
294
295#if ENABLE_FEATURE_EDITING
296static char *current_prompt;
297static line_input_t *line_input_state;
298#endif
299
300
301/*
302 * other functions
303 */
304static const char *rexecve(char *c, char **v, char **envp);
305static char *evalstr(char *cp, int f);
306static char *putn(int n);
307static char *unquote(char *as);
308static int rlookup(char *n);
309static struct wdblock *glob(char *cp, struct wdblock *wb);
310static int my_getc(int ec);
311static int subgetc(char ec, int quoted);
312static char **makenv(int all, struct wdblock *wb);
313static char **eval(char **ap, int f);
314static int setstatus(int s);
315static int waitfor(int lastpid, int canintr);
316
317static void onintr(int s);      /* SIGINT handler */
318
319static int newenv(int f);
320static void quitenv(void);
321static void next(int f);
322static void setdash(void);
323static void onecommand(void);
324static void runtrap(int i);
325
326
327/* -------- area stuff -------- */
328
329#define REGSIZE   sizeof(struct region)
330#define GROWBY    (256)
331/* #define SHRINKBY (64) */
332#undef  SHRINKBY
333#define FREE      (32767)
334#define BUSY      (0)
335#define ALIGN     (sizeof(int)-1)
336
337
338struct region {
339    struct region *next;
340    int area;
341};
342
343
344/* -------- grammar stuff -------- */
345typedef union {
346    char *cp;
347    char **wp;
348    int i;
349    struct op *o;
350} YYSTYPE;
351
352#define WORD    256
353#define LOGAND  257
354#define LOGOR   258
355#define BREAK   259
356#define IF      260
357#define THEN    261
358#define ELSE    262
359#define ELIF    263
360#define FI      264
361#define CASE    265
362#define ESAC    266
363#define FOR     267
364#define WHILE   268
365#define UNTIL   269
366#define DO      270
367#define DONE    271
368#define IN      272
369/* Added for "." file expansion */
370#define DOT     273
371
372#define YYERRCODE 300
373
374/* flags to yylex */
375#define CONTIN 01     /* skip new lines to complete command */
376
377static struct op *pipeline(int cf);
378static struct op *andor(void);
379static struct op *c_list(void);
380static int synio(int cf);
381static void musthave(int c, int cf);
382static struct op *simple(void);
383static struct op *nested(int type, int mark);
384static struct op *command(int cf);
385static struct op *dogroup(int onlydone);
386static struct op *thenpart(void);
387static struct op *elsepart(void);
388static struct op *caselist(void);
389static struct op *casepart(void);
390static char **pattern(void);
391static char **wordlist(void);
392static struct op *list(struct op *t1, struct op *t2);
393static struct op *block(int type, struct op *t1, struct op *t2, char **wp);
394static struct op *newtp(void);
395static struct op *namelist(struct op *t);
396static char **copyw(void);
397static void word(char *cp);
398static struct ioword **copyio(void);
399static struct ioword *io(int u, int f, char *cp);
400static int yylex(int cf);
401static int collect(int c, int c1);
402static int dual(int c);
403static void diag(int ec);
404static char *tree(unsigned size);
405
406/* -------- var.h -------- */
407
408struct var {
409    char *value;
410    char *name;
411    struct var *next;
412    char status;
413};
414
415#define COPYV   1               /* flag to setval, suggesting copy */
416#define RONLY   01              /* variable is read-only */
417#define EXPORT  02              /* variable is to be exported */
418#define GETCELL 04              /* name & value space was got with getcell */
419
420static int yyparse(void);
421
422static int execute(struct op *t, int *pin, int *pout, int act);
423
424
425#define AFID_NOBUF  (~0)
426#define AFID_ID     0
427
428
429/* -------- io.h -------- */
430/* io buffer */
431struct iobuf {
432    unsigned id;            /* buffer id */
433    char buf[512];          /* buffer */
434    char *bufp;             /* pointer into buffer */
435    char *ebufp;            /* pointer to end of buffer */
436};
437
438/* possible arguments to an IO function */
439struct ioarg {
440    const char *aword;
441    char **awordlist;
442    int afile;              /* file descriptor */
443    unsigned afid;          /* buffer id */
444    long afpos;             /* file position */
445    struct iobuf *afbuf;    /* buffer for this file */
446};
447
448/* an input generator's state */
449struct io {
450    int (*iofn) (struct ioarg *, struct io *);
451    struct ioarg *argp;
452    int peekc;
453    char prev;              /* previous character read by readc() */
454    char nlcount;           /* for `'s */
455    char xchar;             /* for `'s */
456    char task;              /* reason for pushed IO */
457};
458
459#define XOTHER  0               /* none of the below */
460#define XDOLL   1               /* expanding ${} */
461#define XGRAVE  2               /* expanding `'s */
462#define XIO 3               /* file IO */
463
464/* in substitution */
465#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL)
466
467static struct ioarg temparg = { 0, 0, 0, AFID_NOBUF, 0 };   /* temporary for PUSHIO */
468/* moved to G: static struct ioarg ioargstack[NPUSH]; */
469static struct io iostack[NPUSH];
470/* moved to G: static struct iobuf sharedbuf = { AFID_NOBUF }; */
471/* moved to G: static struct iobuf mainbuf = { AFID_NOBUF }; */
472static unsigned bufid = AFID_ID;    /* buffer id counter */
473
474#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen)))
475
476
477/*
478 * input generators for IO structure
479 */
480static int nlchar(struct ioarg *ap);
481static int strchar(struct ioarg *ap);
482static int qstrchar(struct ioarg *ap);
483static int filechar(struct ioarg *ap);
484static int herechar(struct ioarg *ap);
485static int linechar(struct ioarg *ap);
486static int gravechar(struct ioarg *ap, struct io *iop);
487static int qgravechar(struct ioarg *ap, struct io *iop);
488static int dolchar(struct ioarg *ap);
489static int wdchar(struct ioarg *ap);
490static void scraphere(void);
491static void freehere(int area);
492static void gethere(void);
493static void markhere(char *s, struct ioword *iop);
494static int herein(char *hname, int xdoll);
495static int run(struct ioarg *argp, int (*f) (struct ioarg *));
496
497
498static int eofc(void);
499static int readc(void);
500static void unget(int c);
501static void ioecho(char c);
502
503
504/*
505 * IO control
506 */
507static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
508#define PUSHIO(what,arg,gen) ((temparg.what = (arg)), pushio(&temparg,(gen)))
509static int remap(int fd);
510static int openpipe(int *pv);
511static void closepipe(int *pv);
512static struct io *setbase(struct io *ip);
513
514/* -------- word.h -------- */
515
516#define NSTART  16              /* default number of words to allow for initially */
517
518struct wdblock {
519    short w_bsize;
520    short w_nword;
521    /* bounds are arbitrary */
522    char *w_words[1];
523};
524
525static struct wdblock *addword(char *wd, struct wdblock *wb);
526static struct wdblock *newword(int nw);
527static char **getwords(struct wdblock *wb);
528
529/* -------- misc stuff -------- */
530
531static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp);
532static int iosetup(struct ioword *iop, int pipein, int pipeout);
533static void brkset(struct brkcon *bc);
534static int dolabel(struct op *t);
535static int dohelp(struct op *t);
536static int dochdir(struct op *t);
537static int doshift(struct op *t);
538static int dologin(struct op *t);
539static int doumask(struct op *t);
540static int doexec(struct op *t);
541static int dodot(struct op *t);
542static int dowait(struct op *t);
543static int doread(struct op *t);
544static int doeval(struct op *t);
545static int dotrap(struct op *t);
546static int getsig(char *s);
547static void setsig(int n, sighandler_t f);
548static int getn(char *as);
549static int dobreak(struct op *t);
550static int docontinue(struct op *t);
551static int brkcontin(char *cp, int val);
552static int doexit(struct op *t);
553static int doexport(struct op *t);
554static int doreadonly(struct op *t);
555static void rdexp(char **wp, void (*f) (struct var *), int key);
556static void badid(char *s);
557static int doset(struct op *t);
558static void varput(char *s, int out);
559static int dotimes(struct op *t);
560static int expand(const char *cp, struct wdblock **wbp, int f);
561static char *blank(int f);
562static int dollar(int quoted);
563static int grave(int quoted);
564static void globname(char *we, char *pp);
565static char *generate(char *start1, char *end1, char *middle, char *end);
566static int anyspcl(struct wdblock *wb);
567static int xstrcmp(char *p1, char *p2);
568static void glob0(char *a0, unsigned a1, int a2,
569                  int (*a3) (char *, char *));
570static void readhere(char **name, char *s, int ec);
571static int xxchar(struct ioarg *ap);
572
573struct here {
574    char *h_tag;
575    int h_dosub;
576    struct ioword *h_iop;
577    struct here *h_next;
578};
579
580static const char *const signame[] = {
581    "Signal 0",
582    "Hangup",
583    NULL,  /* interrupt */
584    "Quit",
585    "Illegal instruction",
586    "Trace/BPT trap",
587    "Abort",
588    "Bus error",
589    "Floating Point Exception",
590    "Killed",
591    "SIGUSR1",
592    "SIGSEGV",
593    "SIGUSR2",
594    NULL,  /* broken pipe */
595    "Alarm clock",
596    "Terminated"
597};
598
599
600struct res {
601    const char *r_name;
602    int r_val;
603};
604static const struct res restab[] = {
605    { "for"  , FOR    },
606    { "case" , CASE   },
607    { "esac" , ESAC   },
608    { "while", WHILE  },
609    { "do"   , DO     },
610    { "done" , DONE   },
611    { "if"   , IF     },
612    { "in"   , IN     },
613    { "then" , THEN   },
614    { "else" , ELSE   },
615    { "elif" , ELIF   },
616    { "until", UNTIL  },
617    { "fi"   , FI     },
618    { ";;"   , BREAK  },
619    { "||"   , LOGOR  },
620    { "&&"   , LOGAND },
621    { "{"    , '{'    },
622    { "}"    , '}'    },
623    { "."    , DOT    },
624    { NULL   , 0      },
625};
626
627struct builtincmd {
628    const char *name;
629    int (*builtinfunc)(struct op *t);
630};
631static const struct builtincmd builtincmds[] = {
632    { "."       , dodot      },
633    { ":"       , dolabel    },
634    { "break"   , dobreak    },
635    { "cd"      , dochdir    },
636    { "continue", docontinue },
637    { "eval"    , doeval     },
638    { "exec"    , doexec     },
639    { "exit"    , doexit     },
640    { "export"  , doexport   },
641    { "help"    , dohelp     },
642    { "login"   , dologin    },
643    { "newgrp"  , dologin    },
644    { "read"    , doread     },
645    { "readonly", doreadonly },
646    { "set"     , doset      },
647    { "shift"   , doshift    },
648    { "times"   , dotimes    },
649    { "trap"    , dotrap     },
650    { "umask"   , doumask    },
651    { "wait"    , dowait     },
652    { NULL      , NULL       },
653};
654
655static struct op *scantree(struct op *);
656static struct op *dowholefile(int, int);
657
658
659/* Globals */
660static char **dolv;
661static int dolc;
662static int exstat;
663static char gflg;
664static int interactive;         /* Is this an interactive shell */
665static int execflg;
666static int multiline;           /* \n changed to ; */
667static struct op *outtree;      /* result from parser */
668static xint *failpt;
669static xint *errpt;
670static struct brkcon *brklist;
671static int isbreak;
672static struct wdblock *wdlist;
673static struct wdblock *iolist;
674
675#ifdef MSHDEBUG
676static struct var *mshdbg_var;
677#endif
678static struct var *vlist;       /* dictionary */
679static struct var *homedir;     /* home directory */
680static struct var *prompt;      /* main prompt */
681static struct var *cprompt;     /* continuation prompt */
682static struct var *path;        /* search path for commands */
683static struct var *shell;       /* shell to interpret command files */
684static struct var *ifs;         /* field separators */
685
686static int areanum;                     /* current allocation area */
687static int intr;                        /* interrupt pending */
688static int inparse;
689static char *null = (char*)"";          /* null value for variable */
690static int heedint = 1;                 /* heed interrupt signals */
691static void (*qflag)(int) = SIG_IGN;
692static int startl;
693static int peeksym;
694static int nlseen;
695static int iounit = IODEFAULT;
696static YYSTYPE yylval;
697static char *elinep; /* done in main(): = line + sizeof(line) - 5 */
698
699static struct here *inhere;     /* list of hear docs while parsing */
700static struct here *acthere;    /* list of active here documents */
701static struct region *areabot;  /* bottom of area */
702static struct region *areatop;  /* top of area */
703static struct region *areanxt;  /* starting point of scan */
704static void *brktop;
705static void *brkaddr;
706
707/*
708 * parsing & execution environment
709 */
710struct env {
711    char *linep;
712    struct io *iobase;
713    struct io *iop;
714    xint *errpt;        /* void * */
715    int iofd;
716    struct env *oenv;
717};
718
719static struct env e = {
720    NULL /* set to line in main() */, /* linep:  char ptr */
721    iostack,                /* iobase:  struct io ptr */
722    iostack - 1,            /* iop:  struct io ptr */
723    (xint *) NULL,          /* errpt:  void ptr for errors? */
724    FDBASE,                 /* iofd:  file desc  */
725    (struct env *) NULL     /* oenv:  struct env ptr */
726};
727
728
729struct globals {
730    char ourtrap[_NSIG + 1];
731    char *trap[_NSIG + 1];
732    struct iobuf sharedbuf; /* in main(): set to { AFID_NOBUF } */
733    struct iobuf mainbuf; /* in main(): set to { AFID_NOBUF } */
734    struct ioarg ioargstack[NPUSH];
735    char filechar_cmdbuf[BUFSIZ];
736    char line[LINELIM];
737    char child_cmd[LINELIM];
738};
739
740#define G (*ptr_to_globals)
741#define ourtrap         (G.ourtrap        )
742#define trap            (G.trap           )
743#define sharedbuf       (G.sharedbuf      )
744#define mainbuf         (G.mainbuf        )
745#define ioargstack      (G.ioargstack     )
746#define filechar_cmdbuf (G.filechar_cmdbuf)
747#define line            (G.line           )
748#define child_cmd       (G.child_cmd      )
749
750
751#ifdef MSHDEBUG
752void print_t(struct op *t)
753{
754    DBGPRINTF(("T: t=%p, type %s, words=%p, IOword=%p\n", t,
755              T_CMD_NAMES[t->type], t->words, t->ioact));
756
757    if (t->words) {
758        DBGPRINTF(("T: W1: %s", t->words[0]));
759    }
760}
761
762void print_tree(struct op *head)
763{
764    if (head == NULL) {
765        DBGPRINTF(("PRINT_TREE: no tree\n"));
766        return;
767    }
768
769    DBGPRINTF(("NODE: %p,  left %p, right %p\n", head, head->left,
770               head->right));
771
772    if (head->left)
773        print_tree(head->left);
774
775    if (head->right)
776        print_tree(head->right);
777}
778#endif /* MSHDEBUG */
779
780
781/*
782 * IO functions
783 */
784static void prs(const char *s)
785{
786    if (*s)
787        write(2, s, strlen(s));
788}
789
790static void prn(unsigned u)
791{
792    prs(itoa(u));
793}
794
795static void echo(char **wp)
796{
797    int i;
798
799    prs("+");
800    for (i = 0; wp[i]; i++) {
801        if (i)
802            prs(" ");
803        prs(wp[i]);
804    }
805    prs("\n");
806}
807
808static void closef(int i)
809{
810    if (i > 2)
811        close(i);
812}
813
814static void closeall(void)
815{
816    int u;
817
818    for (u = NUFILE; u < NOFILE;)
819        close(u++);
820}
821
822
823/* fail but return to process next command */
824static void fail(void) ATTRIBUTE_NORETURN;
825static void fail(void)
826{
827    longjmp(failpt, 1);
828    /* NOTREACHED */
829}
830
831/* abort shell (or fail in subshell) */
832static void leave(void) ATTRIBUTE_NORETURN;
833static void leave(void)
834{
835    DBGPRINTF(("LEAVE: leave called!\n"));
836
837    if (execflg)
838        fail();
839    scraphere();
840    freehere(1);
841    runtrap(0);
842    _exit(exstat);
843    /* NOTREACHED */
844}
845
846static void warn(const char *s)
847{
848    if (*s) {
849        prs(s);
850        exstat = -1;
851    }
852    prs("\n");
853    if (FLAG['e'])
854        leave();
855}
856
857static void err(const char *s)
858{
859    warn(s);
860    if (FLAG['n'])
861        return;
862    if (!interactive)
863        leave();
864    if (e.errpt)
865        longjmp(e.errpt, 1);
866    closeall();
867    e.iop = e.iobase = iostack;
868}
869
870
871/* -------- area.c -------- */
872
873/*
874 * All memory between (char *)areabot and (char *)(areatop+1) is
875 * exclusively administered by the area management routines.
876 * It is assumed that sbrk() and brk() manipulate the high end.
877 */
878
879#define sbrk(X) ({ \
880    void * __q = (void *)-1; \
881    if (brkaddr + (int)(X) < brktop) { \
882        __q = brkaddr; \
883        brkaddr += (int)(X); \
884    } \
885    __q; \
886})
887
888static void initarea(void)
889{
890    brkaddr = xmalloc(AREASIZE);
891    brktop = brkaddr + AREASIZE;
892
893    while ((long) sbrk(0) & ALIGN)
894        sbrk(1);
895    areabot = (struct region *) sbrk(REGSIZE);
896
897    areabot->next = areabot;
898    areabot->area = BUSY;
899    areatop = areabot;
900    areanxt = areabot;
901}
902
903static char *getcell(unsigned nbytes)
904{
905    int nregio;
906    struct region *p, *q;
907    int i;
908
909    if (nbytes == 0) {
910        puts("getcell(0)");
911        abort();
912    }
913    /* silly and defeats the algorithm */
914    /*
915     * round upwards and add administration area
916     */
917    nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
918    p = areanxt;
919    for (;;) {
920        if (p->area > areanum) {
921            /*
922             * merge free cells
923             */
924            while ((q = p->next)->area > areanum && q != areanxt)
925                p->next = q->next;
926            /*
927             * exit loop if cell big enough
928             */
929            if (q >= p + nregio)
930                goto found;
931        }
932        p = p->next;
933        if (p == areanxt)
934            break;
935    }
936    i = nregio >= GROWBY ? nregio : GROWBY;
937    p = (struct region *) sbrk(i * REGSIZE);
938    if (p == (struct region *) -1)
939        return NULL;
940    p--;
941    if (p != areatop) {
942        puts("not contig");
943        abort();                /* allocated areas are contiguous */
944    }
945    q = p + i;
946    p->next = q;
947    p->area = FREE;
948    q->next = areabot;
949    q->area = BUSY;
950    areatop = q;
951 found:
952    /*
953     * we found a FREE area big enough, pointed to by 'p', and up to 'q'
954     */
955    areanxt = p + nregio;
956    if (areanxt < q) {
957        /*
958         * split into requested area and rest
959         */
960        if (areanxt + 1 > q) {
961            puts("OOM");
962            abort();            /* insufficient space left for admin */
963        }
964        areanxt->next = q;
965        areanxt->area = FREE;
966        p->next = areanxt;
967    }
968    p->area = areanum;
969    return (char *) (p + 1);
970}
971
972static void freecell(char *cp)
973{
974    struct region *p;
975
976    p = (struct region *) cp;
977    if (p != NULL) {
978        p--;
979        if (p < areanxt)
980            areanxt = p;
981        p->area = FREE;
982    }
983}
984#define DELETE(obj) freecell((char *)obj)
985
986static void freearea(int a)
987{
988    struct region *p, *top;
989
990    top = areatop;
991    for (p = areabot; p != top; p = p->next)
992        if (p->area >= a)
993            p->area = FREE;
994}
995
996static void setarea(char *cp, int a)
997{
998    struct region *p;
999
1000    p = (struct region *) cp;
1001    if (p != NULL)
1002        (p - 1)->area = a;
1003}
1004
1005static int getarea(char *cp)
1006{
1007    return ((struct region *) cp - 1)->area;
1008}
1009
1010static void garbage(void)
1011{
1012    struct region *p, *q, *top;
1013
1014    top = areatop;
1015    for (p = areabot; p != top; p = p->next) {
1016        if (p->area > areanum) {
1017            while ((q = p->next)->area > areanum)
1018                p->next = q->next;
1019            areanxt = p;
1020        }
1021    }
1022#ifdef SHRINKBY
1023    if (areatop >= q + SHRINKBY && q->area > areanum) {
1024        brk((char *) (q + 1));
1025        q->next = areabot;
1026        q->area = BUSY;
1027        areatop = q;
1028    }
1029#endif
1030}
1031
1032static char *space(int n)
1033{
1034    char *cp;
1035
1036    cp = getcell(n);
1037    if (cp == NULL)
1038        err("out of string space");
1039    return cp;
1040}
1041
1042static char *strsave(const char *s, int a)
1043{
1044    char *cp;
1045
1046    cp = space(strlen(s) + 1);
1047    if (cp == NULL) {
1048// FIXME: I highly doubt this is good.
1049        return (char*)"";
1050    }
1051    setarea(cp, a);
1052    strcpy(cp, s);
1053    return cp;
1054}
1055
1056
1057/* -------- var.c -------- */
1058
1059static int eqname(const char *n1, const char *n2)
1060{
1061    for (; *n1 != '=' && *n1 != '\0'; n1++)
1062        if (*n2++ != *n1)
1063            return 0;
1064    return *n2 == '\0' || *n2 == '=';
1065}
1066
1067static const char *findeq(const char *cp)
1068{
1069    while (*cp != '\0' && *cp != '=')
1070        cp++;
1071    return cp;
1072}
1073
1074/*
1075 * Find the given name in the dictionary
1076 * and return its value.  If the name was
1077 * not previously there, enter it now and
1078 * return a null value.
1079 */
1080static struct var *lookup(const char *n)
1081{
1082// FIXME: dirty hack
1083    static struct var dummy;
1084
1085    struct var *vp;
1086    const char *cp;
1087    char *xp;
1088    int c;
1089
1090    if (isdigit(*n)) {
1091        dummy.name = (char*)n;
1092        for (c = 0; isdigit(*n) && c < 1000; n++)
1093            c = c * 10 + *n - '0';
1094        dummy.status = RONLY;
1095        dummy.value = (c <= dolc ? dolv[c] : null);
1096        return &dummy;
1097    }
1098
1099    for (vp = vlist; vp; vp = vp->next)
1100        if (eqname(vp->name, n))
1101            return vp;
1102
1103    cp = findeq(n);
1104    vp = (struct var *) space(sizeof(*vp));
1105    if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
1106        dummy.name = dummy.value = (char*)"";
1107        return &dummy;
1108    }
1109
1110    xp = vp->name;
1111    while ((*xp = *n++) != '\0' && *xp != '=')
1112        xp++;
1113    *xp++ = '=';
1114    *xp = '\0';
1115    setarea((char *) vp, 0);
1116    setarea((char *) vp->name, 0);
1117    vp->value = null;
1118    vp->next = vlist;
1119    vp->status = GETCELL;
1120    vlist = vp;
1121    return vp;
1122}
1123
1124/*
1125 * if name is not NULL, it must be
1126 * a prefix of the space `val',
1127 * and end with `='.
1128 * this is all so that exporting
1129 * values is reasonably painless.
1130 */
1131static void nameval(struct var *vp, const char *val, const char *name)
1132{
1133    const char *cp;
1134    char *xp;
1135    int fl;
1136
1137    if (vp->status & RONLY) {
1138        xp = vp->name;
1139        while (*xp && *xp != '=')
1140            putc(*xp++, stderr);
1141        err(" is read-only");
1142        return;
1143    }
1144    fl = 0;
1145    if (name == NULL) {
1146        xp = space(strlen(vp->name) + strlen(val) + 2);
1147        if (xp == NULL)
1148            return;
1149        /* make string: name=value */
1150        setarea(xp, 0);
1151        name = xp;
1152        cp = vp->name;
1153        while ((*xp = *cp++) != '\0' && *xp != '=')
1154            xp++;
1155        *xp++ = '=';
1156        strcpy(xp, val);
1157        val = xp;
1158        fl = GETCELL;
1159    }
1160    if (vp->status & GETCELL)
1161        freecell(vp->name);     /* form new string `name=value' */
1162    vp->name = (char*)name;
1163    vp->value = (char*)val;
1164    vp->status |= fl;
1165}
1166
1167/*
1168 * give variable at `vp' the value `val'.
1169 */
1170static void setval(struct var *vp, const char *val)
1171{
1172    nameval(vp, val, NULL);
1173}
1174
1175static void export(struct var *vp)
1176{
1177    vp->status |= EXPORT;
1178}
1179
1180static void ronly(struct var *vp)
1181{
1182    if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
1183        vp->status |= RONLY;
1184}
1185
1186static int isassign(const char *s)
1187{
1188    unsigned char c;
1189    DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
1190
1191    c = *s;
1192    /* no isalpha() - we shouldn't use locale */
1193    /* c | 0x20 - lowercase (Latin) letters */
1194    if (c != '_' && (unsigned)((c|0x20) - 'a') > 25)
1195        /* not letter */
1196        return 0;
1197
1198    while (1) {
1199        c = *++s;
1200        if (c == '=')
1201            return 1;
1202        if (c == '\0')
1203            return 0;
1204        if (c != '_'
1205         && (unsigned)(c - '0') > 9  /* not number */
1206         && (unsigned)((c|0x20) - 'a') > 25 /* not letter */
1207        ) {
1208            return 0;
1209        }
1210    }
1211}
1212
1213static int assign(const char *s, int cf)
1214{
1215    const char *cp;
1216    struct var *vp;
1217
1218    DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
1219
1220    if (!isalpha(*s) && *s != '_')
1221        return 0;
1222    for (cp = s; *cp != '='; cp++)
1223        if (*cp == '\0' || (!isalnum(*cp) && *cp != '_'))
1224            return 0;
1225    vp = lookup(s);
1226    nameval(vp, ++cp, cf == COPYV ? NULL : s);
1227    if (cf != COPYV)
1228        vp->status &= ~GETCELL;
1229    return 1;
1230}
1231
1232static int checkname(char *cp)
1233{
1234    DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
1235
1236    if (!isalpha(*cp++) && *(cp - 1) != '_')
1237        return 0;
1238    while (*cp)
1239        if (!isalnum(*cp++) && *(cp - 1) != '_')
1240            return 0;
1241    return 1;
1242}
1243
1244static void putvlist(int f, int out)
1245{
1246    struct var *vp;
1247
1248    for (vp = vlist; vp; vp = vp->next) {
1249        if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
1250            if (vp->status & EXPORT)
1251                write(out, "export ", 7);
1252            if (vp->status & RONLY)
1253                write(out, "readonly ", 9);
1254            write(out, vp->name, (int) (findeq(vp->name) - vp->name));
1255            write(out, "\n", 1);
1256        }
1257    }
1258}
1259
1260
1261/*
1262 * trap handling
1263 */
1264static void sig(int i)
1265{
1266    trapset = i;
1267    signal(i, sig);
1268}
1269
1270static void runtrap(int i)
1271{
1272    char *trapstr;
1273
1274    trapstr = trap[i];
1275    if (trapstr == NULL)
1276        return;
1277
1278    if (i == 0)
1279        trap[i] = NULL;
1280
1281    RUN(aword, trapstr, nlchar);
1282}
1283
1284
1285static void setdash(void)
1286{
1287    char *cp;
1288    int c;
1289    char m['z' - 'a' + 1];
1290
1291    cp = m;
1292    for (c = 'a'; c <= 'z'; c++)
1293        if (FLAG[c])
1294            *cp++ = c;
1295    *cp = '\0';
1296    setval(lookup("-"), m);
1297}
1298
1299static int newfile(char *s)
1300{
1301    int f;
1302
1303    DBGPRINTF7(("NEWFILE: opening %s\n", s));
1304
1305    f = 0;
1306    if (NOT_LONE_DASH(s)) {
1307        DBGPRINTF(("NEWFILE: s is %s\n", s));
1308        f = open(s, O_RDONLY);
1309        if (f < 0) {
1310            prs(s);
1311            err(": cannot open");
1312            return 1;
1313        }
1314    }
1315
1316    next(remap(f));
1317    return 0;
1318}
1319
1320
1321struct op *scantree(struct op *head)
1322{
1323    struct op *dotnode;
1324
1325    if (head == NULL)
1326        return NULL;
1327
1328    if (head->left != NULL) {
1329        dotnode = scantree(head->left);
1330        if (dotnode)
1331            return dotnode;
1332    }
1333
1334    if (head->right != NULL) {
1335        dotnode = scantree(head->right);
1336        if (dotnode)
1337            return dotnode;
1338    }
1339
1340    if (head->words == NULL)
1341        return NULL;
1342
1343    DBGPRINTF5(("SCANTREE: checking node %p\n", head));
1344
1345    if ((head->type != TDOT) && LONE_CHAR(head->words[0], '.')) {
1346        DBGPRINTF5(("SCANTREE: dot found in node %p\n", head));
1347        return head;
1348    }
1349
1350    return NULL;
1351}
1352
1353
1354static void onecommand(void)
1355{
1356    int i;
1357    jmp_buf m1;
1358
1359    DBGPRINTF(("ONECOMMAND: enter, outtree=%p\n", outtree));
1360
1361    while (e.oenv)
1362        quitenv();
1363
1364    areanum = 1;
1365    freehere(areanum);
1366    freearea(areanum);
1367    garbage();
1368    wdlist = 0;
1369    iolist = 0;
1370    e.errpt = 0;
1371    e.linep = line;
1372    yynerrs = 0;
1373    multiline = 0;
1374    inparse = 1;
1375    intr = 0;
1376    execflg = 0;
1377
1378    failpt = m1;
1379    setjmp(failpt);     /* Bruce Evans' fix */
1380    failpt = m1;
1381    if (setjmp(failpt) || yyparse() || intr) {
1382        DBGPRINTF(("ONECOMMAND: this is not good.\n"));
1383
1384        while (e.oenv)
1385            quitenv();
1386        scraphere();
1387        if (!interactive && intr)
1388            leave();
1389        inparse = 0;
1390        intr = 0;
1391        return;
1392    }
1393
1394    inparse = 0;
1395    brklist = 0;
1396    intr = 0;
1397    execflg = 0;
1398
1399    if (!FLAG['n']) {
1400        DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=%p\n",
1401                   outtree));
1402        execute(outtree, NOPIPE, NOPIPE, 0);
1403    }
1404
1405    if (!interactive && intr) {
1406        execflg = 0;
1407        leave();
1408    }
1409
1410    i = trapset;
1411    if (i != 0) {
1412        trapset = 0;
1413        runtrap(i);
1414    }
1415}
1416
1417static int newenv(int f)
1418{
1419    struct env *ep;
1420
1421    DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f));
1422
1423    if (f) {
1424        quitenv();
1425        return 1;
1426    }
1427
1428    ep = (struct env *) space(sizeof(*ep));
1429    if (ep == NULL) {
1430        while (e.oenv)
1431            quitenv();
1432        fail();
1433    }
1434    *ep = e;
1435    e.oenv = ep;
1436    e.errpt = errpt;
1437
1438    return 0;
1439}
1440
1441static void quitenv(void)
1442{
1443    struct env *ep;
1444    int fd;
1445
1446    DBGPRINTF(("QUITENV: e.oenv=%p\n", e.oenv));
1447
1448    ep = e.oenv;
1449    if (ep != NULL) {
1450        fd = e.iofd;
1451        e = *ep;
1452        /* should close `'d files */
1453        DELETE(ep);
1454        while (--fd >= e.iofd)
1455            close(fd);
1456    }
1457}
1458
1459/*
1460 * Is character c in s?
1461 */
1462static int any(int c, const char *s)
1463{
1464    while (*s)
1465        if (*s++ == c)
1466            return 1;
1467    return 0;
1468}
1469
1470/*
1471 * Is any character from s1 in s2?
1472 */
1473static int anys(const char *s1, const char *s2)
1474{
1475    while (*s1)
1476        if (any(*s1++, s2))
1477            return 1;
1478    return 0;
1479}
1480
1481static char *putn(int n)
1482{
1483    return itoa(n);
1484}
1485
1486static void next(int f)
1487{
1488    PUSHIO(afile, f, filechar);
1489}
1490
1491static void onintr(int s)                   /* ANSI C requires a parameter */
1492{
1493    signal(SIGINT, onintr);
1494    intr = 1;
1495    if (interactive) {
1496        if (inparse) {
1497            prs("\n");
1498            fail();
1499        }
1500    } else if (heedint) {
1501        execflg = 0;
1502        leave();
1503    }
1504}
1505
1506
1507/* -------- gmatch.c -------- */
1508/*
1509 * int gmatch(string, pattern)
1510 * char *string, *pattern;
1511 *
1512 * Match a pattern as in sh(1).
1513 */
1514
1515#define CMASK   0377
1516#define QUOTE   0200
1517#define QMASK   (CMASK & ~QUOTE)
1518#define NOT '!'                 /* might use ^ */
1519
1520static const char *cclass(const char *p, int sub)
1521{
1522    int c, d, not, found;
1523
1524    not = (*p == NOT);
1525    if (not != 0)
1526        p++;
1527    found = not;
1528    do {
1529        if (*p == '\0')
1530            return NULL;
1531        c = *p & CMASK;
1532        if (p[1] == '-' && p[2] != ']') {
1533            d = p[2] & CMASK;
1534            p++;
1535        } else
1536            d = c;
1537        if (c == sub || (c <= sub && sub <= d))
1538            found = !not;
1539    } while (*++p != ']');
1540    return found ? p + 1 : NULL;
1541}
1542
1543static int gmatch(const char *s, const char *p)
1544{
1545    int sc, pc;
1546
1547    if (s == NULL || p == NULL)
1548        return 0;
1549
1550    while ((pc = *p++ & CMASK) != '\0') {
1551        sc = *s++ & QMASK;
1552        switch (pc) {
1553        case '[':
1554            p = cclass(p, sc);
1555            if (p == NULL)
1556                return 0;
1557            break;
1558
1559        case '?':
1560            if (sc == 0)
1561                return 0;
1562            break;
1563
1564        case '*':
1565            s--;
1566            do {
1567                if (*p == '\0' || gmatch(s, p))
1568                    return 1;
1569            } while (*s++ != '\0');
1570            return 0;
1571
1572        default:
1573            if (sc != (pc & ~QUOTE))
1574                return 0;
1575        }
1576    }
1577    return *s == '\0';
1578}
1579
1580
1581/* -------- csyn.c -------- */
1582/*
1583 * shell: syntax (C version)
1584 */
1585
1586static void yyerror(const char *s) ATTRIBUTE_NORETURN;
1587static void yyerror(const char *s)
1588{
1589    yynerrs++;
1590    if (interactive && e.iop <= iostack) {
1591        multiline = 0;
1592        while (eofc() == 0 && yylex(0) != '\n');
1593    }
1594    err(s);
1595    fail();
1596}
1597
1598static void zzerr(void) ATTRIBUTE_NORETURN;
1599static void zzerr(void)
1600{
1601    yyerror("syntax error");
1602}
1603
1604int yyparse(void)
1605{
1606    DBGPRINTF7(("YYPARSE: enter...\n"));
1607
1608    startl = 1;
1609    peeksym = 0;
1610    yynerrs = 0;
1611    outtree = c_list();
1612    musthave('\n', 0);
1613    return (yynerrs != 0);
1614}
1615
1616static struct op *pipeline(int cf)
1617{
1618    struct op *t, *p;
1619    int c;
1620
1621    DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf));
1622
1623    t = command(cf);
1624
1625    DBGPRINTF9(("PIPELINE: t=%p\n", t));
1626
1627    if (t != NULL) {
1628        while ((c = yylex(0)) == '|') {
1629            p = command(CONTIN);
1630            if (p == NULL) {
1631                DBGPRINTF8(("PIPELINE: error!\n"));
1632                zzerr();
1633            }
1634
1635            if (t->type != TPAREN && t->type != TCOM) {
1636                /* shell statement */
1637                t = block(TPAREN, t, NOBLOCK, NOWORDS);
1638            }
1639
1640            t = block(TPIPE, t, p, NOWORDS);
1641        }
1642        peeksym = c;
1643    }
1644
1645    DBGPRINTF7(("PIPELINE: returning t=%p\n", t));
1646    return t;
1647}
1648
1649static struct op *andor(void)
1650{
1651    struct op *t, *p;
1652    int c;
1653
1654    DBGPRINTF7(("ANDOR: enter...\n"));
1655
1656    t = pipeline(0);
1657
1658    DBGPRINTF9(("ANDOR: t=%p\n", t));
1659
1660    if (t != NULL) {
1661        while ((c = yylex(0)) == LOGAND || c == LOGOR) {
1662            p = pipeline(CONTIN);
1663            if (p == NULL) {
1664                DBGPRINTF8(("ANDOR: error!\n"));
1665                zzerr();
1666            }
1667
1668            t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS);
1669        }                       /* WHILE */
1670
1671        peeksym = c;
1672    }
1673
1674    DBGPRINTF7(("ANDOR: returning t=%p\n", t));
1675    return t;
1676}
1677
1678static struct op *c_list(void)
1679{
1680    struct op *t, *p;
1681    int c;
1682
1683    DBGPRINTF7(("C_LIST: enter...\n"));
1684
1685    t = andor();
1686
1687    if (t != NULL) {
1688        peeksym = yylex(0);
1689        if (peeksym == '&')
1690            t = block(TASYNC, t, NOBLOCK, NOWORDS);
1691
1692        while ((c = yylex(0)) == ';' || c == '&'
1693               || (multiline && c == '\n')) {
1694
1695            p = andor();
1696            if (p== NULL)
1697                return t;
1698
1699            peeksym = yylex(0);
1700            if (peeksym == '&')
1701                p = block(TASYNC, p, NOBLOCK, NOWORDS);
1702
1703            t = list(t, p);
1704        }                       /* WHILE */
1705
1706        peeksym = c;
1707    }
1708    /* IF */
1709    DBGPRINTF7(("C_LIST: returning t=%p\n", t));
1710    return t;
1711}
1712
1713static int synio(int cf)
1714{
1715    struct ioword *iop;
1716    int i;
1717    int c;
1718
1719    DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf));
1720
1721    c = yylex(cf);
1722    if (c != '<' && c != '>') {
1723        peeksym = c;
1724        return 0;
1725    }
1726
1727    i = yylval.i;
1728    musthave(WORD, 0);
1729    iop = io(iounit, i, yylval.cp);
1730    iounit = IODEFAULT;
1731
1732    if (i & IOHERE)
1733        markhere(yylval.cp, iop);
1734
1735    DBGPRINTF7(("SYNIO: returning 1\n"));
1736    return 1;
1737}
1738
1739static void musthave(int c, int cf)
1740{
1741    peeksym = yylex(cf);
1742    if (peeksym != c) {
1743        DBGPRINTF7(("MUSTHAVE: error!\n"));
1744        zzerr();
1745    }
1746
1747    peeksym = 0;
1748}
1749
1750static struct op *simple(void)
1751{
1752    struct op *t;
1753
1754    t = NULL;
1755    for (;;) {
1756        switch (peeksym = yylex(0)) {
1757        case '<':
1758        case '>':
1759            (void) synio(0);
1760            break;
1761
1762        case WORD:
1763            if (t == NULL) {
1764                t = newtp();
1765                t->type = TCOM;
1766            }
1767            peeksym = 0;
1768            word(yylval.cp);
1769            break;
1770
1771        default:
1772            return t;
1773        }
1774    }
1775}
1776
1777static struct op *nested(int type, int mark)
1778{
1779    struct op *t;
1780
1781    DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark));
1782
1783    multiline++;
1784    t = c_list();
1785    musthave(mark, 0);
1786    multiline--;
1787    return block(type, t, NOBLOCK, NOWORDS);
1788}
1789
1790static struct op *command(int cf)
1791{
1792    struct op *t;
1793    struct wdblock *iosave;
1794    int c;
1795
1796    DBGPRINTF(("COMMAND: enter, cf=%d\n", cf));
1797
1798    iosave = iolist;
1799    iolist = NULL;
1800
1801    if (multiline)
1802        cf |= CONTIN;
1803
1804    while (synio(cf))
1805        cf = 0;
1806
1807    c = yylex(cf);
1808
1809    switch (c) {
1810    default:
1811        peeksym = c;
1812        t = simple();
1813        if (t == NULL) {
1814            if (iolist == NULL)
1815                return NULL;
1816            t = newtp();
1817            t->type = TCOM;
1818        }
1819        break;
1820
1821    case '(':
1822        t = nested(TPAREN, ')');
1823        break;
1824
1825    case '{':
1826        t = nested(TBRACE, '}');
1827        break;
1828
1829    case FOR:
1830        t = newtp();
1831        t->type = TFOR;
1832        musthave(WORD, 0);
1833        startl = 1;
1834        t->str = yylval.cp;
1835        multiline++;
1836        t->words = wordlist();
1837        c = yylex(0);
1838        if (c != '\n' && c != ';')
1839            peeksym = c;
1840        t->left = dogroup(0);
1841        multiline--;
1842        break;
1843
1844    case WHILE:
1845    case UNTIL:
1846        multiline++;
1847        t = newtp();
1848        t->type = c == WHILE ? TWHILE : TUNTIL;
1849        t->left = c_list();
1850        t->right = dogroup(1);
1851        t->words = NULL;
1852        multiline--;
1853        break;
1854
1855    case CASE:
1856        t = newtp();
1857        t->type = TCASE;
1858        musthave(WORD, 0);
1859        t->str = yylval.cp;
1860        startl++;
1861        multiline++;
1862        musthave(IN, CONTIN);
1863        startl++;
1864
1865        t->left = caselist();
1866
1867        musthave(ESAC, 0);
1868        multiline--;
1869        break;
1870
1871    case IF:
1872        multiline++;
1873        t = newtp();
1874        t->type = TIF;
1875        t->left = c_list();
1876        t->right = thenpart();
1877        musthave(FI, 0);
1878        multiline--;
1879        break;
1880
1881    case DOT:
1882        t = newtp();
1883        t->type = TDOT;
1884
1885        musthave(WORD, 0);      /* gets name of file */
1886        DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp));
1887
1888        word(yylval.cp);        /* add word to wdlist */
1889        word(NOWORD);           /* terminate  wdlist */
1890        t->words = copyw();     /* dup wdlist */
1891        break;
1892
1893    }
1894
1895    while (synio(0));
1896
1897    t = namelist(t);
1898    iolist = iosave;
1899
1900    DBGPRINTF(("COMMAND: returning %p\n", t));
1901
1902    return t;
1903}
1904
1905static struct op *dowholefile(int type, int mark)
1906{
1907    struct op *t;
1908
1909    DBGPRINTF(("DOWHOLEFILE: enter, type=%d, mark=%d\n", type, mark));
1910
1911    multiline++;
1912    t = c_list();
1913    multiline--;
1914    t = block(type, t, NOBLOCK, NOWORDS);
1915    DBGPRINTF(("DOWHOLEFILE: return t=%p\n", t));
1916    return t;
1917}
1918
1919static struct op *dogroup(int onlydone)
1920{
1921    int c;
1922    struct op *mylist;
1923
1924    c = yylex(CONTIN);
1925    if (c == DONE && onlydone)
1926        return NULL;
1927    if (c != DO)
1928        zzerr();
1929    mylist = c_list();
1930    musthave(DONE, 0);
1931    return mylist;
1932}
1933
1934static struct op *thenpart(void)
1935{
1936    int c;
1937    struct op *t;
1938
1939    c = yylex(0);
1940    if (c != THEN) {
1941        peeksym = c;
1942        return NULL;
1943    }
1944    t = newtp();
1945    t->type = 0;
1946    t->left = c_list();
1947    if (t->left == NULL)
1948        zzerr();
1949    t->right = elsepart();
1950    return t;
1951}
1952
1953static struct op *elsepart(void)
1954{
1955    int c;
1956    struct op *t;
1957
1958    switch (c = yylex(0)) {
1959    case ELSE:
1960        t = c_list();
1961        if (t == NULL)
1962            zzerr();
1963        return t;
1964
1965    case ELIF:
1966        t = newtp();
1967        t->type = TELIF;
1968        t->left = c_list();
1969        t->right = thenpart();
1970        return t;
1971
1972    default:
1973        peeksym = c;
1974        return NULL;
1975    }
1976}
1977
1978static struct op *caselist(void)
1979{
1980    struct op *t;
1981
1982    t = NULL;
1983    while ((peeksym = yylex(CONTIN)) != ESAC) {
1984        DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym));
1985        t = list(t, casepart());
1986    }
1987
1988    DBGPRINTF(("CASELIST, returning t=%p\n", t));
1989    return t;
1990}
1991
1992static struct op *casepart(void)
1993{
1994    struct op *t;
1995
1996    DBGPRINTF7(("CASEPART: enter...\n"));
1997
1998    t = newtp();
1999    t->type = TPAT;
2000    t->words = pattern();
2001    musthave(')', 0);
2002    t->left = c_list();
2003    peeksym = yylex(CONTIN);
2004    if (peeksym != ESAC)
2005        musthave(BREAK, CONTIN);
2006
2007    DBGPRINTF7(("CASEPART: made newtp(TPAT, t=%p)\n", t));
2008
2009    return t;
2010}
2011
2012static char **pattern(void)
2013{
2014    int c, cf;
2015
2016    cf = CONTIN;
2017    do {
2018        musthave(WORD, cf);
2019        word(yylval.cp);
2020        cf = 0;
2021        c = yylex(0);
2022    } while (c == '|');
2023    peeksym = c;
2024    word(NOWORD);
2025
2026    return copyw();
2027}
2028
2029static char **wordlist(void)
2030{
2031    int c;
2032
2033    c = yylex(0);
2034    if (c != IN) {
2035        peeksym = c;
2036        return NULL;
2037    }
2038    startl = 0;
2039    while ((c = yylex(0)) == WORD)
2040        word(yylval.cp);
2041    word(NOWORD);
2042    peeksym = c;
2043    return copyw();
2044}
2045
2046/*
2047 * supporting functions
2048 */
2049static struct op *list(struct op *t1, struct op *t2)
2050{
2051    DBGPRINTF7(("LIST: enter, t1=%p, t2=%p\n", t1, t2));
2052
2053    if (t1 == NULL)
2054        return t2;
2055    if (t2 == NULL)
2056        return t1;
2057
2058    return block(TLIST, t1, t2, NOWORDS);
2059}
2060
2061static struct op *block(int type, struct op *t1, struct op *t2, char **wp)
2062{
2063    struct op *t;
2064
2065    DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type]));
2066
2067    t = newtp();
2068    t->type = type;
2069    t->left = t1;
2070    t->right = t2;
2071    t->words = wp;
2072
2073    DBGPRINTF7(("BLOCK: inserted %p between %p and %p\n", t, t1,
2074                t2));
2075
2076    return t;
2077}
2078
2079/* See if given string is a shell multiline (FOR, IF, etc) */
2080static int rlookup(char *n)
2081{
2082    const struct res *rp;
2083
2084    DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n));
2085
2086    for (rp = restab; rp->r_name; rp++)
2087        if (strcmp(rp->r_name, n) == 0) {
2088            DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val));
2089            return rp->r_val;   /* Return numeric code for shell multiline */
2090        }
2091
2092    DBGPRINTF7(("RLOOKUP: NO match, returning 0\n"));
2093    return 0;                   /* Not a shell multiline */
2094}
2095
2096static struct op *newtp(void)
2097{
2098    struct op *t;
2099
2100    t = (struct op *) tree(sizeof(*t));
2101    t->type = 0;
2102    t->words = NULL;
2103    t->ioact = NULL;
2104    t->left = NULL;
2105    t->right = NULL;
2106    t->str = NULL;
2107
2108    DBGPRINTF3(("NEWTP: allocated %p\n", t));
2109
2110    return t;
2111}
2112
2113static struct op *namelist(struct op *t)
2114{
2115    DBGPRINTF7(("NAMELIST: enter, t=%p, type %s, iolist=%p\n", t,
2116                T_CMD_NAMES[t->type], iolist));
2117
2118    if (iolist) {
2119        iolist = addword((char *) NULL, iolist);
2120        t->ioact = copyio();
2121    } else
2122        t->ioact = NULL;
2123
2124    if (t->type != TCOM) {
2125        if (t->type != TPAREN && t->ioact != NULL) {
2126            t = block(TPAREN, t, NOBLOCK, NOWORDS);
2127            t->ioact = t->left->ioact;
2128            t->left->ioact = NULL;
2129        }
2130        return t;
2131    }
2132
2133    word(NOWORD);
2134    t->words = copyw();
2135
2136    return t;
2137}
2138
2139static char **copyw(void)
2140{
2141    char **wd;
2142
2143    wd = getwords(wdlist);
2144    wdlist = 0;
2145    return wd;
2146}
2147
2148static void word(char *cp)
2149{
2150    wdlist = addword(cp, wdlist);
2151}
2152
2153static struct ioword **copyio(void)
2154{
2155    struct ioword **iop;
2156
2157    iop = (struct ioword **) getwords(iolist);
2158    iolist = 0;
2159    return iop;
2160}
2161
2162static struct ioword *io(int u, int f, char *cp)
2163{
2164    struct ioword *iop;
2165
2166    iop = (struct ioword *) tree(sizeof(*iop));
2167    iop->io_unit = u;
2168    iop->io_flag = f;
2169    iop->io_name = cp;
2170    iolist = addword((char *) iop, iolist);
2171    return iop;
2172}
2173
2174static int yylex(int cf)
2175{
2176    int c, c1;
2177    int atstart;
2178
2179    c = peeksym;
2180    if (c > 0) {
2181        peeksym = 0;
2182        if (c == '\n')
2183            startl = 1;
2184        return c;
2185    }
2186
2187    nlseen = 0;
2188    atstart = startl;
2189    startl = 0;
2190    yylval.i = 0;
2191    e.linep = line;
2192
2193/* MALAMO */
2194    line[LINELIM - 1] = '\0';
2195
2196 loop:
2197    while ((c = my_getc(0)) == ' ' || c == '\t')    /* Skip whitespace */
2198        ;
2199
2200    switch (c) {
2201    default:
2202        if (any(c, "0123456789")) {
2203            c1 = my_getc(0);
2204            unget(c1);
2205            if (c1 == '<' || c1 == '>') {
2206                iounit = c - '0';
2207                goto loop;
2208            }
2209            *e.linep++ = c;
2210            c = c1;
2211        }
2212        break;
2213
2214    case '#':                   /* Comment, skip to next newline or End-of-string */
2215        while ((c = my_getc(0)) != '\0' && c != '\n');
2216        unget(c);
2217        goto loop;
2218
2219    case 0:
2220        DBGPRINTF5(("YYLEX: return 0, c=%d\n", c));
2221        return c;
2222
2223    case '$':
2224        DBGPRINTF9(("YYLEX: found $\n"));
2225        *e.linep++ = c;
2226        c = my_getc(0);
2227        if (c == '{') {
2228            c = collect(c, '}');
2229            if (c != '\0')
2230                return c;
2231            goto pack;
2232        }
2233        break;
2234
2235    case '`':
2236    case '\'':
2237    case '"':
2238        c = collect(c, c);
2239        if (c != '\0')
2240            return c;
2241        goto pack;
2242
2243    case '|':
2244    case '&':
2245    case ';':
2246        startl = 1;
2247        /* If more chars process them, else return NULL char */
2248        c1 = dual(c);
2249        if (c1 != '\0')
2250            return c1;
2251        return c;
2252
2253    case '^':
2254        startl = 1;
2255        return '|';
2256    case '>':
2257    case '<':
2258        diag(c);
2259        return c;
2260
2261    case '\n':
2262        nlseen++;
2263        gethere();
2264        startl = 1;
2265        if (multiline || cf & CONTIN) {
2266            if (interactive && e.iop <= iostack) {
2267#if ENABLE_FEATURE_EDITING
2268                current_prompt = cprompt->value;
2269#else
2270                prs(cprompt->value);
2271#endif
2272            }
2273            if (cf & CONTIN)
2274                goto loop;
2275        }
2276        return c;
2277
2278    case '(':
2279    case ')':
2280        startl = 1;
2281        return c;
2282    }
2283
2284    unget(c);
2285
2286 pack:
2287    while ((c = my_getc(0)) != '\0' && !any(c, "`$ '\"\t;&<>()|^\n")) {
2288        if (e.linep >= elinep)
2289            err("word too long");
2290        else
2291            *e.linep++ = c;
2292    };
2293
2294    unget(c);
2295
2296    if (any(c, "\"'`$"))
2297        goto loop;
2298
2299    *e.linep++ = '\0';
2300
2301    if (atstart) {
2302        c = rlookup(line);
2303        if (c != 0) {
2304            startl = 1;
2305            return c;
2306        }
2307    }
2308
2309    yylval.cp = strsave(line, areanum);
2310    return WORD;
2311}
2312
2313
2314static int collect(int c, int c1)
2315{
2316    char s[2];
2317
2318    DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1));
2319
2320    *e.linep++ = c;
2321    while ((c = my_getc(c1)) != c1) {
2322        if (c == 0) {
2323            unget(c);
2324            s[0] = c1;
2325            s[1] = 0;
2326            prs("no closing ");
2327            yyerror(s);
2328            return YYERRCODE;
2329        }
2330        if (interactive && c == '\n' && e.iop <= iostack) {
2331#if ENABLE_FEATURE_EDITING
2332            current_prompt = cprompt->value;
2333#else
2334            prs(cprompt->value);
2335#endif
2336        }
2337        *e.linep++ = c;
2338    }
2339
2340    *e.linep++ = c;
2341
2342    DBGPRINTF8(("COLLECT: return 0, line is %s\n", line));
2343
2344    return 0;
2345}
2346
2347/* "multiline commands" helper func */
2348/* see if next 2 chars form a shell multiline */
2349static int dual(int c)
2350{
2351    char s[3];
2352    char *cp = s;
2353
2354    DBGPRINTF8(("DUAL: enter, c=%d\n", c));
2355
2356    *cp++ = c;              /* c is the given "peek" char */
2357    *cp++ = my_getc(0);     /* get next char of input */
2358    *cp = '\0';             /* add EOS marker */
2359
2360    c = rlookup(s);         /* see if 2 chars form a shell multiline */
2361    if (c == 0)
2362        unget(*--cp);   /* String is not a shell multiline, put peek char back */
2363
2364    return c;               /* String is multiline, return numeric multiline (restab) code */
2365}
2366
2367static void diag(int ec)
2368{
2369    int c;
2370
2371    DBGPRINTF8(("DIAG: enter, ec=%d\n", ec));
2372
2373    c = my_getc(0);
2374    if (c == '>' || c == '<') {
2375        if (c != ec)
2376            zzerr();
2377        yylval.i = (ec == '>' ? IOWRITE | IOCAT : IOHERE);
2378        c = my_getc(0);
2379    } else
2380        yylval.i = (ec == '>' ? IOWRITE : IOREAD);
2381    if (c != '&' || yylval.i == IOHERE)
2382        unget(c);
2383    else
2384        yylval.i |= IODUP;
2385}
2386
2387static char *tree(unsigned size)
2388{
2389    char *t;
2390
2391    t = getcell(size);
2392    if (t == NULL) {
2393        DBGPRINTF2(("TREE: getcell(%d) failed!\n", size));
2394        prs("command line too complicated\n");
2395        fail();
2396        /* NOTREACHED */
2397    }
2398    return t;
2399}
2400
2401
2402/* VARARGS1 */
2403/* ARGSUSED */
2404
2405/* -------- exec.c -------- */
2406
2407static struct op **find1case(struct op *t, const char *w)
2408{
2409    struct op *t1;
2410    struct op **tp;
2411    char **wp;
2412    char *cp;
2413
2414    if (t == NULL) {
2415        DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n"));
2416        return NULL;
2417    }
2418
2419    DBGPRINTF3(("FIND1CASE: enter, t->type=%d (%s)\n", t->type,
2420                T_CMD_NAMES[t->type]));
2421
2422    if (t->type == TLIST) {
2423        tp = find1case(t->left, w);
2424        if (tp != NULL) {
2425            DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=%p\n", tp));
2426            return tp;
2427        }
2428        t1 = t->right;          /* TPAT */
2429    } else
2430        t1 = t;
2431
2432    for (wp = t1->words; *wp;) {
2433        cp = evalstr(*wp++, DOSUB);
2434        if (cp && gmatch(w, cp)) {
2435            DBGPRINTF3(("FIND1CASE: returning &t1->left= %p.\n",
2436                        &t1->left));
2437            return &t1->left;
2438        }
2439    }
2440
2441    DBGPRINTF(("FIND1CASE: returning NULL\n"));
2442    return NULL;
2443}
2444
2445static struct op *findcase(struct op *t, const char *w)
2446{
2447    struct op **tp;
2448
2449    tp = find1case(t, w);
2450    return tp != NULL ? *tp : NULL;
2451}
2452
2453/*
2454 * execute tree
2455 */
2456
2457static int execute(struct op *t, int *pin, int *pout, int act)
2458{
2459    struct op *t1;
2460    volatile int i, rv, a;
2461    const char *cp;
2462    char **wp, **wp2;
2463    struct var *vp;
2464    struct op *outtree_save;
2465    struct brkcon bc;
2466
2467#if __GNUC__
2468    /* Avoid longjmp clobbering */
2469    (void) &wp;
2470#endif
2471
2472    if (t == NULL) {
2473        DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n"));
2474        return 0;
2475    }
2476
2477    DBGPRINTF(("EXECUTE: t=%p, t->type=%d (%s), t->words is %s\n", t,
2478               t->type, T_CMD_NAMES[t->type],
2479               ((t->words == NULL) ? "NULL" : t->words[0])));
2480
2481    rv = 0;
2482    a = areanum++;
2483    wp = (wp2 = t->words) != NULL
2484        ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY)
2485        : NULL;
2486
2487    switch (t->type) {
2488    case TDOT:
2489        DBGPRINTF3(("EXECUTE: TDOT\n"));
2490
2491        outtree_save = outtree;
2492
2493        newfile(evalstr(t->words[0], DOALL));
2494
2495        t->left = dowholefile(TLIST, 0);
2496        t->right = NULL;
2497
2498        outtree = outtree_save;
2499
2500        if (t->left)
2501            rv = execute(t->left, pin, pout, 0);
2502        if (t->right)
2503            rv = execute(t->right, pin, pout, 0);
2504        break;
2505
2506    case TPAREN:
2507        rv = execute(t->left, pin, pout, 0);
2508        break;
2509
2510    case TCOM:
2511        rv = forkexec(t, pin, pout, act, wp);
2512        break;
2513
2514    case TPIPE:
2515        {
2516            int pv[2];
2517
2518            rv = openpipe(pv);
2519            if (rv < 0)
2520                break;
2521            pv[0] = remap(pv[0]);
2522            pv[1] = remap(pv[1]);
2523            (void) execute(t->left, pin, pv, 0);
2524            rv = execute(t->right, pv, pout, 0);
2525        }
2526        break;
2527
2528    case TLIST:
2529        (void) execute(t->left, pin, pout, 0);
2530        rv = execute(t->right, pin, pout, 0);
2531        break;
2532
2533    case TASYNC:
2534        {
2535            int hinteractive = interactive;
2536
2537            DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n"));
2538
2539            i = vfork();
2540            if (i == 0) { /* child */
2541                signal(SIGINT, SIG_IGN);
2542                signal(SIGQUIT, SIG_IGN);
2543                if (interactive)
2544                    signal(SIGTERM, SIG_DFL);
2545                interactive = 0;
2546                if (pin == NULL) {
2547                    close(0);
2548                    xopen(bb_dev_null, O_RDONLY);
2549                }
2550                _exit(execute(t->left, pin, pout, FEXEC));
2551            }
2552            interactive = hinteractive;
2553            if (i != -1) {
2554                setval(lookup("!"), putn(i));
2555                if (pin != NULL)
2556                    closepipe(pin);
2557                if (interactive) {
2558                    prs(putn(i));
2559                    prs("\n");
2560                }
2561            } else
2562                rv = -1;
2563            setstatus(rv);
2564        }
2565        break;
2566
2567    case TOR:
2568    case TAND:
2569        rv = execute(t->left, pin, pout, 0);
2570        t1 = t->right;
2571        if (t1 != NULL && (rv == 0) == (t->type == TAND))
2572            rv = execute(t1, pin, pout, 0);
2573        break;
2574
2575    case TFOR:
2576        if (wp == NULL) {
2577            wp = dolv + 1;
2578            i = dolc;
2579            if (i < 0)
2580                i = 0;
2581        } else {
2582            i = -1;
2583            while (*wp++ != NULL);
2584        }
2585        vp = lookup(t->str);
2586        while (setjmp(bc.brkpt))
2587            if (isbreak)
2588                goto broken;
2589        brkset(&bc);
2590        for (t1 = t->left; i-- && *wp != NULL;) {
2591            setval(vp, *wp++);
2592            rv = execute(t1, pin, pout, 0);
2593        }
2594        brklist = brklist->nextlev;
2595        break;
2596
2597    case TWHILE:
2598    case TUNTIL:
2599        while (setjmp(bc.brkpt))
2600            if (isbreak)
2601                goto broken;
2602        brkset(&bc);
2603        t1 = t->left;
2604        while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
2605            rv = execute(t->right, pin, pout, 0);
2606        brklist = brklist->nextlev;
2607        break;
2608
2609    case TIF:
2610    case TELIF:
2611        if (t->right != NULL) {
2612            rv = !execute(t->left, pin, pout, 0) ?
2613                execute(t->right->left, pin, pout, 0) :
2614                execute(t->right->right, pin, pout, 0);
2615        }
2616        break;
2617
2618    case TCASE:
2619        cp = evalstr(t->str, DOSUB | DOTRIM);
2620        if (cp == NULL)
2621            cp = "";
2622
2623        DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n",
2624                    ((t->str == NULL) ? "NULL" : t->str),
2625                    ((cp == NULL) ? "NULL" : cp)));
2626
2627        t1 = findcase(t->left, cp);
2628        if (t1 != NULL) {
2629            DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=%p, t1=%p)...\n", t, t1));
2630            rv = execute(t1, pin, pout, 0);
2631            DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=%p, t1=%p)...\n", t, t1));
2632        }
2633        break;
2634
2635    case TBRACE:
2636/*
2637        iopp = t->ioact;
2638        if (i)
2639            while (*iopp)
2640                if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
2641                    rv = -1;
2642                    break;
2643                }
2644*/
2645        if (rv >= 0) {
2646            t1 = t->left;
2647            if (t1) {
2648                rv = execute(t1, pin, pout, 0);
2649            }
2650        }
2651        break;
2652
2653    };
2654
2655 broken:
2656    t->words = wp2;
2657    isbreak = 0;
2658    freehere(areanum);
2659    freearea(areanum);
2660    areanum = a;
2661    if (interactive && intr) {
2662        closeall();
2663        fail();
2664    }
2665
2666    i = trapset;
2667    if (i != 0) {
2668        trapset = 0;
2669        runtrap(i);
2670    }
2671
2672    DBGPRINTF(("EXECUTE: returning from t=%p, rv=%d\n", t, rv));
2673    return rv;
2674}
2675
2676typedef int (*builtin_func_ptr)(struct op *);
2677
2678static builtin_func_ptr inbuilt(const char *s)
2679{
2680    const struct builtincmd *bp;
2681
2682    for (bp = builtincmds; bp->name; bp++)
2683        if (strcmp(bp->name, s) == 0)
2684            return bp->builtinfunc;
2685    return NULL;
2686}
2687
2688static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp)
2689{
2690    pid_t newpid;
2691    int i, rv;
2692    builtin_func_ptr shcom = NULL;
2693    int f;
2694    const char *cp = NULL;
2695    struct ioword **iopp;
2696    int resetsig;
2697    char **owp;
2698    int forked = 0;
2699
2700    int *hpin = pin;
2701    int *hpout = pout;
2702    char *hwp;
2703    int hinteractive;
2704    int hintr;
2705    struct brkcon *hbrklist;
2706    int hexecflg;
2707
2708#if __GNUC__
2709    /* Avoid longjmp clobbering */
2710    (void) &pin;
2711    (void) &pout;
2712    (void) &wp;
2713    (void) &shcom;
2714    (void) &cp;
2715    (void) &resetsig;
2716    (void) &owp;
2717#endif
2718
2719    DBGPRINTF(("FORKEXEC: t=%p, pin %p, pout %p, act %d\n", t, pin,
2720               pout, act));
2721    DBGPRINTF7(("FORKEXEC: t->words is %s\n",
2722                ((t->words == NULL) ? "NULL" : t->words[0])));
2723
2724    owp = wp;
2725    resetsig = 0;
2726    rv = -1;                    /* system-detected error */
2727    if (t->type == TCOM) {
2728        while (*wp++ != NULL)
2729            continue;
2730        cp = *wp;
2731
2732        /* strip all initial assignments */
2733        /* not correct wrt PATH=yyy command  etc */
2734        if (FLAG['x']) {
2735            DBGPRINTF9(("FORKEXEC: echo'ing, cp=%p, wp=%p, owp=%p\n",
2736                        cp, wp, owp));
2737            echo(cp ? wp : owp);
2738        }
2739
2740        if (cp == NULL && t->ioact == NULL) {
2741            while ((cp = *owp++) != NULL && assign(cp, COPYV))
2742                continue;
2743            DBGPRINTF(("FORKEXEC: returning setstatus()\n"));
2744            return setstatus(0);
2745        }
2746        if (cp != NULL) {
2747            shcom = inbuilt(cp);
2748        }
2749    }
2750
2751    t->words = wp;
2752    f = act;
2753
2754    DBGPRINTF(("FORKEXEC: shcom %p, f&FEXEC 0x%x, owp %p\n", shcom,
2755               f & FEXEC, owp));
2756
2757    if (shcom == NULL && (f & FEXEC) == 0) {
2758        /* Save values in case the child process alters them */
2759        hpin = pin;
2760        hpout = pout;
2761        hwp = *wp;
2762        hinteractive = interactive;
2763        hintr = intr;
2764        hbrklist = brklist;
2765        hexecflg = execflg;
2766
2767        DBGPRINTF3(("FORKEXEC: calling vfork()...\n"));
2768
2769        newpid = vfork();
2770
2771        if (newpid == -1) {
2772            DBGPRINTF(("FORKEXEC: ERROR, cannot vfork()!\n"));
2773            return -1;
2774        }
2775
2776        if (newpid > 0) {  /* Parent */
2777            /* Restore values */
2778            pin = hpin;
2779            pout = hpout;
2780            *wp = hwp;
2781            interactive = hinteractive;
2782            intr = hintr;
2783            brklist = hbrklist;
2784            execflg = hexecflg;
2785/* moved up
2786            if (i == -1)
2787                return rv;
2788*/
2789            if (pin != NULL)
2790                closepipe(pin);
2791
2792            return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0);
2793        }
2794
2795        /* Must be the child process, pid should be 0 */
2796        DBGPRINTF(("FORKEXEC: child process, shcom=%p\n", shcom));
2797
2798        if (interactive) {
2799            signal(SIGINT, SIG_IGN);
2800            signal(SIGQUIT, SIG_IGN);
2801            resetsig = 1;
2802        }
2803        interactive = 0;
2804        intr = 0;
2805        forked = 1;
2806        brklist = 0;
2807        execflg = 0;
2808    }
2809
2810    if (owp != NULL)
2811        while ((cp = *owp++) != NULL && assign(cp, COPYV))
2812            if (shcom == NULL)
2813                export(lookup(cp));
2814
2815#ifdef COMPIPE
2816    if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
2817        err("piping to/from shell builtins not yet done");
2818        if (forked)
2819            _exit(-1);
2820        return -1;
2821    }
2822#endif
2823
2824    if (pin != NULL) {
2825        xmove_fd(pin[0], 0);
2826        if (pin[1] != 0) close(pin[1]);
2827    }
2828    if (pout != NULL) {
2829        xmove_fd(pout[1], 1);
2830        if (pout[1] != 1) close(pout[0]);
2831    }
2832
2833    iopp = t->ioact;
2834    if (iopp != NULL) {
2835        if (shcom != NULL && shcom != doexec) {
2836            prs(cp);
2837            err(": cannot redirect shell command");
2838            if (forked)
2839                _exit(-1);
2840            return -1;
2841        }
2842        while (*iopp)
2843            if (iosetup(*iopp++, pin != NULL, pout != NULL)) {
2844                if (forked)
2845                    _exit(rv);
2846                return rv;
2847            }
2848    }
2849
2850    if (shcom) {
2851        i = setstatus((*shcom) (t));
2852        if (forked)
2853            _exit(i);
2854        DBGPRINTF(("FORKEXEC: returning i=%d\n", i));
2855        return i;
2856    }
2857
2858    /* should use FIOCEXCL */
2859    for (i = FDBASE; i < NOFILE; i++)
2860        close(i);
2861    if (resetsig) {
2862        signal(SIGINT, SIG_DFL);
2863        signal(SIGQUIT, SIG_DFL);
2864    }
2865
2866    if (t->type == TPAREN)
2867        _exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
2868    if (wp[0] == NULL)
2869        _exit(0);
2870
2871    cp = rexecve(wp[0], wp, makenv(0, NULL));
2872    prs(wp[0]);
2873    prs(": ");
2874    err(cp);
2875    if (!execflg)
2876        trap[0] = NULL;
2877
2878    DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", newpid));
2879
2880    leave();
2881    /* NOTREACHED */
2882    _exit(1);
2883}
2884
2885/*
2886 * 0< 1> are ignored as required
2887 * within pipelines.
2888 */
2889static int iosetup(struct ioword *iop, int pipein, int pipeout)
2890{
2891    int u = -1;
2892    char *cp = NULL;
2893    const char *msg;
2894
2895    DBGPRINTF(("IOSETUP: iop %p, pipein %i, pipeout %i\n", iop,
2896               pipein, pipeout));
2897
2898    if (iop->io_unit == IODEFAULT)  /* take default */
2899        iop->io_unit = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1;
2900
2901    if (pipein && iop->io_unit == 0)
2902        return 0;
2903
2904    if (pipeout && iop->io_unit == 1)
2905        return 0;
2906
2907    msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create";
2908    if ((iop->io_flag & IOHERE) == 0) {
2909        cp = iop->io_name; /* huh?? */
2910        cp = evalstr(cp, DOSUB | DOTRIM);
2911        if (cp == NULL)
2912            return 1;
2913    }
2914
2915    if (iop->io_flag & IODUP) {
2916        if (cp[1] || (!isdigit(*cp) && *cp != '-')) {
2917            prs(cp);
2918            err(": illegal >& argument");
2919            return 1;
2920        }
2921        if (*cp == '-')
2922            iop->io_flag = IOCLOSE;
2923        iop->io_flag &= ~(IOREAD | IOWRITE);
2924    }
2925    switch (iop->io_flag) {
2926    case IOREAD:
2927        u = open(cp, O_RDONLY);
2928        break;
2929
2930    case IOHERE:
2931    case IOHERE | IOXHERE:
2932        u = herein(iop->io_name, iop->io_flag & IOXHERE);
2933        cp = (char*)"here file";
2934        break;
2935
2936    case IOWRITE | IOCAT:
2937        u = open(cp, O_WRONLY);
2938        if (u >= 0) {
2939            lseek(u, (long) 0, SEEK_END);
2940            break;
2941        }
2942    case IOWRITE:
2943        u = creat(cp, 0666);
2944        break;
2945
2946    case IODUP:
2947        u = dup2(*cp - '0', iop->io_unit);
2948        break;
2949
2950    case IOCLOSE:
2951        close(iop->io_unit);
2952        return 0;
2953    }
2954    if (u < 0) {
2955        prs(cp);
2956        prs(": cannot ");
2957        warn(msg);
2958        return 1;
2959    }
2960    if (u != iop->io_unit) {
2961        dup2(u, iop->io_unit);
2962        close(u);
2963    }
2964    return 0;
2965}
2966
2967/*
2968 * Enter a new loop level (marked for break/continue).
2969 */
2970static void brkset(struct brkcon *bc)
2971{
2972    bc->nextlev = brklist;
2973    brklist = bc;
2974}
2975
2976/*
2977 * Wait for the last process created.
2978 * Print a message for each process found
2979 * that was killed by a signal.
2980 * Ignore interrupt signals while waiting
2981 * unless `canintr' is true.
2982 */
2983static int waitfor(int lastpid, int canintr)
2984{
2985    int pid, rv;
2986    int s;
2987    int oheedint = heedint;
2988
2989    heedint = 0;
2990    rv = 0;
2991    do {
2992        pid = wait(&s);
2993        if (pid == -1) {
2994            if (errno != EINTR || canintr)
2995                break;
2996        } else {
2997            rv = WAITSIG(s);
2998            if (rv != 0) {
2999                if (rv < ARRAY_SIZE(signame)) {
3000                    if (signame[rv] != NULL) {
3001                        if (pid != lastpid) {
3002                            prn(pid);
3003                            prs(": ");
3004                        }
3005                        prs(signame[rv]);
3006                    }
3007                } else {
3008                    if (pid != lastpid) {
3009                        prn(pid);
3010                        prs(": ");
3011                    }
3012                    prs("Signal ");
3013                    prn(rv);
3014                    prs(" ");
3015                }
3016                if (WAITCORE(s))
3017                    prs(" - core dumped");
3018                if (rv >= ARRAY_SIZE(signame) || signame[rv])
3019                    prs("\n");
3020                rv = -1;
3021            } else
3022                rv = WAITVAL(s);
3023        }
3024    } while (pid != lastpid);
3025    heedint = oheedint;
3026    if (intr) {
3027        if (interactive) {
3028            if (canintr)
3029                intr = 0;
3030        } else {
3031            if (exstat == 0)
3032                exstat = rv;
3033            onintr(0);
3034        }
3035    }
3036    return rv;
3037}
3038
3039static int setstatus(int s)
3040{
3041    exstat = s;
3042    setval(lookup("?"), putn(s));
3043    return s;
3044}
3045
3046/*
3047 * PATH-searching interface to execve.
3048 * If getenv("PATH") were kept up-to-date,
3049 * execvp might be used.
3050 */
3051static const char *rexecve(char *c, char **v, char **envp)
3052{
3053    int i;
3054    const char *sp;
3055    char *tp;
3056    int eacces = 0, asis = 0;
3057    char *name = c;
3058
3059    if (ENABLE_FEATURE_SH_STANDALONE) {
3060        if (find_applet_by_name(name)) {
3061            /* We have to exec here since we vforked.  Running
3062             * run_applet_and_exit() won't work and bad things
3063             * will happen. */
3064            execve(bb_busybox_exec_path, v, envp);
3065        }
3066    }
3067
3068    DBGPRINTF(("REXECVE: c=%p, v=%p, envp=%p\n", c, v, envp));
3069
3070    sp = any('/', c) ? "" : path->value;
3071    asis = (*sp == '\0');
3072    while (asis || *sp != '\0') {
3073        asis = 0;
3074        tp = e.linep;
3075        for (; *sp != '\0'; tp++) {
3076            *tp = *sp++;
3077            if (*tp == ':') {
3078                asis = (*sp == '\0');
3079                break;
3080            }
3081        }
3082        if (tp != e.linep)
3083            *tp++ = '/';
3084        for (i = 0; (*tp++ = c[i++]) != '\0';);
3085
3086        DBGPRINTF3(("REXECVE: e.linep is %s\n", e.linep));
3087
3088        execve(e.linep, v, envp);
3089
3090        switch (errno) {
3091        case ENOEXEC:
3092            *v = e.linep;
3093            tp = *--v;
3094            *v = e.linep;
3095            execve(DEFAULT_SHELL, v, envp);
3096            *v = tp;
3097            return "no Shell";
3098
3099        case ENOMEM:
3100            return (char *) bb_msg_memory_exhausted;
3101
3102        case E2BIG:
3103            return "argument list too long";
3104
3105        case EACCES:
3106            eacces++;
3107            break;
3108        }
3109    }
3110    return errno == ENOENT ? "not found" : "cannot execute";
3111}
3112
3113/*
3114 * Run the command produced by generator `f'
3115 * applied to stream `arg'.
3116 */
3117static int run(struct ioarg *argp, int (*f) (struct ioarg *))
3118{
3119    struct op *otree;
3120    struct wdblock *swdlist;
3121    struct wdblock *siolist;
3122    jmp_buf ev, rt;
3123    xint *ofail;
3124    int rv;
3125
3126#if __GNUC__
3127    /* Avoid longjmp clobbering */
3128    (void) &rv;
3129#endif
3130
3131    DBGPRINTF(("RUN: enter, areanum %d, outtree %p, failpt %p\n",
3132               areanum, outtree, failpt));
3133
3134    areanum++;
3135    swdlist = wdlist;
3136    siolist = iolist;
3137    otree = outtree;
3138    ofail = failpt;
3139    rv = -1;
3140
3141    errpt = ev;
3142    if (newenv(setjmp(errpt)) == 0) {
3143        wdlist = 0;
3144        iolist = 0;
3145        pushio(argp, f);
3146        e.iobase = e.iop;
3147        yynerrs = 0;
3148        failpt = rt;
3149        if (setjmp(failpt) == 0 && yyparse() == 0)
3150            rv = execute(outtree, NOPIPE, NOPIPE, 0);
3151        quitenv();
3152    } else {
3153        DBGPRINTF(("RUN: error from newenv()!\n"));
3154    }
3155
3156    wdlist = swdlist;
3157    iolist = siolist;
3158    failpt = ofail;
3159    outtree = otree;
3160    freearea(areanum--);
3161
3162    return rv;
3163}
3164
3165/* -------- do.c -------- */
3166
3167/*
3168 * built-in commands: doX
3169 */
3170
3171static int dohelp(struct op *t)
3172{
3173    int col;
3174    const struct builtincmd *x;
3175
3176    puts("\nBuilt-in commands:\n"
3177         "-------------------");
3178
3179    col = 0;
3180    x = builtincmds;
3181    while (x->name) {
3182        col += printf("%c%s", ((col == 0) ? '\t' : ' '), x->name);
3183        if (col > 60) {
3184            puts("");
3185            col = 0;
3186        }
3187        x++;
3188    }
3189#if ENABLE_FEATURE_SH_STANDALONE
3190    {
3191        const struct bb_applet *applet = applets;
3192
3193        while (applet->name) {
3194            col += printf("%c%s", ((col == 0) ? '\t' : ' '), applet->name);
3195            if (col > 60) {
3196                puts("");
3197                col = 0;
3198            }
3199            applet++;
3200        }
3201    }
3202#endif
3203    puts("\n");
3204    return EXIT_SUCCESS;
3205}
3206
3207static int dolabel(struct op *t)
3208{
3209    return 0;
3210}
3211
3212static int dochdir(struct op *t)
3213{
3214    const char *cp, *er;
3215
3216    cp = t->words[1];
3217    if (cp == NULL) {
3218        cp = homedir->value;
3219        if (cp != NULL)
3220            goto do_cd;
3221        er = ": no home directory";
3222    } else {
3223 do_cd:
3224        if (chdir(cp) >= 0)
3225            return 0;
3226        er = ": bad directory";
3227    }
3228    prs(cp != NULL ? cp : "cd");
3229    err(er);
3230    return 1;
3231}
3232
3233static int doshift(struct op *t)
3234{
3235    int n;
3236
3237    n = t->words[1] ? getn(t->words[1]) : 1;
3238    if (dolc < n) {
3239        err("nothing to shift");
3240        return 1;
3241    }
3242    dolv[n] = dolv[0];
3243    dolv += n;
3244    dolc -= n;
3245    setval(lookup("#"), putn(dolc));
3246    return 0;
3247}
3248
3249/*
3250 * execute login and newgrp directly
3251 */
3252static int dologin(struct op *t)
3253{
3254    const char *cp;
3255
3256    if (interactive) {
3257        signal(SIGINT, SIG_DFL);
3258        signal(SIGQUIT, SIG_DFL);
3259    }
3260    cp = rexecve(t->words[0], t->words, makenv(0, NULL));
3261    prs(t->words[0]);
3262    prs(": ");
3263    err(cp);
3264    return 1;
3265}
3266
3267static int doumask(struct op *t)
3268{
3269    int i, n;
3270    char *cp;
3271
3272    cp = t->words[1];
3273    if (cp == NULL) {
3274        i = umask(0);
3275        umask(i);
3276        for (n = 3 * 4; (n -= 3) >= 0;)
3277            putc('0' + ((i >> n) & 07), stderr);
3278        putc('\n', stderr);
3279    } else {
3280/* huh??? '8','9' are not allowed! */
3281        for (n = 0; *cp >= '0' && *cp <= '9'; cp++)
3282            n = n * 8 + (*cp - '0');
3283        umask(n);
3284    }
3285    return 0;
3286}
3287
3288static int doexec(struct op *t)
3289{
3290    int i;
3291    jmp_buf ex;
3292    xint *ofail;
3293
3294    t->ioact = NULL;
3295    for (i = 0; (t->words[i] = t->words[i + 1]) != NULL; i++);
3296    if (i == 0)
3297        return 1;
3298    execflg = 1;
3299    ofail = failpt;
3300    failpt = ex;
3301    if (setjmp(failpt) == 0)
3302        execute(t, NOPIPE, NOPIPE, FEXEC);
3303    failpt = ofail;
3304    execflg = 0;
3305    return 1;
3306}
3307
3308static int dodot(struct op *t)
3309{
3310    int i;
3311    const char *sp;
3312    char *tp;
3313    char *cp;
3314    int maltmp;
3315
3316    DBGPRINTF(("DODOT: enter, t=%p, tleft %p, tright %p, e.linep is %s\n", t, t->left, t->right, ((e.linep == NULL) ? "NULL" : e.linep)));
3317
3318    cp = t->words[1];
3319    if (cp == NULL) {
3320        DBGPRINTF(("DODOT: bad args, ret 0\n"));
3321        return 0;
3322    }
3323    DBGPRINTF(("DODOT: cp is %s\n", cp));
3324
3325    sp = any('/', cp) ? ":" : path->value;
3326
3327    DBGPRINTF(("DODOT: sp is %s,  e.linep is %s\n",
3328               ((sp == NULL) ? "NULL" : sp),
3329               ((e.linep == NULL) ? "NULL" : e.linep)));
3330
3331    while (*sp) {
3332        tp = e.linep;
3333        while (*sp && (*tp = *sp++) != ':')
3334            tp++;
3335        if (tp != e.linep)
3336            *tp++ = '/';
3337
3338        for (i = 0; (*tp++ = cp[i++]) != '\0';);
3339
3340        /* Original code */
3341        i = open(e.linep, O_RDONLY);
3342        if (i >= 0) {
3343            exstat = 0;
3344            maltmp = remap(i);
3345            DBGPRINTF(("DODOT: remap=%d, exstat=%d, e.iofd %d, i %d, e.linep is %s\n", maltmp, exstat, e.iofd, i, e.linep));
3346
3347            next(maltmp);       /* Basically a PUSHIO */
3348
3349            DBGPRINTF(("DODOT: returning exstat=%d\n", exstat));
3350
3351            return exstat;
3352        }
3353    } /* while */
3354
3355    prs(cp);
3356    err(": not found");
3357
3358    return -1;
3359}
3360
3361static int dowait(struct op *t)
3362{
3363    int i;
3364    char *cp;
3365
3366    cp = t->words[1];
3367    if (cp != NULL) {
3368        i = getn(cp);
3369        if (i == 0)
3370            return 0;
3371    } else
3372        i = -1;
3373    setstatus(waitfor(i, 1));
3374    return 0;
3375}
3376
3377static int doread(struct op *t)
3378{
3379    char *cp, **wp;
3380    int nb = 0;
3381    int nl = 0;
3382
3383    if (t->words[1] == NULL) {
3384        err("Usage: read name ...");
3385        return 1;
3386    }
3387    for (wp = t->words + 1; *wp; wp++) {
3388        for (cp = e.linep; !nl && cp < elinep - 1; cp++) {
3389            nb = read(0, cp, sizeof(*cp));
3390            if (nb != sizeof(*cp))
3391                break;
3392            nl = (*cp == '\n');
3393            if (nl || (wp[1] && any(*cp, ifs->value)))
3394                break;
3395        }
3396        *cp = '\0';
3397        if (nb <= 0)
3398            break;
3399        setval(lookup(*wp), e.linep);
3400    }
3401    return nb <= 0;
3402}
3403
3404static int doeval(struct op *t)
3405{
3406    return RUN(awordlist, t->words + 1, wdchar);
3407}
3408
3409static int dotrap(struct op *t)
3410{
3411    int n, i;
3412    int resetsig;
3413
3414    if (t->words[1] == NULL) {
3415        for (i = 0; i <= _NSIG; i++)
3416            if (trap[i]) {
3417                prn(i);
3418                prs(": ");
3419                prs(trap[i]);
3420                prs("\n");
3421            }
3422        return 0;
3423    }
3424    resetsig = isdigit(*t->words[1]);
3425    for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) {
3426        n = getsig(t->words[i]);
3427        freecell(trap[n]);
3428        trap[n] = 0;
3429        if (!resetsig) {
3430            if (*t->words[1] != '\0') {
3431                trap[n] = strsave(t->words[1], 0);
3432                setsig(n, sig);
3433            } else
3434                setsig(n, SIG_IGN);
3435        } else {
3436            if (interactive) {
3437                if (n == SIGINT)
3438                    setsig(n, onintr);
3439                else
3440                    setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
3441            } else
3442                setsig(n, SIG_DFL);
3443        }
3444    }
3445    return 0;
3446}
3447
3448static int getsig(char *s)
3449{
3450    int n;
3451
3452    n = getn(s);
3453    if (n < 0 || n > _NSIG) {
3454        err("trap: bad signal number");
3455        n = 0;
3456    }
3457    return n;
3458}
3459
3460static void setsig(int n, sighandler_t f)
3461{
3462    if (n == 0)
3463        return;
3464    if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
3465        ourtrap[n] = 1;
3466        signal(n, f);
3467    }
3468}
3469
3470static int getn(char *as)
3471{
3472    char *s;
3473    int n, m;
3474
3475    s = as;
3476    m = 1;
3477    if (*s == '-') {
3478        m = -1;
3479        s++;
3480    }
3481    for (n = 0; isdigit(*s); s++)
3482        n = (n * 10) + (*s - '0');
3483    if (*s) {
3484        prs(as);
3485        err(": bad number");
3486    }
3487    return n * m;
3488}
3489
3490static int dobreak(struct op *t)
3491{
3492    return brkcontin(t->words[1], 1);
3493}
3494
3495static int docontinue(struct op *t)
3496{
3497    return brkcontin(t->words[1], 0);
3498}
3499
3500static int brkcontin(char *cp, int val)
3501{
3502    struct brkcon *bc;
3503    int nl;
3504
3505    nl = cp == NULL ? 1 : getn(cp);
3506    if (nl <= 0)
3507        nl = 999;
3508    do {
3509        bc = brklist;
3510        if (bc == NULL)
3511            break;
3512        brklist = bc->nextlev;
3513    } while (--nl);
3514    if (nl) {
3515        err("bad break/continue level");
3516        return 1;
3517    }
3518    isbreak = val;
3519    longjmp(bc->brkpt, 1);
3520    /* NOTREACHED */
3521}
3522
3523static int doexit(struct op *t)
3524{
3525    char *cp;
3526
3527    execflg = 0;
3528    cp = t->words[1];
3529    if (cp != NULL)
3530        setstatus(getn(cp));
3531
3532    DBGPRINTF(("DOEXIT: calling leave(), t=%p\n", t));
3533
3534    leave();
3535    /* NOTREACHED */
3536    return 0;
3537}
3538
3539static int doexport(struct op *t)
3540{
3541    rdexp(t->words + 1, export, EXPORT);
3542    return 0;
3543}
3544
3545static int doreadonly(struct op *t)
3546{
3547    rdexp(t->words + 1, ronly, RONLY);
3548    return 0;
3549}
3550
3551static void rdexp(char **wp, void (*f) (struct var *), int key)
3552{
3553    DBGPRINTF6(("RDEXP: enter, wp=%p, func=%p, key=%d\n", wp, f, key));
3554    DBGPRINTF6(("RDEXP: *wp=%s\n", *wp));
3555
3556    if (*wp != NULL) {
3557        for (; *wp != NULL; wp++) {
3558            if (isassign(*wp)) {
3559                char *cp;
3560
3561                assign(*wp, COPYV);
3562                for (cp = *wp; *cp != '='; cp++);
3563                *cp = '\0';
3564            }
3565            if (checkname(*wp))
3566                (*f) (lookup(*wp));
3567            else
3568                badid(*wp);
3569        }
3570    } else
3571        putvlist(key, 1);
3572}
3573
3574static void badid(char *s)
3575{
3576    prs(s);
3577    err(": bad identifier");
3578}
3579
3580static int doset(struct op *t)
3581{
3582    struct var *vp;
3583    char *cp;
3584    int n;
3585
3586    cp = t->words[1];
3587    if (cp == NULL) {
3588        for (vp = vlist; vp; vp = vp->next)
3589            varput(vp->name, 1);
3590        return 0;
3591    }
3592    if (*cp == '-') {
3593        /* bad: t->words++; */
3594        for (n = 0; (t->words[n] = t->words[n + 1]) != NULL; n++);
3595        if (*++cp == 0)
3596            FLAG['x'] = FLAG['v'] = 0;
3597        else {
3598            for (; *cp; cp++) {
3599                switch (*cp) {
3600                case 'e':
3601                    if (!interactive)
3602                        FLAG['e']++;
3603                    break;
3604
3605                default:
3606                    if (*cp >= 'a' && *cp <= 'z')
3607                        FLAG[(int) *cp]++;
3608                    break;
3609                }
3610            }
3611        }
3612        setdash();
3613    }
3614    if (t->words[1]) {
3615        t->words[0] = dolv[0];
3616        for (n = 1; t->words[n]; n++)
3617            setarea((char *) t->words[n], 0);
3618        dolc = n - 1;
3619        dolv = t->words;
3620        setval(lookup("#"), putn(dolc));
3621        setarea((char *) (dolv - 1), 0);
3622    }
3623    return 0;
3624}
3625
3626static void varput(char *s, int out)
3627{
3628    if (isalnum(*s) || *s == '_') {
3629        write(out, s, strlen(s));
3630        write(out, "\n", 1);
3631    }
3632}
3633
3634
3635/*
3636 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
3637 * This file contains code for the times builtin.
3638 */
3639static int dotimes(struct op *t)
3640{
3641    struct tms buf;
3642    long clk_tck = sysconf(_SC_CLK_TCK);
3643
3644    times(&buf);
3645    printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
3646           (int) (buf.tms_utime / clk_tck / 60),
3647           ((double) buf.tms_utime) / clk_tck,
3648           (int) (buf.tms_stime / clk_tck / 60),
3649           ((double) buf.tms_stime) / clk_tck,
3650           (int) (buf.tms_cutime / clk_tck / 60),
3651           ((double) buf.tms_cutime) / clk_tck,
3652           (int) (buf.tms_cstime / clk_tck / 60),
3653           ((double) buf.tms_cstime) / clk_tck);
3654    return 0;
3655}
3656
3657
3658/* -------- eval.c -------- */
3659
3660/*
3661 * ${}
3662 * `command`
3663 * blank interpretation
3664 * quoting
3665 * glob
3666 */
3667
3668static char **eval(char **ap, int f)
3669{
3670    struct wdblock *wb;
3671    char **wp;
3672    char **wf;
3673    jmp_buf ev;
3674
3675#if __GNUC__
3676    /* Avoid longjmp clobbering */
3677    (void) &wp;
3678    (void) &ap;
3679#endif
3680
3681    DBGPRINTF4(("EVAL: enter, f=%d\n", f));
3682
3683    wp = NULL;
3684    wb = NULL;
3685    wf = NULL;
3686    errpt = ev;
3687    if (newenv(setjmp(errpt)) == 0) {
3688        while (*ap && isassign(*ap))
3689            expand(*ap++, &wb, f & ~DOGLOB);
3690        if (FLAG['k']) {
3691            for (wf = ap; *wf; wf++) {
3692                if (isassign(*wf))
3693                    expand(*wf, &wb, f & ~DOGLOB);
3694            }
3695        }
3696        for (wb = addword((char *) 0, wb); *ap; ap++) {
3697            if (!FLAG['k'] || !isassign(*ap))
3698                expand(*ap, &wb, f & ~DOKEY);
3699        }
3700        wb = addword((char *) 0, wb);
3701        wp = getwords(wb);
3702        quitenv();
3703    } else
3704        gflg = 1;
3705
3706    return gflg ? (char **) NULL : wp;
3707}
3708
3709
3710/*
3711 * Make the exported environment from the exported
3712 * names in the dictionary. Keyword assignments
3713 * will already have been done.
3714 */
3715static char **makenv(int all, struct wdblock *wb)
3716{
3717    struct var *vp;
3718
3719    DBGPRINTF5(("MAKENV: enter, all=%d\n", all));
3720
3721    for (vp = vlist; vp; vp = vp->next)
3722        if (all || vp->status & EXPORT)
3723            wb = addword(vp->name, wb);
3724    wb = addword((char *) 0, wb);
3725    return getwords(wb);
3726}
3727
3728static int expand(const char *cp, struct wdblock **wbp, int f)
3729{
3730    jmp_buf ev;
3731    char *xp;
3732
3733#if __GNUC__
3734    /* Avoid longjmp clobbering */
3735    (void) &cp;
3736#endif
3737
3738    DBGPRINTF3(("EXPAND: enter, f=%d\n", f));
3739
3740    gflg = 0;
3741
3742    if (cp == NULL)
3743        return 0;
3744
3745    if (!anys("$`'\"", cp) && !anys(ifs->value, cp)
3746     && ((f & DOGLOB) == 0 || !anys("[*?", cp))
3747    ) {
3748        xp = strsave(cp, areanum);
3749        if (f & DOTRIM)
3750            unquote(xp);
3751        *wbp = addword(xp, *wbp);
3752        return 1;
3753    }
3754    errpt = ev;
3755    if (newenv(setjmp(errpt)) == 0) {
3756        PUSHIO(aword, cp, strchar);
3757        e.iobase = e.iop;
3758        while ((xp = blank(f)) && gflg == 0) {
3759            e.linep = xp;
3760            xp = strsave(xp, areanum);
3761            if ((f & DOGLOB) == 0) {
3762                if (f & DOTRIM)
3763                    unquote(xp);
3764                *wbp = addword(xp, *wbp);
3765            } else
3766                *wbp = glob(xp, *wbp);
3767        }
3768        quitenv();
3769    } else
3770        gflg = 1;
3771    return gflg == 0;
3772}
3773
3774static char *evalstr(char *cp, int f)
3775{
3776    struct wdblock *wb;
3777
3778    DBGPRINTF6(("EVALSTR: enter, cp=%p, f=%d\n", cp, f));
3779
3780    wb = NULL;
3781    if (expand(cp, &wb, f)) {
3782        if (wb == NULL || wb->w_nword == 0
3783         || (cp = wb->w_words[0]) == NULL
3784        ) {
3785// TODO: I suspect that
3786// char *evalstr(char *cp, int f)  is actually
3787// const char *evalstr(const char *cp, int f)!
3788            cp = (char*)"";
3789        }
3790        DELETE(wb);
3791    } else
3792        cp = NULL;
3793    return cp;
3794}
3795
3796
3797/*
3798 * Blank interpretation and quoting
3799 */
3800static char *blank(int f)
3801{
3802    int c, c1;
3803    char *sp;
3804    int scanequals, foundequals;
3805
3806    DBGPRINTF3(("BLANK: enter, f=%d\n", f));
3807
3808    sp = e.linep;
3809    scanequals = f & DOKEY;
3810    foundequals = 0;
3811
3812 loop:
3813    c = subgetc('"', foundequals);
3814    switch (c) {
3815    case 0:
3816        if (sp == e.linep)
3817            return 0;
3818        *e.linep++ = 0;
3819        return sp;
3820
3821    default:
3822        if (f & DOBLANK && any(c, ifs->value))
3823            goto loop;
3824        break;
3825
3826    case '"':
3827    case '\'':
3828        scanequals = 0;
3829        if (INSUB())
3830            break;
3831        for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
3832            if (c == 0)
3833                break;
3834            if (c == '\'' || !any(c, "$`\""))
3835                c |= QUOTE;
3836            *e.linep++ = c;
3837        }
3838        c = 0;
3839    }
3840    unget(c);
3841    if (!isalpha(c) && c != '_')
3842        scanequals = 0;
3843    for (;;) {
3844        c = subgetc('"', foundequals);
3845        if (c == 0 ||
3846            f & (DOBLANK && any(c, ifs->value)) ||
3847            (!INSUB() && any(c, "\"'"))) {
3848            scanequals = 0;
3849            unget(c);
3850            if (any(c, "\"'"))
3851                goto loop;
3852            break;
3853        }
3854        if (scanequals) {
3855            if (c == '=') {
3856                foundequals = 1;
3857                scanequals = 0;
3858            } else if (!isalnum(c) && c != '_')
3859                scanequals = 0;
3860        }
3861        *e.linep++ = c;
3862    }
3863    *e.linep++ = 0;
3864    return sp;
3865}
3866
3867/*
3868 * Get characters, substituting for ` and $
3869 */
3870static int subgetc(char ec, int quoted)
3871{
3872    char c;
3873
3874    DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted));
3875
3876 again:
3877    c = my_getc(ec);
3878    if (!INSUB() && ec != '\'') {
3879        if (c == '`') {
3880            if (grave(quoted) == 0)
3881                return 0;
3882            e.iop->task = XGRAVE;
3883            goto again;
3884        }
3885        if (c == '$') {
3886            c = dollar(quoted);
3887            if (c == 0) {
3888                e.iop->task = XDOLL;
3889                goto again;
3890            }
3891        }
3892    }
3893    return c;
3894}
3895
3896/*
3897 * Prepare to generate the string returned by ${} substitution.
3898 */
3899static int dollar(int quoted)
3900{
3901    int otask;
3902    struct io *oiop;
3903    char *dolp;
3904    char *s, c, *cp = NULL;
3905    struct var *vp;
3906
3907    DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted));
3908
3909    c = readc();
3910    s = e.linep;
3911    if (c != '{') {
3912        *e.linep++ = c;
3913        if (isalpha(c) || c == '_') {
3914            while ((c = readc()) != 0 && (isalnum(c) || c == '_'))
3915                if (e.linep < elinep)
3916                    *e.linep++ = c;
3917            unget(c);
3918        }
3919        c = 0;
3920    } else {
3921        oiop = e.iop;
3922        otask = e.iop->task;
3923
3924        e.iop->task = XOTHER;
3925        while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n')
3926            if (e.linep < elinep)
3927                *e.linep++ = c;
3928        if (oiop == e.iop)
3929            e.iop->task = otask;
3930        if (c != '}') {
3931            err("unclosed ${");
3932            gflg++;
3933            return c;
3934        }
3935    }
3936    if (e.linep >= elinep) {
3937        err("string in ${} too long");
3938        gflg++;
3939        e.linep -= 10;
3940    }
3941    *e.linep = 0;
3942    if (*s)
3943        for (cp = s + 1; *cp; cp++)
3944            if (any(*cp, "=-+?")) {
3945                c = *cp;
3946                *cp++ = 0;
3947                break;
3948            }
3949    if (s[1] == 0 && (*s == '*' || *s == '@')) {
3950        if (dolc > 1) {
3951            /* currently this does not distinguish $* and $@ */
3952            /* should check dollar */
3953            e.linep = s;
3954            PUSHIO(awordlist, dolv + 1, dolchar);
3955            return 0;
3956        } else {                /* trap the nasty ${=} */
3957            s[0] = '1';
3958            s[1] = '\0';
3959        }
3960    }
3961    vp = lookup(s);
3962    dolp = vp->value;
3963    if (dolp == null) {
3964        switch (c) {
3965        case '=':
3966            if (isdigit(*s)) {
3967                err("cannot use ${...=...} with $n");
3968                gflg++;
3969                break;
3970            }
3971            setval(vp, cp);
3972            dolp = vp->value;
3973            break;
3974
3975        case '-':
3976            dolp = strsave(cp, areanum);
3977            break;
3978
3979        case '?':
3980            if (*cp == 0) {
3981                prs("missing value for ");
3982                err(s);
3983            } else
3984                err(cp);
3985            gflg++;
3986            break;
3987        }
3988    } else if (c == '+')
3989        dolp = strsave(cp, areanum);
3990    if (FLAG['u'] && dolp == null) {
3991        prs("unset variable: ");
3992        err(s);
3993        gflg++;
3994    }
3995    e.linep = s;
3996    PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
3997    return 0;
3998}
3999
4000/*
4001 * Run the command in `...` and read its output.
4002 */
4003
4004static int grave(int quoted)
4005{
4006    /* moved to G: static char child_cmd[LINELIM]; */
4007
4008    const char *cp;
4009    int i;
4010    int j;
4011    int pf[2];
4012    const char *src;
4013    char *dest;
4014    int count;
4015    int ignore;
4016    int ignore_once;
4017    char *argument_list[4];
4018    struct wdblock *wb = NULL;
4019
4020#if __GNUC__
4021    /* Avoid longjmp clobbering */
4022    (void) &cp;
4023#endif
4024
4025    for (cp = e.iop->argp->aword; *cp != '`'; cp++) {
4026        if (*cp == 0) {
4027            err("no closing `");
4028            return 0;
4029        }
4030    }
4031
4032    /* string copy with dollar expansion */
4033    src = e.iop->argp->aword;
4034    dest = child_cmd;
4035    count = 0;
4036    ignore = 0;
4037    ignore_once = 0;
4038    while ((*src != '`') && (count < LINELIM)) {
4039        if (*src == '\'')
4040            ignore = !ignore;
4041        if (*src == '\\')
4042            ignore_once = 1;
4043        if (*src == '$' && !ignore && !ignore_once) {
4044            struct var *vp;
4045            char var_name[LINELIM];
4046            char alt_value[LINELIM];
4047            int var_index = 0;
4048            int alt_index = 0;
4049            char operator = 0;
4050            int braces = 0;
4051            char *value;
4052
4053            src++;
4054            if (*src == '{') {
4055                braces = 1;
4056                src++;
4057            }
4058
4059            var_name[var_index++] = *src++;
4060            while (isalnum(*src) || *src=='_')
4061                var_name[var_index++] = *src++;
4062            var_name[var_index] = 0;
4063
4064            if (braces) {
4065                switch (*src) {
4066                case '}':
4067                    break;
4068                case '-':
4069                case '=':
4070                case '+':
4071                case '?':
4072                    operator = * src;
4073                    break;
4074                default:
4075                    err("unclosed ${\n");
4076                    return 0;
4077                }
4078                if (operator) {
4079                    src++;
4080                    while (*src && (*src != '}')) {
4081                        alt_value[alt_index++] = *src++;
4082                    }
4083                    alt_value[alt_index] = 0;
4084                    if (*src != '}') {
4085                        err("unclosed ${\n");
4086                        return 0;
4087                    }
4088                }
4089                src++;
4090            }
4091
4092            if (isalpha(*var_name)) {
4093                /* let subshell handle it instead */
4094
4095                char *namep = var_name;
4096
4097                *dest++ = '$';
4098                if (braces)
4099                    *dest++ = '{';
4100                while (*namep)
4101                    *dest++ = *namep++;
4102                if (operator) {
4103                    char *altp = alt_value;
4104                    *dest++ = operator;
4105                    while (*altp)
4106                        *dest++ = *altp++;
4107                }
4108                if (braces)
4109                    *dest++ = '}';
4110
4111                wb = addword(lookup(var_name)->name, wb);
4112            } else {
4113                /* expand */
4114
4115                vp = lookup(var_name);
4116                if (vp->value != null)
4117                    value = (operator == '+') ?
4118                        alt_value : vp->value;
4119                else if (operator == '?') {
4120                    err(alt_value);
4121                    return 0;
4122                } else if (alt_index && (operator != '+')) {
4123                    value = alt_value;
4124                    if (operator == '=')
4125                        setval(vp, value);
4126                } else
4127                    continue;
4128
4129                while (*value && (count < LINELIM)) {
4130                    *dest++ = *value++;
4131                    count++;
4132                }
4133            }
4134        } else {
4135            *dest++ = *src++;
4136            count++;
4137            ignore_once = 0;
4138        }
4139    }
4140    *dest = '\0';
4141
4142    if (openpipe(pf) < 0)
4143        return 0;
4144
4145    while ((i = vfork()) == -1 && errno == EAGAIN);
4146
4147    DBGPRINTF3(("GRAVE: i is %p\n", io));
4148
4149    if (i < 0) {
4150        closepipe(pf);
4151        err((char *) bb_msg_memory_exhausted);
4152        return 0;
4153    }
4154    if (i != 0) {
4155        waitpid(i, NULL, 0);
4156        e.iop->argp->aword = ++cp;
4157        close(pf[1]);
4158        PUSHIO(afile, remap(pf[0]),
4159            (int (*)(struct ioarg *)) ((quoted) ? qgravechar : gravechar));
4160        return 1;
4161    }
4162    /* allow trapped signals */
4163    /* XXX - Maybe this signal stuff should go as well? */
4164    for (j = 0; j <= _NSIG; j++)
4165        if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN)
4166            signal(j, SIG_DFL);
4167
4168    /* Testcase where below checks are needed:
4169     * close stdout & run this script:
4170     *  files=`ls`
4171     *  echo "$files" >zz
4172     */
4173    xmove_fd(pf[1], 1);
4174    if (pf[0] != 1) close(pf[0]);
4175
4176    argument_list[0] = (char *) DEFAULT_SHELL;
4177    argument_list[1] = (char *) "-c";
4178    argument_list[2] = child_cmd;
4179    argument_list[3] = NULL;
4180
4181    cp = rexecve(argument_list[0], argument_list, makenv(1, wb));
4182    prs(argument_list[0]);
4183    prs(": ");
4184    err(cp);
4185    _exit(1);
4186}
4187
4188
4189static char *unquote(char *as)
4190{
4191    char *s;
4192
4193    s = as;
4194    if (s != NULL)
4195        while (*s)
4196            *s++ &= ~QUOTE;
4197    return as;
4198}
4199
4200/* -------- glob.c -------- */
4201
4202/*
4203 * glob
4204 */
4205
4206#define scopy(x) strsave((x), areanum)
4207#define BLKSIZ  512
4208#define NDENT   ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent))
4209
4210static struct wdblock *cl, *nl;
4211static const char spcl[] ALIGN1= "[?*";
4212
4213static struct wdblock *glob(char *cp, struct wdblock *wb)
4214{
4215    int i;
4216    char *pp;
4217
4218    if (cp == 0)
4219        return wb;
4220    i = 0;
4221    for (pp = cp; *pp; pp++)
4222        if (any(*pp, spcl))
4223            i++;
4224        else if (!any(*pp & ~QUOTE, spcl))
4225            *pp &= ~QUOTE;
4226    if (i != 0) {
4227        for (cl = addword(scopy(cp), NULL); anyspcl(cl); cl = nl) {
4228            nl = newword(cl->w_nword * 2);
4229            for (i = 0; i < cl->w_nword; i++) { /* for each argument */
4230                for (pp = cl->w_words[i]; *pp; pp++)
4231                    if (any(*pp, spcl)) {
4232                        globname(cl->w_words[i], pp);
4233                        break;
4234                    }
4235                if (*pp == '\0')
4236                    nl = addword(scopy(cl->w_words[i]), nl);
4237            }
4238            for (i = 0; i < cl->w_nword; i++)
4239                DELETE(cl->w_words[i]);
4240            DELETE(cl);
4241        }
4242        for (i = 0; i < cl->w_nword; i++)
4243            unquote(cl->w_words[i]);
4244        glob0((char *) cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
4245        if (cl->w_nword) {
4246            for (i = 0; i < cl->w_nword; i++)
4247                wb = addword(cl->w_words[i], wb);
4248            DELETE(cl);
4249            return wb;
4250        }
4251    }
4252    wb = addword(unquote(cp), wb);
4253    return wb;
4254}
4255
4256static void globname(char *we, char *pp)
4257{
4258    char *np, *cp;
4259    char *name, *gp, *dp;
4260    int k;
4261    DIR *dirp;
4262    struct dirent *de;
4263    char dname[NAME_MAX + 1];
4264    struct stat dbuf;
4265
4266    for (np = we; np != pp; pp--)
4267        if (pp[-1] == '/')
4268            break;
4269    for (dp = cp = space((int) (pp - np) + 3); np < pp;)
4270        *cp++ = *np++;
4271    *cp++ = '.';
4272    *cp = '\0';
4273    for (gp = cp = space(strlen(pp) + 1); *np && *np != '/';)
4274        *cp++ = *np++;
4275    *cp = '\0';
4276    dirp = opendir(dp);
4277    if (dirp == 0) {
4278        DELETE(dp);
4279        DELETE(gp);
4280        return;
4281    }
4282    dname[NAME_MAX] = '\0';
4283    while ((de = readdir(dirp)) != NULL) {
4284        /* XXX Hmmm... What this could be? (abial) */
4285        /*
4286           if (ent[j].d_ino == 0)
4287              continue;
4288         */
4289        strncpy(dname, de->d_name, NAME_MAX);
4290        if (dname[0] == '.')
4291            if (*gp != '.')
4292                continue;
4293        for (k = 0; k < NAME_MAX; k++)
4294            if (any(dname[k], spcl))
4295                dname[k] |= QUOTE;
4296        if (gmatch(dname, gp)) {
4297            name = generate(we, pp, dname, np);
4298            if (*np && !anys(np, spcl)) {
4299                if (stat(name, &dbuf)) {
4300                    DELETE(name);
4301                    continue;
4302                }
4303            }
4304            nl = addword(name, nl);
4305        }
4306    }
4307    closedir(dirp);
4308    DELETE(dp);
4309    DELETE(gp);
4310}
4311
4312/*
4313 * generate a pathname as below.
4314 * start..end1 / middle end
4315 * the slashes come for free
4316 */
4317static char *generate(char *start1, char *end1, char *middle, char *end)
4318{
4319    char *p;
4320    char *op, *xp;
4321
4322    p = op = space((int)(end1 - start1) + strlen(middle) + strlen(end) + 2);
4323    for (xp = start1; xp != end1;)
4324        *op++ = *xp++;
4325    for (xp = middle; (*op++ = *xp++) != '\0';);
4326    op--;
4327    for (xp = end; (*op++ = *xp++) != '\0';);
4328    return p;
4329}
4330
4331static int anyspcl(struct wdblock *wb)
4332{
4333    int i;
4334    char **wd;
4335
4336    wd = wb->w_words;
4337    for (i = 0; i < wb->w_nword; i++)
4338        if (anys(spcl, *wd++))
4339            return 1;
4340    return 0;
4341}
4342
4343static int xstrcmp(char *p1, char *p2)
4344{
4345    return strcmp(*(char **) p1, *(char **) p2);
4346}
4347
4348
4349/* -------- word.c -------- */
4350
4351static struct wdblock *newword(int nw)
4352{
4353    struct wdblock *wb;
4354
4355    wb = (struct wdblock *) space(sizeof(*wb) + nw * sizeof(char *));
4356    wb->w_bsize = nw;
4357    wb->w_nword = 0;
4358    return wb;
4359}
4360
4361static struct wdblock *addword(char *wd, struct wdblock *wb)
4362{
4363    struct wdblock *wb2;
4364    int nw;
4365
4366    if (wb == NULL)
4367        wb = newword(NSTART);
4368    nw = wb->w_nword;
4369    if (nw >= wb->w_bsize) {
4370        wb2 = newword(nw * 2);
4371        memcpy((char *) wb2->w_words, (char *) wb->w_words,
4372               nw * sizeof(char *));
4373        wb2->w_nword = nw;
4374        DELETE(wb);
4375        wb = wb2;
4376    }
4377    wb->w_words[wb->w_nword++] = wd;
4378    return wb;
4379}
4380
4381static char **getwords(struct wdblock *wb)
4382{
4383    char **wd;
4384    int nb;
4385
4386    if (wb == NULL)
4387        return NULL;
4388    if (wb->w_nword == 0) {
4389        DELETE(wb);
4390        return NULL;
4391    }
4392    wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
4393    memcpy((char *) wd, (char *) wb->w_words, nb);
4394    DELETE(wb);                 /* perhaps should done by caller */
4395    return wd;
4396}
4397
4398static int (*func) (char *, char *);
4399static int globv;
4400
4401static void glob3(char *i, char *j, char *k)
4402{
4403    char *index1, *index2, *index3;
4404    int c;
4405    int m;
4406
4407    m = globv;
4408    index1 = i;
4409    index2 = j;
4410    index3 = k;
4411    do {
4412        c = *index1;
4413        *index1++ = *index3;
4414        *index3++ = *index2;
4415        *index2++ = c;
4416    } while (--m);
4417}
4418
4419static void glob2(char *i, char *j)
4420{
4421    char *index1, *index2, c;
4422    int m;
4423
4424    m = globv;
4425    index1 = i;
4426    index2 = j;
4427    do {
4428        c = *index1;
4429        *index1++ = *index2;
4430        *index2++ = c;
4431    } while (--m);
4432}
4433
4434static void glob1(char *base, char *lim)
4435{
4436    char *i, *j;
4437    int v2;
4438    char *lptr, *hptr;
4439    int c;
4440    unsigned n;
4441
4442    v2 = globv;
4443
4444 top:
4445    n = (int) (lim - base);
4446    if (n <= v2)
4447        return;
4448    n = v2 * (n / (2 * v2));
4449    hptr = lptr = base + n;
4450    i = base;
4451    j = lim - v2;
4452    for (;;) {
4453        if (i < lptr) {
4454            c = (*func) (i, lptr);
4455            if (c == 0) {
4456                lptr -= v2;
4457                glob2(i, lptr);
4458                continue;
4459            }
4460            if (c < 0) {
4461                i += v2;
4462                continue;
4463            }
4464        }
4465
4466 begin:
4467        if (j > hptr) {
4468            c = (*func) (hptr, j);
4469            if (c == 0) {
4470                hptr += v2;
4471                glob2(hptr, j);
4472                goto begin;
4473            }
4474            if (c > 0) {
4475                if (i == lptr) {
4476                    hptr += v2;
4477                    glob3(i, hptr, j);
4478                    i = (lptr += v2);
4479                    goto begin;
4480                }
4481                glob2(i, j);
4482                j -= v2;
4483                i += v2;
4484                continue;
4485            }
4486            j -= v2;
4487            goto begin;
4488        }
4489
4490
4491        if (i == lptr) {
4492            if (lptr - base >= lim - hptr) {
4493                glob1(hptr + v2, lim);
4494                lim = lptr;
4495            } else {
4496                glob1(base, lptr);
4497                base = hptr + v2;
4498            }
4499            goto top;
4500        }
4501
4502        lptr -= v2;
4503        glob3(j, lptr, i);
4504        j = (hptr -= v2);
4505    }
4506}
4507
4508static void glob0(char *a0, unsigned a1, int a2, int (*a3) (char *, char *))
4509{
4510    func = a3;
4511    globv = a2;
4512    glob1(a0, a0 + a1 * a2);
4513}
4514
4515
4516/* -------- io.c -------- */
4517
4518/*
4519 * shell IO
4520 */
4521
4522static int my_getc(int ec)
4523{
4524    int c;
4525
4526    if (e.linep > elinep) {
4527        while ((c = readc()) != '\n' && c);
4528        err("input line too long");
4529        gflg++;
4530        return c;
4531    }
4532    c = readc();
4533    if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) {
4534        if (c == '\\') {
4535            c = readc();
4536            if (c == '\n' && ec != '\"')
4537                return my_getc(ec);
4538            c |= QUOTE;
4539        }
4540    }
4541    return c;
4542}
4543
4544static void unget(int c)
4545{
4546    if (e.iop >= e.iobase)
4547        e.iop->peekc = c;
4548}
4549
4550static int eofc(void)
4551{
4552    return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0);
4553}
4554
4555static int readc(void)
4556{
4557    int c;
4558
4559    RCPRINTF(("READC: e.iop %p, e.iobase %p\n", e.iop, e.iobase));
4560
4561    for (; e.iop >= e.iobase; e.iop--) {
4562        RCPRINTF(("READC: e.iop %p, peekc 0x%x\n", e.iop, e.iop->peekc));
4563        c = e.iop->peekc;
4564        if (c != '\0') {
4565            e.iop->peekc = 0;
4566            return c;
4567        }
4568        if (e.iop->prev != 0) {
4569            c = (*e.iop->iofn)(e.iop->argp, e.iop);
4570            if (c != '\0') {
4571                if (c == -1) {
4572                    e.iop++;
4573                    continue;
4574                }
4575                if (e.iop == iostack)
4576                    ioecho(c);
4577                e.iop->prev = c;
4578                return e.iop->prev;
4579            }
4580            if (e.iop->task == XIO && e.iop->prev != '\n') {
4581                e.iop->prev = 0;
4582                if (e.iop == iostack)
4583                    ioecho('\n');
4584                return '\n';
4585            }
4586        }
4587        if (e.iop->task == XIO) {
4588            if (multiline) {
4589                e.iop->prev = 0;
4590                return e.iop->prev;
4591            }
4592            if (interactive && e.iop == iostack + 1) {
4593#if ENABLE_FEATURE_EDITING
4594                current_prompt = prompt->value;
4595#else
4596                prs(prompt->value);
4597#endif
4598            }
4599        }
4600    }                           /* FOR */
4601
4602    if (e.iop >= iostack) {
4603        RCPRINTF(("READC: return 0, e.iop %p\n", e.iop));
4604        return 0;
4605    }
4606
4607    DBGPRINTF(("READC: leave()...\n"));
4608    leave();
4609
4610    /* NOTREACHED */
4611    return 0;
4612}
4613
4614static void ioecho(char c)
4615{
4616    if (FLAG['v'])
4617        write(2, &c, sizeof c);
4618}
4619
4620
4621static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *))
4622{
4623    DBGPRINTF(("PUSHIO: argp %p, argp->afid 0x%x, e.iop %p\n", argp,
4624               argp->afid, e.iop));
4625
4626    /* Set env ptr for io source to next array spot and check for array overflow */
4627    if (++e.iop >= &iostack[NPUSH]) {
4628        e.iop--;
4629        err("Shell input nested too deeply");
4630        gflg++;
4631        return;
4632    }
4633
4634    /* We did not overflow the NPUSH array spots so setup data structs */
4635
4636    e.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn;    /* Store data source func ptr */
4637
4638    if (argp->afid != AFID_NOBUF)
4639        e.iop->argp = argp;
4640    else {
4641
4642        e.iop->argp = ioargstack + (e.iop - iostack);   /* MAL - index into stack */
4643        *e.iop->argp = *argp;   /* copy data from temp area into stack spot */
4644
4645        /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */
4646
4647        if (e.iop == &iostack[0])
4648            e.iop->argp->afbuf = &mainbuf;
4649        else
4650            e.iop->argp->afbuf = &sharedbuf;
4651
4652        /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */
4653        /* This line appears to be active when running scripts from command line */
4654        if ((isatty(e.iop->argp->afile) == 0)
4655            && (e.iop == &iostack[0]
4656                || lseek(e.iop->argp->afile, 0L, SEEK_CUR) != -1)) {
4657            if (++bufid == AFID_NOBUF)  /* counter rollover check, AFID_NOBUF = 11111111  */
4658                bufid = AFID_ID;    /* AFID_ID = 0 */
4659
4660            e.iop->argp->afid = bufid;  /* assign buffer id */
4661        }
4662
4663        DBGPRINTF(("PUSHIO: iostack %p,  e.iop %p, afbuf %p\n",
4664                   iostack, e.iop, e.iop->argp->afbuf));
4665        DBGPRINTF(("PUSHIO: mbuf %p, sbuf %p, bid %d, e.iop %p\n",
4666                   &mainbuf, &sharedbuf, bufid, e.iop));
4667
4668    }
4669
4670    e.iop->prev = ~'\n';
4671    e.iop->peekc = 0;
4672    e.iop->xchar = 0;
4673    e.iop->nlcount = 0;
4674
4675    if (fn == filechar || fn == linechar)
4676        e.iop->task = XIO;
4677    else if (fn == (int (*)(struct ioarg *)) gravechar
4678     || fn == (int (*)(struct ioarg *)) qgravechar)
4679        e.iop->task = XGRAVE;
4680    else
4681        e.iop->task = XOTHER;
4682}
4683
4684static struct io *setbase(struct io *ip)
4685{
4686    struct io *xp;
4687
4688    xp = e.iobase;
4689    e.iobase = ip;
4690    return xp;
4691}
4692
4693/*
4694 * Input generating functions
4695 */
4696
4697/*
4698 * Produce the characters of a string, then a newline, then EOF.
4699 */
4700static int nlchar(struct ioarg *ap)
4701{
4702    int c;
4703
4704    if (ap->aword == NULL)
4705        return 0;
4706    c = *ap->aword++;
4707    if (c == 0) {
4708        ap->aword = NULL;
4709        return '\n';
4710    }
4711    return c;
4712}
4713
4714/*
4715 * Given a list of words, produce the characters
4716 * in them, with a space after each word.
4717 */
4718static int wdchar(struct ioarg *ap)
4719{
4720    char c;
4721    char **wl;
4722
4723    wl = ap->awordlist;
4724    if (wl == NULL)
4725        return 0;
4726    if (*wl != NULL) {
4727        c = *(*wl)++;
4728        if (c != 0)
4729            return c & 0177;
4730        ap->awordlist++;
4731        return ' ';
4732    }
4733    ap->awordlist = NULL;
4734    return '\n';
4735}
4736
4737/*
4738 * Return the characters of a list of words,
4739 * producing a space between them.
4740 */
4741static int dolchar(struct ioarg *ap)
4742{
4743    char *wp;
4744
4745    wp = *ap->awordlist++;
4746    if (wp != NULL) {
4747        PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar);
4748        return -1;
4749    }
4750    return 0;
4751}
4752
4753static int xxchar(struct ioarg *ap)
4754{
4755    int c;
4756
4757    if (ap->aword == NULL)
4758        return 0;
4759    c = *ap->aword++;
4760    if (c == '\0') {
4761        ap->aword = NULL;
4762        return ' ';
4763    }
4764    return c;
4765}
4766
4767/*
4768 * Produce the characters from a single word (string).
4769 */
4770static int strchar(struct ioarg *ap)
4771{
4772    if (ap->aword == NULL)
4773        return 0;
4774    return *ap->aword++;
4775}
4776
4777/*
4778 * Produce quoted characters from a single word (string).
4779 */
4780static int qstrchar(struct ioarg *ap)
4781{
4782    int c;
4783
4784    if (ap->aword == NULL)
4785        return 0;
4786    c = *ap->aword++;
4787    if (c)
4788        c |= QUOTE;
4789    return c;
4790}
4791
4792/*
4793 * Return the characters from a file.
4794 */
4795static int filechar(struct ioarg *ap)
4796{
4797    int i;
4798    char c;
4799    struct iobuf *bp = ap->afbuf;
4800
4801    if (ap->afid != AFID_NOBUF) {
4802        i = (ap->afid != bp->id);
4803        if (i || bp->bufp == bp->ebufp) {
4804            if (i)
4805                lseek(ap->afile, ap->afpos, SEEK_SET);
4806
4807            i = safe_read(ap->afile, bp->buf, sizeof(bp->buf));
4808            if (i <= 0) {
4809                closef(ap->afile);
4810                return 0;
4811            }
4812
4813            bp->id = ap->afid;
4814            bp->bufp = bp->buf;
4815            bp->ebufp = bp->bufp + i;
4816        }
4817
4818        ap->afpos++;
4819        return *bp->bufp++ & 0177;
4820    }
4821#if ENABLE_FEATURE_EDITING
4822    if (interactive && isatty(ap->afile)) {
4823        /* moved to G: static char filechar_cmdbuf[BUFSIZ]; */
4824        static int position = 0, size = 0;
4825
4826        while (size == 0 || position >= size) {
4827            read_line_input(current_prompt, filechar_cmdbuf, BUFSIZ, line_input_state);
4828            size = strlen(filechar_cmdbuf);
4829            position = 0;
4830        }
4831        c = filechar_cmdbuf[position];
4832        position++;
4833        return c;
4834    }
4835#endif
4836    i = safe_read(ap->afile, &c, sizeof(c));
4837    return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0);
4838}
4839
4840/*
4841 * Return the characters from a here temp file.
4842 */
4843static int herechar(struct ioarg *ap)
4844{
4845    char c;
4846
4847    if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
4848        close(ap->afile);
4849        c = '\0';
4850    }
4851    return c;
4852}
4853
4854/*
4855 * Return the characters produced by a process (`...`).
4856 * Quote them if required, and remove any trailing newline characters.
4857 */
4858static int gravechar(struct ioarg *ap, struct io *iop)
4859{
4860    int c;
4861
4862    c = qgravechar(ap, iop) & ~QUOTE;
4863    if (c == '\n')
4864        c = ' ';
4865    return c;
4866}
4867
4868static int qgravechar(struct ioarg *ap, struct io *iop)
4869{
4870    int c;
4871
4872    DBGPRINTF3(("QGRAVECHAR: enter, ap=%p, iop=%p\n", ap, iop));
4873
4874    if (iop->xchar) {
4875        if (iop->nlcount) {
4876            iop->nlcount--;
4877            return '\n' | QUOTE;
4878        }
4879        c = iop->xchar;
4880        iop->xchar = 0;
4881    } else if ((c = filechar(ap)) == '\n') {
4882        iop->nlcount = 1;
4883        while ((c = filechar(ap)) == '\n')
4884            iop->nlcount++;
4885        iop->xchar = c;
4886        if (c == 0)
4887            return c;
4888        iop->nlcount--;
4889        c = '\n';
4890    }
4891    return c != 0 ? c | QUOTE : 0;
4892}
4893
4894/*
4895 * Return a single command (usually the first line) from a file.
4896 */
4897static int linechar(struct ioarg *ap)
4898{
4899    int c;
4900
4901    c = filechar(ap);
4902    if (c == '\n') {
4903        if (!multiline) {
4904            closef(ap->afile);
4905            ap->afile = -1;     /* illegal value */
4906        }
4907    }
4908    return c;
4909}
4910
4911/*
4912 * remap fd into Shell's fd space
4913 */
4914static int remap(int fd)
4915{
4916    int i;
4917    int map[NOFILE];
4918    int newfd;
4919
4920    DBGPRINTF(("REMAP: fd=%d, e.iofd=%d\n", fd, e.iofd));
4921
4922    if (fd < e.iofd) {
4923        for (i = 0; i < NOFILE; i++)
4924            map[i] = 0;
4925
4926        do {
4927            map[fd] = 1;
4928            newfd = dup(fd);
4929            fd = newfd;
4930        } while (fd >= 0 && fd < e.iofd);
4931
4932        for (i = 0; i < NOFILE; i++)
4933            if (map[i])
4934                close(i);
4935
4936        if (fd < 0)
4937            err("too many files open in shell");
4938    }
4939
4940    return fd;
4941}
4942
4943static int openpipe(int *pv)
4944{
4945    int i;
4946
4947    i = pipe(pv);
4948    if (i < 0)
4949        err("can't create pipe - try again");
4950    return i;
4951}
4952
4953static void closepipe(int *pv)
4954{
4955    if (pv != NULL) {
4956        close(*pv++);
4957        close(*pv);
4958    }
4959}
4960
4961
4962/* -------- here.c -------- */
4963
4964/*
4965 * here documents
4966 */
4967
4968static void markhere(char *s, struct ioword *iop)
4969{
4970    struct here *h, *lh;
4971
4972    DBGPRINTF7(("MARKHERE: enter, s=%p\n", s));
4973
4974    h = (struct here *) space(sizeof(struct here));
4975    if (h == NULL)
4976        return;
4977
4978    h->h_tag = evalstr(s, DOSUB);
4979    if (h->h_tag == 0)
4980        return;
4981
4982    h->h_iop = iop;
4983    iop->io_name = 0;
4984    h->h_next = NULL;
4985    if (inhere == 0)
4986        inhere = h;
4987    else {
4988        for (lh = inhere; lh != NULL; lh = lh->h_next) {
4989            if (lh->h_next == 0) {
4990                lh->h_next = h;
4991                break;
4992            }
4993        }
4994    }
4995    iop->io_flag |= IOHERE | IOXHERE;
4996    for (s = h->h_tag; *s; s++) {
4997        if (*s & QUOTE) {
4998            iop->io_flag &= ~IOXHERE;
4999            *s &= ~QUOTE;
5000        }
5001    }
5002    h->h_dosub = iop->io_flag & IOXHERE;
5003}
5004
5005static void gethere(void)
5006{
5007    struct here *h, *hp;
5008
5009    DBGPRINTF7(("GETHERE: enter...\n"));
5010
5011    /* Scan here files first leaving inhere list in place */
5012    for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
5013        readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'');
5014
5015    /* Make inhere list active - keep list intact for scraphere */
5016    if (hp != NULL) {
5017        hp->h_next = acthere;
5018        acthere = inhere;
5019        inhere = NULL;
5020    }
5021}
5022
5023static void readhere(char **name, char *s, int ec)
5024{
5025    int tf;
5026    char tname[30] = ".msh_XXXXXX";
5027    int c;
5028    jmp_buf ev;
5029    char myline[LINELIM + 1];
5030    char *thenext;
5031
5032    DBGPRINTF7(("READHERE: enter, name=%p, s=%p\n", name, s));
5033
5034    tf = mkstemp(tname);
5035    if (tf < 0)
5036        return;
5037
5038    *name = strsave(tname, areanum);
5039    errpt = ev;
5040    if (newenv(setjmp(errpt)) != 0)
5041        unlink(tname);
5042    else {
5043        pushio(e.iop->argp, (int (*)(struct ioarg *)) e.iop->iofn);
5044        e.iobase = e.iop;
5045        for (;;) {
5046            if (interactive && e.iop <= iostack) {
5047#if ENABLE_FEATURE_EDITING
5048                current_prompt = cprompt->value;
5049#else
5050                prs(cprompt->value);
5051#endif
5052            }
5053            thenext = myline;
5054            while ((c = my_getc(ec)) != '\n' && c) {
5055                if (ec == '\'')
5056                    c &= ~QUOTE;
5057                if (thenext >= &myline[LINELIM]) {
5058                    c = 0;
5059                    break;
5060                }
5061                *thenext++ = c;
5062            }
5063            *thenext = 0;
5064            if (strcmp(s, myline) == 0 || c == 0)
5065                break;
5066            *thenext++ = '\n';
5067            write(tf, myline, (int) (thenext - myline));
5068        }
5069        if (c == 0) {
5070            prs("here document `");
5071            prs(s);
5072            err("' unclosed");
5073        }
5074        quitenv();
5075    }
5076    close(tf);
5077}
5078
5079/*
5080 * open here temp file.
5081 * if unquoted here, expand here temp file into second temp file.
5082 */
5083static int herein(char *hname, int xdoll)
5084{
5085    int hf;
5086    int tf;
5087
5088#if __GNUC__
5089    /* Avoid longjmp clobbering */
5090    (void) &tf;
5091#endif
5092    if (hname == NULL)
5093        return -1;
5094
5095    DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll));
5096
5097    hf = open(hname, O_RDONLY);
5098    if (hf < 0)
5099        return -1;
5100
5101    if (xdoll) {
5102        char c;
5103        char tname[30] = ".msh_XXXXXX";
5104        jmp_buf ev;
5105
5106        tf = mkstemp(tname);
5107        if (tf < 0)
5108            return -1;
5109        errpt = ev;
5110        if (newenv(setjmp(errpt)) == 0) {
5111            PUSHIO(afile, hf, herechar);
5112            setbase(e.iop);
5113            while ((c = subgetc(0, 0)) != 0) {
5114                c &= ~QUOTE;
5115                write(tf, &c, sizeof c);
5116            }
5117            quitenv();
5118        } else
5119            unlink(tname);
5120        close(tf);
5121        tf = open(tname, O_RDONLY);
5122        unlink(tname);
5123        return tf;
5124    }
5125    return hf;
5126}
5127
5128static void scraphere(void)
5129{
5130    struct here *h;
5131
5132    DBGPRINTF7(("SCRAPHERE: enter...\n"));
5133
5134    for (h = inhere; h != NULL; h = h->h_next) {
5135        if (h->h_iop && h->h_iop->io_name)
5136            unlink(h->h_iop->io_name);
5137    }
5138    inhere = NULL;
5139}
5140
5141/* unlink here temp files before a freearea(area) */
5142static void freehere(int area)
5143{
5144    struct here *h, *hl;
5145
5146    DBGPRINTF6(("FREEHERE: enter, area=%d\n", area));
5147
5148    hl = NULL;
5149    for (h = acthere; h != NULL; h = h->h_next)
5150        if (getarea((char *) h) >= area) {
5151            if (h->h_iop->io_name != NULL)
5152                unlink(h->h_iop->io_name);
5153            if (hl == NULL)
5154                acthere = h->h_next;
5155            else
5156                hl->h_next = h->h_next;
5157        } else
5158            hl = h;
5159}
5160
5161
5162/* -------- sh.c -------- */
5163/*
5164 * shell
5165 */
5166
5167int msh_main(int argc, char **argv);
5168int msh_main(int argc, char **argv)
5169{
5170    int f;
5171    char *s;
5172    int cflag;
5173    char *name, **ap;
5174    int (*iof) (struct ioarg *);
5175
5176    PTR_TO_GLOBALS = xzalloc(sizeof(G));
5177    sharedbuf.id = AFID_NOBUF;
5178    mainbuf.id = AFID_NOBUF;
5179    e.linep = line;
5180    elinep = line + sizeof(line) - 5;
5181
5182#if ENABLE_FEATURE_EDITING
5183    line_input_state = new_line_input_t(FOR_SHELL);
5184#endif
5185
5186    DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
5187
5188    initarea();
5189    ap = environ;
5190    if (ap != NULL) {
5191        while (*ap)
5192            assign(*ap++, !COPYV);
5193        for (ap = environ; *ap;)
5194            export(lookup(*ap++));
5195    }
5196    closeall();
5197    areanum = 1;
5198
5199    shell = lookup("SHELL");
5200    if (shell->value == null)
5201        setval(shell, (char *)DEFAULT_SHELL);
5202    export(shell);
5203
5204    homedir = lookup("HOME");
5205    if (homedir->value == null)
5206        setval(homedir, "/");
5207    export(homedir);
5208
5209    setval(lookup("$"), putn(getpid()));
5210
5211    path = lookup("PATH");
5212    if (path->value == null) {
5213        /* Can be merged with same string elsewhere in bbox */
5214        if (geteuid() == 0)
5215            setval(path, bb_default_root_path);
5216        else
5217            setval(path, bb_default_path);
5218    }
5219    export(path);
5220
5221    ifs = lookup("IFS");
5222    if (ifs->value == null)
5223        setval(ifs, " \t\n");
5224
5225#ifdef MSHDEBUG
5226    mshdbg_var = lookup("MSHDEBUG");
5227    if (mshdbg_var->value == null)
5228        setval(mshdbg_var, "0");
5229#endif
5230
5231    prompt = lookup("PS1");
5232#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
5233    if (prompt->value == null)
5234#endif
5235        setval(prompt, DEFAULT_USER_PROMPT);
5236    if (geteuid() == 0) {
5237        setval(prompt, DEFAULT_ROOT_PROMPT);
5238        prompt->status &= ~EXPORT;
5239    }
5240    cprompt = lookup("PS2");
5241#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
5242    if (cprompt->value == null)
5243#endif
5244        setval(cprompt, "> ");
5245
5246    iof = filechar;
5247    cflag = 0;
5248    name = *argv++;
5249    if (--argc >= 1) {
5250        if (argv[0][0] == '-' && argv[0][1] != '\0') {
5251            for (s = argv[0] + 1; *s; s++)
5252                switch (*s) {
5253                case 'c':
5254                    prompt->status &= ~EXPORT;
5255                    cprompt->status &= ~EXPORT;
5256                    setval(prompt, "");
5257                    setval(cprompt, "");
5258                    cflag = 1;
5259                    if (--argc > 0)
5260                        PUSHIO(aword, *++argv, iof = nlchar);
5261                    break;
5262
5263                case 'q':
5264                    qflag = SIG_DFL;
5265                    break;
5266
5267                case 's':
5268                    /* standard input */
5269                    break;
5270
5271                case 't':
5272                    prompt->status &= ~EXPORT;
5273                    setval(prompt, "");
5274                    iof = linechar;
5275                    break;
5276
5277                case 'i':
5278                    interactive++;
5279                default:
5280                    if (*s >= 'a' && *s <= 'z')
5281                        FLAG[(int) *s]++;
5282                }
5283        } else {
5284            argv--;
5285            argc++;
5286        }
5287
5288        if (iof == filechar && --argc > 0) {
5289            setval(prompt, "");
5290            setval(cprompt, "");
5291            prompt->status &= ~EXPORT;
5292            cprompt->status &= ~EXPORT;
5293
5294/* Shell is non-interactive, activate printf-based debug */
5295#ifdef MSHDEBUG
5296            mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
5297            if (mshdbg < 0)
5298                mshdbg = 0;
5299#endif
5300            DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
5301
5302            name = *++argv;
5303            if (newfile(name))
5304                exit(1);        /* Exit on error */
5305        }
5306    }
5307
5308    setdash();
5309
5310    /* This won't be true if PUSHIO has been called, say from newfile() above */
5311    if (e.iop < iostack) {
5312        PUSHIO(afile, 0, iof);
5313        if (isatty(0) && isatty(1) && !cflag) {
5314            interactive++;
5315#if !ENABLE_FEATURE_SH_EXTRA_QUIET
5316#ifdef MSHDEBUG
5317            printf("\n\n%s built-in shell (msh with debug)\n", bb_banner);
5318#else
5319            printf("\n\n%s built-in shell (msh)\n", bb_banner);
5320#endif
5321            printf("Enter 'help' for a list of built-in commands.\n\n");
5322#endif
5323        }
5324    }
5325
5326    signal(SIGQUIT, qflag);
5327    if (name && name[0] == '-') {
5328        interactive++;
5329        f = open(".profile", O_RDONLY);
5330        if (f >= 0)
5331            next(remap(f));
5332        f = open("/etc/profile", O_RDONLY);
5333        if (f >= 0)
5334            next(remap(f));
5335    }
5336    if (interactive)
5337        signal(SIGTERM, sig);
5338
5339    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
5340        signal(SIGINT, onintr);
5341    dolv = argv;
5342    dolc = argc;
5343    dolv[0] = name;
5344    if (dolc > 1) {
5345        for (ap = ++argv; --argc > 0;) {
5346            *ap = *argv++;
5347            if (assign(*ap, !COPYV)) {
5348                dolc--;         /* keyword */
5349            } else {
5350                ap++;
5351            }
5352        }
5353    }
5354    setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
5355
5356    DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop %p, iostack %p\n", interactive, e.iop, iostack));
5357
5358    for (;;) {
5359        if (interactive && e.iop <= iostack) {
5360#if ENABLE_FEATURE_EDITING
5361            current_prompt = prompt->value;
5362#else
5363            prs(prompt->value);
5364#endif
5365        }
5366        onecommand();
5367        /* Ensure that getenv("PATH") stays current */
5368        setenv("PATH", path->value, 1);
5369    }
5370
5371    DBGPRINTF(("MSH_MAIN: returning.\n"));
5372}
5373
5374
5375/*
5376 * Copyright (c) 1987,1997, Prentice Hall
5377 * All rights reserved.
5378 *
5379 * Redistribution and use of the MINIX operating system in source and
5380 * binary forms, with or without modification, are permitted provided
5381 * that the following conditions are met:
5382 *
5383 * Redistributions of source code must retain the above copyright
5384 * notice, this list of conditions and the following disclaimer.
5385 *
5386 * Redistributions in binary form must reproduce the above
5387 * copyright notice, this list of conditions and the following
5388 * disclaimer in the documentation and/or other materials provided
5389 * with the distribution.
5390 *
5391 * Neither the name of Prentice Hall nor the names of the software
5392 * authors or contributors may be used to endorse or promote
5393 * products derived from this software without specific prior
5394 * written permission.
5395 *
5396 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
5397 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
5398 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
5399 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5400 * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
5401 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5402 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5403 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
5404 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
5405 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
5406 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
5407 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5408 *
5409 */
Note: See TracBrowser for help on using the repository browser.