blob: c77bfec4fcca9a2537731056c5aa87a45471ada1 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
8 * This code is derived from software contributed to Berkeley by
9 * Kenneth Almquist.
10 *
Eric Andersendf82f612001-06-28 07:46:40 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
Eric Andersencb57d552001-06-28 07:25:16 +000015 *
Eric Andersendf82f612001-06-28 07:46:40 +000016 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
Eric Andersen2870d962001-07-02 17:27:21 +000025 * This version of ash is adapted from the source in Debian's ash 0.3.8-5
26 * package.
Eric Andersendf82f612001-06-28 07:46:40 +000027 *
Eric Andersen2870d962001-07-02 17:27:21 +000028 * Modified by Erik Andersen <andersee@debian.org> and
Eric Andersen7467c8d2001-07-12 20:26:32 +000029 * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
Eric Andersen2870d962001-07-02 17:27:21 +000030 *
Eric Andersendf82f612001-06-28 07:46:40 +000031 *
32 * Original copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000033 */
34
Eric Andersen2870d962001-07-02 17:27:21 +000035
Eric Andersen2870d962001-07-02 17:27:21 +000036/* Enable this to compile in extra debugging noise. When debugging is
37 * on, debugging info will be written to $HOME/trace and a quit signal
38 * will generate a core dump. */
39#undef DEBUG
40
Eric Andersen2870d962001-07-02 17:27:21 +000041/* These are here to work with glibc -- Don't change these... */
Eric Andersendf82f612001-06-28 07:46:40 +000042#undef FNMATCH_BROKEN
43#undef GLOB_BROKEN
Eric Andersen5bb16772001-09-06 18:00:41 +000044#define IFS_BROKEN
Eric Andersencb57d552001-06-28 07:25:16 +000045
46#include <assert.h>
Manuel Novoa III 16815d42001-08-10 19:36:07 +000047#include <stddef.h>
Eric Andersencb57d552001-06-28 07:25:16 +000048#include <ctype.h>
49#include <dirent.h>
50#include <errno.h>
51#include <fcntl.h>
52#include <limits.h>
53#include <paths.h>
Eric Andersencb57d552001-06-28 07:25:16 +000054#include <setjmp.h>
55#include <signal.h>
56#include <stdarg.h>
Eric Andersencb57d552001-06-28 07:25:16 +000057#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <sysexits.h>
61#include <unistd.h>
62#include <sys/stat.h>
63#include <sys/cdefs.h>
64#include <sys/ioctl.h>
65#include <sys/param.h>
66#include <sys/resource.h>
67#include <sys/time.h>
68#include <sys/times.h>
69#include <sys/types.h>
70#include <sys/wait.h>
Eric Andersen1a10eec2001-10-24 17:19:38 +000071#include "pwd.h"
Eric Andersencb57d552001-06-28 07:25:16 +000072
73
74#if !defined(FNMATCH_BROKEN)
75#include <fnmatch.h>
76#endif
77#if !defined(GLOB_BROKEN)
78#include <glob.h>
79#endif
80
Eric Andersend35c5df2002-01-09 15:37:36 +000081#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000082#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000083#endif
84
Eric Andersencb57d552001-06-28 07:25:16 +000085#include "busybox.h"
Eric Andersen2870d962001-07-02 17:27:21 +000086#include "cmdedit.h"
87
Eric Andersen2870d962001-07-02 17:27:21 +000088/*
89 * This file was generated by the mksyntax program.
90 */
91
92/* Syntax classes */
93#define CWORD 0 /* character is nothing special */
94#define CNL 1 /* newline character */
95#define CBACK 2 /* a backslash character */
96#define CSQUOTE 3 /* single quote */
97#define CDQUOTE 4 /* double quote */
98#define CENDQUOTE 5 /* a terminating quote */
99#define CBQUOTE 6 /* backwards single quote */
100#define CVAR 7 /* a dollar sign */
101#define CENDVAR 8 /* a '}' character */
102#define CLP 9 /* a left paren in arithmetic */
103#define CRP 10 /* a right paren in arithmetic */
104#define CENDFILE 11 /* end of file */
105#define CCTL 12 /* like CWORD, except it must be escaped */
106#define CSPCL 13 /* these terminate a word */
107#define CIGN 14 /* character should be ignored */
108
Eric Andersen2870d962001-07-02 17:27:21 +0000109#define SYNBASE 130
110#define PEOF -130
111
112#define PEOA -129
113
114#define TEOF 0
115#define TNL 1
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000116#define TREDIR 2
117#define TWORD 3
118#define TASSIGN 4
119#define TSEMI 5
120#define TBACKGND 6
121#define TAND 7
122#define TOR 8
123#define TPIPE 9
124#define TLP 10
125#define TRP 11
126#define TENDCASE 12
127#define TENDBQUOTE 13
Eric Andersen2870d962001-07-02 17:27:21 +0000128#define TNOT 14
129#define TCASE 15
130#define TDO 16
131#define TDONE 17
132#define TELIF 18
133#define TELSE 19
134#define TESAC 20
135#define TFI 21
136#define TFOR 22
137#define TIF 23
138#define TIN 24
139#define TTHEN 25
140#define TUNTIL 26
141#define TWHILE 27
142#define TBEGIN 28
143#define TEND 29
144
145
Eric Andersen2870d962001-07-02 17:27:21 +0000146
147/* control characters in argument strings */
148#define CTLESC '\201'
149#define CTLVAR '\202'
150#define CTLENDVAR '\203'
151#define CTLBACKQ '\204'
152#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
153/* CTLBACKQ | CTLQUOTE == '\205' */
154#define CTLARI '\206'
155#define CTLENDARI '\207'
156#define CTLQUOTEMARK '\210'
157
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000158
Eric Andersen62483552001-07-10 06:09:16 +0000159#define is_digit(c) ((c)>='0' && (c)<='9')
Eric Andersen2870d962001-07-02 17:27:21 +0000160#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
161#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000162
163/*
164 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
165 * (assuming ascii char codes, as the original implementation did)
166 */
167#define is_special(c) \
168 ( (((unsigned int)c) - 33 < 32) \
169 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
170
Eric Andersen2870d962001-07-02 17:27:21 +0000171#define digit_val(c) ((c) - '0')
Eric Andersencb57d552001-06-28 07:25:16 +0000172
173
Eric Andersen2870d962001-07-02 17:27:21 +0000174#define S_DFL 1 /* default signal handling (SIG_DFL) */
175#define S_CATCH 2 /* signal is caught */
176#define S_IGN 3 /* signal is ignored (SIG_IGN) */
177#define S_HARD_IGN 4 /* signal is ignored permenantly */
178#define S_RESET 5 /* temporary - to reset a hard ignored sig */
Eric Andersencb57d552001-06-28 07:25:16 +0000179
180
Eric Andersen2870d962001-07-02 17:27:21 +0000181/* variable substitution byte (follows CTLVAR) */
182#define VSTYPE 0x0f /* type of variable substitution */
183#define VSNUL 0x10 /* colon--treat the empty string as unset */
184#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
Eric Andersencb57d552001-06-28 07:25:16 +0000185
Eric Andersen2870d962001-07-02 17:27:21 +0000186/* values of VSTYPE field */
187#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
188#define VSMINUS 0x2 /* ${var-text} */
189#define VSPLUS 0x3 /* ${var+text} */
190#define VSQUESTION 0x4 /* ${var?message} */
191#define VSASSIGN 0x5 /* ${var=text} */
192#define VSTRIMLEFT 0x6 /* ${var#pattern} */
193#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
194#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
195#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
196#define VSLENGTH 0xa /* ${#var} */
Eric Andersencb57d552001-06-28 07:25:16 +0000197
Eric Andersen2870d962001-07-02 17:27:21 +0000198/* flags passed to redirect */
199#define REDIR_PUSH 01 /* save previous values of file descriptors */
Eric Andersen3102ac42001-07-06 04:26:23 +0000200#define REDIR_BACKQ 02 /* save the command output to pipe */
Eric Andersencb57d552001-06-28 07:25:16 +0000201
Eric Andersen2870d962001-07-02 17:27:21 +0000202/*
203 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
204 * so we use _setjmp instead.
205 */
206
Eric Andersen62483552001-07-10 06:09:16 +0000207#if defined(BSD)
Eric Andersen2870d962001-07-02 17:27:21 +0000208#define setjmp(jmploc) _setjmp(jmploc)
209#define longjmp(jmploc, val) _longjmp(jmploc, val)
210#endif
211
212/*
213 * Most machines require the value returned from malloc to be aligned
214 * in some way. The following macro will get this right on many machines.
215 */
216
217#ifndef ALIGN
218union align {
219 int i;
220 char *cp;
221};
222
223#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
224#endif
225
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000226#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +0000227#include <locale.h>
228static void change_lc_all(const char *value);
229static void change_lc_ctype(const char *value);
230#endif
231
232/*
233 * These macros allow the user to suspend the handling of interrupt signals
234 * over a period of time. This is similar to SIGHOLD to or sigblock, but
235 * much more efficient and portable. (But hacking the kernel is so much
236 * more fun than worrying about efficiency and portability. :-))
237 */
238
239static void onint (void);
240static volatile int suppressint;
241static volatile int intpending;
242
243#define INTOFF suppressint++
Eric Andersend35c5df2002-01-09 15:37:36 +0000244#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +0000245#define INTON { if (--suppressint == 0 && intpending) onint(); }
Eric Andersen3102ac42001-07-06 04:26:23 +0000246#define FORCEINTON {suppressint = 0; if (intpending) onint();}
Eric Andersen2870d962001-07-02 17:27:21 +0000247#else
248static void __inton (void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000249static void forceinton (void);
Eric Andersen2870d962001-07-02 17:27:21 +0000250#define INTON __inton()
Eric Andersen3102ac42001-07-06 04:26:23 +0000251#define FORCEINTON forceinton()
Eric Andersen2870d962001-07-02 17:27:21 +0000252#endif
Eric Andersen3102ac42001-07-06 04:26:23 +0000253
Eric Andersen2870d962001-07-02 17:27:21 +0000254#define CLEAR_PENDING_INT intpending = 0
255#define int_pending() intpending
256
257
258typedef void *pointer;
259#ifndef NULL
260#define NULL (void *)0
261#endif
262
Eric Andersen2870d962001-07-02 17:27:21 +0000263static pointer stalloc (int);
264static void stunalloc (pointer);
265static void ungrabstackstr (char *, char *);
266static char * growstackstr(void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000267static char * makestrspace(size_t newlen);
Eric Andersen2870d962001-07-02 17:27:21 +0000268static char *sstrdup (const char *);
269
270/*
271 * Parse trees for commands are allocated in lifo order, so we use a stack
272 * to make this more efficient, and also to avoid all sorts of exception
273 * handling code to handle interrupts in the middle of a parse.
274 *
275 * The size 504 was chosen because the Ultrix malloc handles that size
276 * well.
277 */
278
279#define MINSIZE 504 /* minimum size of a block */
280
281
282struct stack_block {
283 struct stack_block *prev;
284 char space[MINSIZE];
285};
286
287static struct stack_block stackbase;
288static struct stack_block *stackp = &stackbase;
289static struct stackmark *markp;
290static char *stacknxt = stackbase.space;
291static int stacknleft = MINSIZE;
292
293
294#define equal(s1, s2) (strcmp(s1, s2) == 0)
295
296#define stackblock() stacknxt
297#define stackblocksize() stacknleft
298#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
Eric Andersen3102ac42001-07-06 04:26:23 +0000299
Eric Andersen2870d962001-07-02 17:27:21 +0000300#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
301#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); }
Eric Andersen2870d962001-07-02 17:27:21 +0000302#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
Eric Andersen3102ac42001-07-06 04:26:23 +0000303
304
305#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
Eric Andersen2870d962001-07-02 17:27:21 +0000306#define STUNPUTC(p) (++sstrnleft, --p)
307#define STTOPC(p) p[-1]
308#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
309#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
310
311#define ckfree(p) free((pointer)(p))
312
Eric Andersen2870d962001-07-02 17:27:21 +0000313
314#ifdef DEBUG
315#define TRACE(param) trace param
Eric Andersen69a20f02001-10-31 10:40:37 +0000316typedef union node unode;
Eric Andersen2870d962001-07-02 17:27:21 +0000317static void trace (const char *, ...);
318static void trargs (char **);
Eric Andersen69a20f02001-10-31 10:40:37 +0000319static void showtree (unode *);
Eric Andersen2870d962001-07-02 17:27:21 +0000320static void trputc (int);
321static void trputs (const char *);
322static void opentrace (void);
323#else
324#define TRACE(param)
325#endif
326
327#define NSEMI 0
328#define NCMD 1
329#define NPIPE 2
330#define NREDIR 3
331#define NBACKGND 4
332#define NSUBSHELL 5
333#define NAND 6
334#define NOR 7
335#define NIF 8
336#define NWHILE 9
337#define NUNTIL 10
338#define NFOR 11
339#define NCASE 12
340#define NCLIST 13
341#define NDEFUN 14
342#define NARG 15
343#define NTO 16
344#define NFROM 17
345#define NFROMTO 18
346#define NAPPEND 19
347#define NTOOV 20
348#define NTOFD 21
349#define NFROMFD 22
350#define NHERE 23
351#define NXHERE 24
352#define NNOT 25
353
354/*
355 * expandarg() flags
356 */
357#define EXP_FULL 0x1 /* perform word splitting & file globbing */
358#define EXP_TILDE 0x2 /* do normal tilde expansion */
359#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
360#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
361#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
362#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
363
364
365#define NOPTS 16
366
367static char optet_vals[NOPTS];
368
369static const char * const optlist[NOPTS] = {
370 "e" "errexit",
371 "f" "noglob",
372 "I" "ignoreeof",
373 "i" "interactive",
374 "m" "monitor",
375 "n" "noexec",
376 "s" "stdin",
377 "x" "xtrace",
378 "v" "verbose",
379 "V" "vi",
380 "E" "emacs",
381 "C" "noclobber",
382 "a" "allexport",
383 "b" "notify",
384 "u" "nounset",
385 "q" "quietprofile"
386};
387
388#define optent_name(optent) (optent+1)
389#define optent_letter(optent) optent[0]
390#define optent_val(optent) optet_vals[optent]
391
392#define eflag optent_val(0)
393#define fflag optent_val(1)
394#define Iflag optent_val(2)
395#define iflag optent_val(3)
396#define mflag optent_val(4)
397#define nflag optent_val(5)
398#define sflag optent_val(6)
399#define xflag optent_val(7)
400#define vflag optent_val(8)
401#define Vflag optent_val(9)
402#define Eflag optent_val(10)
403#define Cflag optent_val(11)
404#define aflag optent_val(12)
405#define bflag optent_val(13)
406#define uflag optent_val(14)
407#define qflag optent_val(15)
408
409
410/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
411#define FORK_FG 0
412#define FORK_BG 1
413#define FORK_NOJOB 2
414
415
416struct nbinary {
417 int type;
418 union node *ch1;
419 union node *ch2;
420};
421
422
423struct ncmd {
424 int type;
425 int backgnd;
426 union node *assign;
427 union node *args;
428 union node *redirect;
429};
430
431
432struct npipe {
433 int type;
434 int backgnd;
435 struct nodelist *cmdlist;
436};
437
438
439struct nredir {
440 int type;
441 union node *n;
442 union node *redirect;
443};
444
445
446struct nif {
447 int type;
448 union node *test;
449 union node *ifpart;
450 union node *elsepart;
451};
452
453
454struct nfor {
455 int type;
456 union node *args;
457 union node *body;
458 char *var;
459};
460
461
462struct ncase {
463 int type;
464 union node *expr;
465 union node *cases;
466};
467
468
469struct nclist {
470 int type;
471 union node *next;
472 union node *pattern;
473 union node *body;
474};
475
476
477struct narg {
478 int type;
479 union node *next;
480 char *text;
481 struct nodelist *backquote;
482};
483
484
485struct nfile {
486 int type;
487 union node *next;
488 int fd;
489 union node *fname;
490 char *expfname;
491};
492
493
494struct ndup {
495 int type;
496 union node *next;
497 int fd;
498 int dupfd;
499 union node *vname;
500};
501
502
503struct nhere {
504 int type;
505 union node *next;
506 int fd;
507 union node *doc;
508};
509
510
511struct nnot {
512 int type;
513 union node *com;
514};
515
516
517union node {
518 int type;
519 struct nbinary nbinary;
520 struct ncmd ncmd;
521 struct npipe npipe;
522 struct nredir nredir;
523 struct nif nif;
524 struct nfor nfor;
525 struct ncase ncase;
526 struct nclist nclist;
527 struct narg narg;
528 struct nfile nfile;
529 struct ndup ndup;
530 struct nhere nhere;
531 struct nnot nnot;
532};
533
534
535struct nodelist {
536 struct nodelist *next;
537 union node *n;
538};
539
540struct backcmd { /* result of evalbackcmd */
541 int fd; /* file descriptor to read from */
542 char *buf; /* buffer */
543 int nleft; /* number of chars in buffer */
544 struct job *jp; /* job structure for command */
545};
546
547struct cmdentry {
548 int cmdtype;
549 union param {
550 int index;
551 union node *func;
552 const struct builtincmd *cmd;
553 } u;
554};
555
556struct strlist {
557 struct strlist *next;
558 char *text;
559};
560
561
562struct arglist {
563 struct strlist *list;
564 struct strlist **lastp;
565};
566
567struct strpush {
568 struct strpush *prev; /* preceding string on stack */
569 char *prevstring;
570 int prevnleft;
Eric Andersend35c5df2002-01-09 15:37:36 +0000571#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +0000572 struct alias *ap; /* if push was associated with an alias */
573#endif
574 char *string; /* remember the string since it may change */
575};
576
577struct parsefile {
578 struct parsefile *prev; /* preceding file on stack */
579 int linno; /* current line */
580 int fd; /* file descriptor (or -1 if string) */
581 int nleft; /* number of chars left in this line */
582 int lleft; /* number of chars left in this buffer */
583 char *nextc; /* next char in buffer */
584 char *buf; /* input buffer */
585 struct strpush *strpush; /* for pushing strings at this level */
586 struct strpush basestrpush; /* so pushing one is fast */
587};
588
589struct stackmark {
590 struct stack_block *stackp;
591 char *stacknxt;
592 int stacknleft;
593 struct stackmark *marknext;
594};
595
596struct shparam {
597 int nparam; /* # of positional parameters (without $0) */
598 unsigned char malloc; /* if parameter list dynamically allocated */
599 char **p; /* parameter list */
600 int optind; /* next parameter to be processed by getopts */
601 int optoff; /* used by getopts */
602};
603
Eric Andersen62483552001-07-10 06:09:16 +0000604/*
605 * When commands are first encountered, they are entered in a hash table.
606 * This ensures that a full path search will not have to be done for them
607 * on each invocation.
608 *
609 * We should investigate converting to a linear search, even though that
610 * would make the command name "hash" a misnomer.
611 */
612#define CMDTABLESIZE 31 /* should be prime */
613#define ARB 1 /* actual size determined at run time */
614
615
616
617struct tblentry {
618 struct tblentry *next; /* next entry in hash chain */
619 union param param; /* definition of builtin function */
620 short cmdtype; /* index identifying command */
621 char rehash; /* if set, cd done since entry created */
622 char cmdname[ARB]; /* name of command */
623};
624
625
626static struct tblentry *cmdtable[CMDTABLESIZE];
627static int builtinloc = -1; /* index in path of %builtin, or -1 */
628static int exerrno = 0; /* Last exec error */
629
630
631static void tryexec (char *, char **, char **);
632static void printentry (struct tblentry *, int);
633static void clearcmdentry (int);
634static struct tblentry *cmdlookup (const char *, int);
635static void delete_cmd_entry (void);
636static int path_change (const char *, int *);
637
638
Eric Andersen2870d962001-07-02 17:27:21 +0000639static void flushall (void);
Eric Andersen3102ac42001-07-06 04:26:23 +0000640static void out2fmt (const char *, ...)
641 __attribute__((__format__(__printf__,1,2)));
Eric Andersen2870d962001-07-02 17:27:21 +0000642static int xwrite (int, const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +0000643
Manuel Novoa III c639a352001-08-12 17:32:56 +0000644static inline void outstr (const char *p, FILE *file) { fputs(p, file); }
Eric Andersen3102ac42001-07-06 04:26:23 +0000645static void out1str(const char *p) { outstr(p, stdout); }
646static void out2str(const char *p) { outstr(p, stderr); }
Eric Andersen2870d962001-07-02 17:27:21 +0000647
Eric Andersend35c5df2002-01-09 15:37:36 +0000648#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen3102ac42001-07-06 04:26:23 +0000649#define out2c(c) putc((c), stderr)
Eric Andersen62483552001-07-10 06:09:16 +0000650#else
651static void out2c(int c) { putc(c, stderr); }
652#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000653
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000654
Eric Andersend35c5df2002-01-09 15:37:36 +0000655#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000656#define USE_SIT_FUNCTION
657#endif
658
659/* number syntax index */
660#define BASESYNTAX 0 /* not in quotes */
661#define DQSYNTAX 1 /* in double quotes */
662#define SQSYNTAX 2 /* in single quotes */
663#define ARISYNTAX 3 /* in arithmetic */
664
665static const char S_I_T[][4] = {
666 /* 0 */ { CSPCL, CIGN, CIGN, CIGN }, /* PEOA */
667 /* 1 */ { CSPCL, CWORD, CWORD, CWORD }, /* ' ' */
668 /* 2 */ { CNL, CNL, CNL, CNL }, /* \n */
669 /* 3 */ { CWORD, CCTL, CCTL, CWORD }, /* !*-/:=?[]~ */
670 /* 4 */ { CDQUOTE, CENDQUOTE, CWORD, CDQUOTE }, /* '"' */
671 /* 5 */ { CVAR, CVAR, CWORD, CVAR }, /* $ */
672 /* 6 */ { CSQUOTE, CWORD, CENDQUOTE, CSQUOTE }, /* "'" */
673 /* 7 */ { CSPCL, CWORD, CWORD, CLP }, /* ( */
674 /* 8 */ { CSPCL, CWORD, CWORD, CRP }, /* ) */
675 /* 9 */ { CBACK, CBACK, CCTL, CBACK }, /* \ */
676 /* 10 */ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* ` */
677 /* 11 */ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* } */
678#ifndef USE_SIT_FUNCTION
679 /* 12 */ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* PEOF */
680 /* 13 */ { CWORD, CWORD, CWORD, CWORD }, /* 0-9A-Za-z */
681 /* 14 */ { CCTL, CCTL, CCTL, CCTL } /* CTLESC ... */
682#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000683};
684
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000685#ifdef USE_SIT_FUNCTION
686
687#define U_C(c) ((unsigned char)(c))
688
689static int SIT(int c, int syntax)
690{
691 static const char spec_symbls[]="\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
692 static const char syntax_index_table [] = {
693 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
694 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
695 3, 1, 3, 3, 9, 3,10, 1, /* "=>?[\\]`|" */
696 11,3 }; /* "}~" */
697 const char *s;
698 int indx;
699
700 if(c==PEOF) /* 2^8+2 */
701 return CENDFILE;
702 if(c==PEOA) /* 2^8+1 */
703 indx = 0;
704 else if(U_C(c)>=U_C(CTLESC) && U_C(c)<=U_C(CTLQUOTEMARK))
705 return CCTL;
706 else {
707 s = strchr(spec_symbls, c);
708 if(s==0)
709 return CWORD;
710 indx = syntax_index_table[(s-spec_symbls)];
711 }
712 return S_I_T[indx][syntax];
713}
714
715#else /* USE_SIT_FUNCTION */
716
717#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
718
719#define CSPCL_CIGN_CIGN_CIGN 0
720#define CSPCL_CWORD_CWORD_CWORD 1
721#define CNL_CNL_CNL_CNL 2
722#define CWORD_CCTL_CCTL_CWORD 3
723#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE 4
724#define CVAR_CVAR_CWORD_CVAR 5
725#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE 6
726#define CSPCL_CWORD_CWORD_CLP 7
727#define CSPCL_CWORD_CWORD_CRP 8
728#define CBACK_CBACK_CCTL_CBACK 9
729#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
730#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
731#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
732#define CWORD_CWORD_CWORD_CWORD 13
733#define CCTL_CCTL_CCTL_CCTL 14
734
735static const char syntax_index_table[258] = {
736 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
737 /* 0 -130 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
738 /* 1 -129 PEOA */ CSPCL_CIGN_CIGN_CIGN,
739 /* 2 -128 0xff */ CWORD_CWORD_CWORD_CWORD,
740 /* 3 -127 */ CCTL_CCTL_CCTL_CCTL, /* CTLQUOTEMARK */
741 /* 4 -126 */ CCTL_CCTL_CCTL_CCTL,
742 /* 5 -125 */ CCTL_CCTL_CCTL_CCTL,
743 /* 6 -124 */ CCTL_CCTL_CCTL_CCTL,
744 /* 7 -123 */ CCTL_CCTL_CCTL_CCTL,
745 /* 8 -122 */ CCTL_CCTL_CCTL_CCTL,
746 /* 9 -121 */ CCTL_CCTL_CCTL_CCTL,
747 /* 10 -120 */ CCTL_CCTL_CCTL_CCTL, /* CTLESC */
748 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
749 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
750 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
751 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
752 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
753 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
754 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
755 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
756 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
757 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
758 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
759 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
760 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
761 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
762 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
763 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
764 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
765 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
766 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
767 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
768 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
769 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
770 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
771 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
772 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
773 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
774 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
775 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
776 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
777 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
778 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
779 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
780 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
781 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
782 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
783 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
784 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
785 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
786 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
787 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
788 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
789 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
790 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
791 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
792 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
793 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
794 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
795 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
796 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
797 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
798 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
799 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
800 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
801 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
802 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
803 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
804 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
805 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
806 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
807 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
808 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
809 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
810 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
811 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
812 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
813 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
814 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
815 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
816 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
817 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
818 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
819 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
820 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
821 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
822 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
823 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
824 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
825 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
826 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
827 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
828 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
829 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
830 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
831 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
832 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
833 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
834 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
835 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
836 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
837 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
838 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
839 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
840 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
841 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
842 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
843 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
844 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
845 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
846 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
847 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
848 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
849 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
850 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
851 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
852 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
853 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
854 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
855 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
856 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
857 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
858 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
859 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
860 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
861 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
862 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
863 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
864 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
865 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
866 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
867 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
868 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
869 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
870 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
871 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
872 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
873 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
874 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
875 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
876 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
877 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
878 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
879 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
880 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
881 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
882 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
883 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
884 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
885 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
886 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
887 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
888 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
889 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
890 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
891 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
892 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
893 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
894 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
895 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
896 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
897 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
898 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
899 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
900 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
901 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CDQUOTE,
902 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
903 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
904 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
905 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
906 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CSQUOTE,
907 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
908 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
909 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
910 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
911 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
912 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
913 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
914 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
915 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
916 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
917 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
918 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
919 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
920 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
921 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
922 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
923 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
924 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
925 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
926 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
927 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
928 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
929 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
930 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
931 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
932 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
933 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
934 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
935 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
936 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
937 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
938 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
939 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
940 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
941 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
942 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
943 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
944 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
945 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
946 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
947 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
948 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
949 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
950 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
951 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
952 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
953 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
954 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
955 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
956 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
957 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
958 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
959 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
960 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
961 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
962 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
963 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
964 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
965 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
966 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
967 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
968 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
969 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
970 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
971 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
972 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
973 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
974 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
975 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
976 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
977 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
978 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
979 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
980 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
981 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
982 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
983 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
984 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
985 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
986 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
987 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
988 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
989 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
990 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
991 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
992 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
993 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
994 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +0000995};
996
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000997#endif /* USE_SIT_FUNCTION */
Eric Andersen2870d962001-07-02 17:27:21 +0000998
Eric Andersen2870d962001-07-02 17:27:21 +0000999
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001000/* first char is indicating which tokens mark the end of a list */
1001static const char *const tokname_array[] = {
1002 "\1end of file",
1003 "\0newline",
1004 "\0redirection",
1005 "\0word",
1006 "\0assignment",
1007 "\0;",
1008 "\0&",
1009 "\0&&",
1010 "\0||",
1011 "\0|",
1012 "\0(",
1013 "\1)",
1014 "\1;;",
1015 "\1`",
Eric Andersen2870d962001-07-02 17:27:21 +00001016#define KWDOFFSET 14
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001017 /* the following are keywords */
1018 "\0!",
1019 "\0case",
1020 "\1do",
1021 "\1done",
1022 "\1elif",
1023 "\1else",
1024 "\1esac",
1025 "\1fi",
1026 "\0for",
1027 "\0if",
1028 "\0in",
1029 "\1then",
1030 "\0until",
1031 "\0while",
1032 "\0{",
1033 "\1}",
Eric Andersen2870d962001-07-02 17:27:21 +00001034};
1035
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001036static const char *tokname(int tok)
1037{
1038 static char buf[16];
1039
1040 if(tok>=TSEMI)
1041 buf[0] = '"';
1042 sprintf(buf+(tok>=TSEMI), "%s%c",
1043 tokname_array[tok]+1, (tok>=TSEMI ? '"' : 0));
1044 return buf;
1045}
Eric Andersen2870d962001-07-02 17:27:21 +00001046
1047static int plinno = 1; /* input line number */
1048
1049static int parselleft; /* copy of parsefile->lleft */
1050
1051static struct parsefile basepf; /* top level input file */
1052static char basebuf[BUFSIZ]; /* buffer for top level input file */
1053static struct parsefile *parsefile = &basepf; /* current input file */
1054
1055/*
1056 * NEOF is returned by parsecmd when it encounters an end of file. It
1057 * must be distinct from NULL, so we use the address of a variable that
1058 * happens to be handy.
1059 */
1060
1061static int tokpushback; /* last token pushed back */
1062#define NEOF ((union node *)&tokpushback)
1063static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
1064
1065
1066static void error (const char *, ...) __attribute__((__noreturn__));
1067static void exerror (int, const char *, ...) __attribute__((__noreturn__));
1068static void shellexec (char **, char **, const char *, int)
1069 __attribute__((noreturn));
1070static void exitshell (int) __attribute__((noreturn));
1071
1072static int goodname(const char *);
1073static void ignoresig (int);
1074static void onsig (int);
1075static void dotrap (void);
1076static int decode_signal (const char *, int);
1077
1078static void shprocvar(void);
1079static void deletefuncs(void);
1080static void setparam (char **);
1081static void freeparam (volatile struct shparam *);
1082
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001083static void find_command (const char *, struct cmdentry *, int, const char *);
1084
1085static inline void hashcd (void);
1086
Eric Andersen2870d962001-07-02 17:27:21 +00001087/* reasons for skipping commands (see comment on breakcmd routine) */
1088#define SKIPBREAK 1
1089#define SKIPCONT 2
1090#define SKIPFUNC 3
1091#define SKIPFILE 4
1092
1093/* values of cmdtype */
1094#define CMDUNKNOWN -1 /* no entry in table for command */
1095#define CMDNORMAL 0 /* command is an executable program */
1096#define CMDBUILTIN 1 /* command is a shell builtin */
1097#define CMDFUNCTION 2 /* command is a shell function */
1098
1099#define DO_ERR 1 /* find_command prints errors */
1100#define DO_ABS 2 /* find_command checks absolute paths */
1101#define DO_NOFUN 4 /* find_command ignores functions */
1102#define DO_BRUTE 8 /* find_command ignores hash table */
1103
1104/*
1105 * Shell variables.
1106 */
1107
1108/* flags */
1109#define VEXPORT 0x01 /* variable is exported */
1110#define VREADONLY 0x02 /* variable cannot be modified */
1111#define VSTRFIXED 0x04 /* variable struct is staticly allocated */
1112#define VTEXTFIXED 0x08 /* text is staticly allocated */
1113#define VSTACK 0x10 /* text is allocated on the stack */
1114#define VUNSET 0x20 /* the variable is not set */
1115#define VNOFUNC 0x40 /* don't call the callback function */
1116
1117
1118struct var {
1119 struct var *next; /* next entry in hash list */
1120 int flags; /* flags are defined above */
1121 char *text; /* name=value */
1122 void (*func) (const char *);
1123 /* function to be called when */
1124 /* the variable gets set/unset */
1125};
1126
1127struct localvar {
1128 struct localvar *next; /* next local variable in list */
1129 struct var *vp; /* the variable that was made local */
1130 int flags; /* saved flags */
1131 char *text; /* saved text */
1132};
1133
1134
Eric Andersen62483552001-07-10 06:09:16 +00001135#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00001136#define rmescapes(p) _rmescapes((p), 0)
1137static char *_rmescapes (char *, int);
1138#else
1139static void rmescapes (char *);
1140#endif
1141
1142static int casematch (union node *, const char *);
1143static void clearredir(void);
1144static void popstring(void);
1145static void readcmdfile (const char *);
1146
1147static int number (const char *);
1148static int is_number (const char *, int *num);
1149static char *single_quote (const char *);
1150static int nextopt (const char *);
1151
1152static void redirect (union node *, int);
1153static void popredir (void);
1154static int dup_as_newfd (int, int);
1155
1156static void changepath(const char *newval);
1157static void getoptsreset(const char *value);
1158
1159
1160static int parsenleft; /* copy of parsefile->nleft */
1161static char *parsenextc; /* copy of parsefile->nextc */
1162static int rootpid; /* pid of main shell */
1163static int rootshell; /* true if we aren't a child of the main shell */
1164
1165static const char spcstr[] = " ";
1166static const char snlfmt[] = "%s\n";
1167
1168static int sstrnleft;
1169static int herefd = -1;
1170
1171static struct localvar *localvars;
1172
1173static struct var vifs;
1174static struct var vmail;
1175static struct var vmpath;
1176static struct var vpath;
1177static struct var vps1;
1178static struct var vps2;
1179static struct var voptind;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001180#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00001181static struct var vlc_all;
1182static struct var vlc_ctype;
1183#endif
1184
1185struct varinit {
1186 struct var *var;
1187 int flags;
1188 const char *text;
1189 void (*func) (const char *);
1190};
1191
1192static const char defpathvar[] =
1193 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
1194#define defpath (defpathvar + 5)
1195
1196#ifdef IFS_BROKEN
1197static const char defifsvar[] = "IFS= \t\n";
1198#define defifs (defifsvar + 4)
1199#else
1200static const char defifs[] = " \t\n";
1201#endif
1202
1203static const struct varinit varinit[] = {
1204#ifdef IFS_BROKEN
1205 { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar,
1206#else
1207 { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=",
1208#endif
1209 NULL },
1210 { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
1211 NULL },
1212 { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
1213 NULL },
1214 { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar,
1215 changepath },
Tim Riker497a8852002-04-13 05:37:10 +00001216#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1217 { &vps1, VSTRFIXED|VTEXTFIXED, "PS1=\\w \\$ ",
1218 NULL },
1219#endif /* else vps1 depends on uid */
Eric Andersen2870d962001-07-02 17:27:21 +00001220 { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
1221 NULL },
1222 { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1",
1223 getoptsreset },
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001224#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00001225 { &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=",
1226 change_lc_all },
1227 { &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=",
1228 change_lc_ctype },
1229#endif
1230 { NULL, 0, NULL,
1231 NULL }
1232};
1233
1234#define VTABSIZE 39
1235
1236static struct var *vartab[VTABSIZE];
1237
1238/*
1239 * The following macros access the values of the above variables.
1240 * They have to skip over the name. They return the null string
1241 * for unset variables.
1242 */
1243
1244#define ifsval() (vifs.text + 4)
1245#define ifsset() ((vifs.flags & VUNSET) == 0)
1246#define mailval() (vmail.text + 5)
1247#define mpathval() (vmpath.text + 9)
1248#define pathval() (vpath.text + 5)
1249#define ps1val() (vps1.text + 4)
1250#define ps2val() (vps2.text + 4)
1251#define optindval() (voptind.text + 7)
1252
1253#define mpathset() ((vmpath.flags & VUNSET) == 0)
1254
1255static void initvar (void);
1256static void setvar (const char *, const char *, int);
1257static void setvareq (char *, int);
1258static void listsetvar (struct strlist *);
Eric Andersen62483552001-07-10 06:09:16 +00001259static const char *lookupvar (const char *);
1260static const char *bltinlookup (const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001261static char **environment (void);
1262static int showvarscmd (int, char **);
1263static void mklocal (char *);
1264static void poplocalvars (void);
1265static int unsetvar (const char *);
1266static int varequal (const char *, const char *);
1267
1268
1269static char *arg0; /* value of $0 */
1270static struct shparam shellparam; /* current positional parameters */
1271static char **argptr; /* argument list for builtin commands */
1272static char *optionarg; /* set by nextopt (like getopt) */
1273static char *optptr; /* used by nextopt */
1274static char *minusc; /* argument to -c option */
1275
1276
Eric Andersend35c5df2002-01-09 15:37:36 +00001277#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00001278
1279#define ALIASINUSE 1
1280#define ALIASDEAD 2
1281
Eric Andersen3102ac42001-07-06 04:26:23 +00001282#define ATABSIZE 39
1283
Eric Andersen2870d962001-07-02 17:27:21 +00001284struct alias {
1285 struct alias *next;
1286 char *name;
1287 char *val;
1288 int flag;
1289};
1290
1291static struct alias *atab[ATABSIZE];
1292
1293static void setalias (char *, char *);
1294static struct alias **hashalias (const char *);
1295static struct alias *freealias (struct alias *);
1296static struct alias **__lookupalias (const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00001297
1298static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001299setalias(char *name, char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00001300{
1301 struct alias *ap, **app;
1302
1303 app = __lookupalias(name);
1304 ap = *app;
1305 INTOFF;
1306 if (ap) {
1307 if (!(ap->flag & ALIASINUSE)) {
1308 ckfree(ap->val);
1309 }
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001310 ap->val = xstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00001311 ap->flag &= ~ALIASDEAD;
1312 } else {
1313 /* not found */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001314 ap = xmalloc(sizeof (struct alias));
1315 ap->name = xstrdup(name);
1316 ap->val = xstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00001317 ap->flag = 0;
1318 ap->next = 0;
1319 *app = ap;
1320 }
1321 INTON;
1322}
1323
1324static int
Eric Andersen2870d962001-07-02 17:27:21 +00001325unalias(char *name)
1326{
Eric Andersencb57d552001-06-28 07:25:16 +00001327 struct alias **app;
1328
1329 app = __lookupalias(name);
1330
1331 if (*app) {
1332 INTOFF;
1333 *app = freealias(*app);
1334 INTON;
1335 return (0);
1336 }
1337
1338 return (1);
1339}
1340
Eric Andersencb57d552001-06-28 07:25:16 +00001341static void
Eric Andersen2870d962001-07-02 17:27:21 +00001342rmaliases(void)
1343{
Eric Andersencb57d552001-06-28 07:25:16 +00001344 struct alias *ap, **app;
1345 int i;
1346
1347 INTOFF;
1348 for (i = 0; i < ATABSIZE; i++) {
1349 app = &atab[i];
1350 for (ap = *app; ap; ap = *app) {
1351 *app = freealias(*app);
1352 if (ap == *app) {
1353 app = &ap->next;
1354 }
1355 }
1356 }
1357 INTON;
1358}
1359
Eric Andersen2870d962001-07-02 17:27:21 +00001360static void
1361printalias(const struct alias *ap) {
1362 char *p;
1363
1364 p = single_quote(ap->val);
Eric Andersen62483552001-07-10 06:09:16 +00001365 printf("alias %s=%s\n", ap->name, p);
Eric Andersen2870d962001-07-02 17:27:21 +00001366 stunalloc(p);
1367}
1368
Eric Andersencb57d552001-06-28 07:25:16 +00001369
1370/*
1371 * TODO - sort output
1372 */
1373static int
Eric Andersen2870d962001-07-02 17:27:21 +00001374aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001375{
1376 char *n, *v;
1377 int ret = 0;
1378 struct alias *ap;
1379
1380 if (argc == 1) {
1381 int i;
1382
1383 for (i = 0; i < ATABSIZE; i++)
1384 for (ap = atab[i]; ap; ap = ap->next) {
1385 printalias(ap);
1386 }
1387 return (0);
1388 }
1389 while ((n = *++argv) != NULL) {
1390 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
1391 if ((ap = *__lookupalias(n)) == NULL) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001392 out2fmt("%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00001393 ret = 1;
1394 } else
1395 printalias(ap);
1396 }
1397 else {
1398 *v++ = '\0';
1399 setalias(n, v);
1400 }
1401 }
1402
1403 return (ret);
1404}
1405
1406static int
Eric Andersen2870d962001-07-02 17:27:21 +00001407unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001408{
1409 int i;
1410
1411 while ((i = nextopt("a")) != '\0') {
1412 if (i == 'a') {
1413 rmaliases();
1414 return (0);
1415 }
1416 }
1417 for (i = 0; *argptr; argptr++) {
1418 if (unalias(*argptr)) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001419 out2fmt("%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00001420 i = 1;
1421 }
1422 }
1423
1424 return (i);
1425}
1426
1427static struct alias **
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001428hashalias(const char *p)
1429{
Eric Andersencb57d552001-06-28 07:25:16 +00001430 unsigned int hashval;
1431
1432 hashval = *p << 4;
1433 while (*p)
1434 hashval+= *p++;
1435 return &atab[hashval % ATABSIZE];
1436}
1437
1438static struct alias *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001439freealias(struct alias *ap)
1440{
Eric Andersencb57d552001-06-28 07:25:16 +00001441 struct alias *next;
1442
1443 if (ap->flag & ALIASINUSE) {
1444 ap->flag |= ALIASDEAD;
1445 return ap;
1446 }
1447
1448 next = ap->next;
1449 ckfree(ap->name);
1450 ckfree(ap->val);
1451 ckfree(ap);
1452 return next;
1453}
1454
Eric Andersencb57d552001-06-28 07:25:16 +00001455
1456static struct alias **
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001457__lookupalias(const char *name)
1458{
Eric Andersencb57d552001-06-28 07:25:16 +00001459 struct alias **app = hashalias(name);
1460
1461 for (; *app; app = &(*app)->next) {
1462 if (equal(name, (*app)->name)) {
1463 break;
1464 }
1465 }
1466
1467 return app;
1468}
Eric Andersen2870d962001-07-02 17:27:21 +00001469#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001470
Eric Andersend35c5df2002-01-09 15:37:36 +00001471#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersen74bcd162001-07-30 21:41:37 +00001472/* The generated file arith.c has been replaced with a custom hand
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001473 * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
1474 * This is now part of libbb, so that it can be used by all the shells
Eric Andersen74bcd162001-07-30 21:41:37 +00001475 * in busybox. */
Eric Andersen2870d962001-07-02 17:27:21 +00001476static void expari (int);
Eric Andersencb57d552001-06-28 07:25:16 +00001477#endif
1478
Eric Andersen2870d962001-07-02 17:27:21 +00001479static char *trap[NSIG]; /* trap handler commands */
1480static char sigmode[NSIG - 1]; /* current value of signal */
1481static char gotsig[NSIG - 1]; /* indicates specified signal received */
1482static int pendingsigs; /* indicates some signal received */
1483
Eric Andersencb57d552001-06-28 07:25:16 +00001484/*
1485 * This file was generated by the mkbuiltins program.
1486 */
1487
Eric Andersend35c5df2002-01-09 15:37:36 +00001488#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001489static int bgcmd (int, char **);
1490static int fgcmd (int, char **);
1491static int killcmd (int, char **);
1492#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001493static int bltincmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001494static int cdcmd (int, char **);
1495static int breakcmd (int, char **);
Eric Andersend35c5df2002-01-09 15:37:36 +00001496#ifdef CONFIG_ASH_CMDCMD
Eric Andersen2870d962001-07-02 17:27:21 +00001497static int commandcmd (int, char **);
1498#endif
1499static int dotcmd (int, char **);
1500static int evalcmd (int, char **);
1501static int execcmd (int, char **);
1502static int exitcmd (int, char **);
1503static int exportcmd (int, char **);
1504static int histcmd (int, char **);
1505static int hashcmd (int, char **);
Eric Andersen1c039232001-07-07 00:05:55 +00001506static int helpcmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001507static int jobscmd (int, char **);
1508static int localcmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001509static int pwdcmd (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001510static int readcmd (int, char **);
1511static int returncmd (int, char **);
1512static int setcmd (int, char **);
1513static int setvarcmd (int, char **);
1514static int shiftcmd (int, char **);
1515static int trapcmd (int, char **);
1516static int umaskcmd (int, char **);
Eric Andersend35c5df2002-01-09 15:37:36 +00001517#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00001518static int aliascmd (int, char **);
1519static int unaliascmd (int, char **);
1520#endif
1521static int unsetcmd (int, char **);
1522static int waitcmd (int, char **);
1523static int ulimitcmd (int, char **);
1524static int timescmd (int, char **);
Eric Andersend35c5df2002-01-09 15:37:36 +00001525#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +00001526static int letcmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001527#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001528static int typecmd (int, char **);
Eric Andersend35c5df2002-01-09 15:37:36 +00001529#ifdef CONFIG_ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00001530static int getoptscmd (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001531#endif
1532
Eric Andersen69a20f02001-10-31 10:40:37 +00001533#ifndef CONFIG_TRUE
Eric Andersen2870d962001-07-02 17:27:21 +00001534static int true_main (int, char **);
Eric Andersen69a20f02001-10-31 10:40:37 +00001535#endif
1536#ifndef CONFIG_FALSE
Eric Andersen2870d962001-07-02 17:27:21 +00001537static int false_main (int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001538#endif
1539
1540static void setpwd (const char *, int);
1541
1542
1543#define BUILTIN_NOSPEC "0"
1544#define BUILTIN_SPECIAL "1"
1545#define BUILTIN_REGULAR "2"
1546#define BUILTIN_ASSIGN "4"
1547#define BUILTIN_SPEC_ASSG "5"
1548#define BUILTIN_REG_ASSG "6"
1549
1550#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1551#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1552#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
1553
1554struct builtincmd {
1555 const char *name;
1556 int (*const builtinfunc) (int, char **);
1557 //unsigned flags;
1558};
1559
Eric Andersencb57d552001-06-28 07:25:16 +00001560
1561/* It is CRUCIAL that this listing be kept in ascii order, otherwise
1562 * the binary search in find_builtin() will stop working. If you value
1563 * your kneecaps, you'll be sure to *make sure* that any changes made
1564 * to this array result in the listing remaining in ascii order. You
1565 * have been warned.
1566 */
1567static const struct builtincmd builtincmds[] = {
Eric Andersen62483552001-07-10 06:09:16 +00001568 { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */
Eric Andersen2870d962001-07-02 17:27:21 +00001569 { BUILTIN_SPECIAL ":", true_main },
Eric Andersend35c5df2002-01-09 15:37:36 +00001570#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00001571 { BUILTIN_REG_ASSG "alias", aliascmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001572#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001573#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001574 { BUILTIN_REGULAR "bg", bgcmd },
1575#endif
1576 { BUILTIN_SPECIAL "break", breakcmd },
Eric Andersen3102ac42001-07-06 04:26:23 +00001577 { BUILTIN_SPECIAL "builtin", bltincmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001578 { BUILTIN_REGULAR "cd", cdcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001579 { BUILTIN_NOSPEC "chdir", cdcmd },
Eric Andersend35c5df2002-01-09 15:37:36 +00001580#ifdef CONFIG_ASH_CMDCMD
Eric Andersen2870d962001-07-02 17:27:21 +00001581 { BUILTIN_REGULAR "command", commandcmd },
1582#endif
1583 { BUILTIN_SPECIAL "continue", breakcmd },
1584 { BUILTIN_SPECIAL "eval", evalcmd },
1585 { BUILTIN_SPECIAL "exec", execcmd },
1586 { BUILTIN_SPECIAL "exit", exitcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001587 { BUILTIN_SPEC_ASSG "export", exportcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001588 { BUILTIN_REGULAR "false", false_main },
Eric Andersen2870d962001-07-02 17:27:21 +00001589 { BUILTIN_REGULAR "fc", histcmd },
Eric Andersend35c5df2002-01-09 15:37:36 +00001590#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001591 { BUILTIN_REGULAR "fg", fgcmd },
1592#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001593#ifdef CONFIG_ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00001594 { BUILTIN_REGULAR "getopts", getoptscmd },
1595#endif
1596 { BUILTIN_NOSPEC "hash", hashcmd },
Eric Andersen1c039232001-07-07 00:05:55 +00001597 { BUILTIN_NOSPEC "help", helpcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001598 { BUILTIN_REGULAR "jobs", jobscmd },
Eric Andersend35c5df2002-01-09 15:37:36 +00001599#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001600 { BUILTIN_REGULAR "kill", killcmd },
1601#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001602#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +00001603 { BUILTIN_REGULAR "let", letcmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001604#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001605 { BUILTIN_ASSIGN "local", localcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001606 { BUILTIN_NOSPEC "pwd", pwdcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001607 { BUILTIN_REGULAR "read", readcmd },
1608 { BUILTIN_SPEC_ASSG "readonly", exportcmd },
1609 { BUILTIN_SPECIAL "return", returncmd },
1610 { BUILTIN_SPECIAL "set", setcmd },
1611 { BUILTIN_NOSPEC "setvar", setvarcmd },
1612 { BUILTIN_SPECIAL "shift", shiftcmd },
1613 { BUILTIN_SPECIAL "times", timescmd },
1614 { BUILTIN_SPECIAL "trap", trapcmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001615 { BUILTIN_REGULAR "true", true_main },
Eric Andersen2870d962001-07-02 17:27:21 +00001616 { BUILTIN_NOSPEC "type", typecmd },
Eric Andersen2870d962001-07-02 17:27:21 +00001617 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
1618 { BUILTIN_REGULAR "umask", umaskcmd },
Eric Andersend35c5df2002-01-09 15:37:36 +00001619#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00001620 { BUILTIN_REGULAR "unalias", unaliascmd },
1621#endif
1622 { BUILTIN_SPECIAL "unset", unsetcmd },
1623 { BUILTIN_REGULAR "wait", waitcmd },
Eric Andersencb57d552001-06-28 07:25:16 +00001624};
1625#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
1626
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001627#define DOTCMD &builtincmds[0]
Eric Andersen2870d962001-07-02 17:27:21 +00001628static struct builtincmd *BLTINCMD;
1629static struct builtincmd *EXECCMD;
1630static struct builtincmd *EVALCMD;
Eric Andersencb57d552001-06-28 07:25:16 +00001631
Eric Andersen2870d962001-07-02 17:27:21 +00001632/* states */
Eric Andersend35c5df2002-01-09 15:37:36 +00001633#define CONFIG_ASH_JOB_CONTROLTOPPED 1 /* all procs are stopped */
Eric Andersen2870d962001-07-02 17:27:21 +00001634#define JOBDONE 2 /* all procs are completed */
Eric Andersencb57d552001-06-28 07:25:16 +00001635
Eric Andersen2870d962001-07-02 17:27:21 +00001636/*
1637 * A job structure contains information about a job. A job is either a
1638 * single process or a set of processes contained in a pipeline. In the
1639 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1640 * array of pids.
1641 */
Eric Andersencb57d552001-06-28 07:25:16 +00001642
Eric Andersen2870d962001-07-02 17:27:21 +00001643struct procstat {
1644 pid_t pid; /* process id */
1645 int status; /* status flags (defined above) */
1646 char *cmd; /* text of command being run */
1647};
Eric Andersencb57d552001-06-28 07:25:16 +00001648
Eric Andersen2870d962001-07-02 17:27:21 +00001649
1650static int job_warning; /* user was warned about stopped jobs */
1651
Eric Andersend35c5df2002-01-09 15:37:36 +00001652#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001653static void setjobctl(int enable);
1654#else
1655#define setjobctl(on) /* do nothing */
Eric Andersencb57d552001-06-28 07:25:16 +00001656#endif
1657
Eric Andersen2870d962001-07-02 17:27:21 +00001658
1659struct job {
1660 struct procstat ps0; /* status of process */
1661 struct procstat *ps; /* status or processes when more than one */
1662 short nprocs; /* number of processes */
1663 short pgrp; /* process group of this job */
1664 char state; /* true if job is finished */
1665 char used; /* true if this entry is in used */
1666 char changed; /* true if status has changed */
Eric Andersend35c5df2002-01-09 15:37:36 +00001667#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001668 char jobctl; /* job running under job control */
1669#endif
1670};
1671
1672static struct job *jobtab; /* array of jobs */
1673static int njobs; /* size of array */
1674static int backgndpid = -1; /* pid of last background process */
Eric Andersend35c5df2002-01-09 15:37:36 +00001675#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001676static int initialpgrp; /* pgrp of shell on invocation */
1677static int curjob; /* current job */
1678static int jobctl;
1679#endif
1680static int intreceived;
1681
Eric Andersen62483552001-07-10 06:09:16 +00001682static struct job *makejob (const union node *, int);
1683static int forkshell (struct job *, const union node *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001684static int waitforjob (struct job *);
1685
1686static int docd (char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001687static void getpwd (void);
1688
1689static char *padvance (const char **, const char *);
1690
1691static char nullstr[1]; /* zero length string */
1692static char *curdir = nullstr; /* current working directory */
Eric Andersen2870d962001-07-02 17:27:21 +00001693
Eric Andersencb57d552001-06-28 07:25:16 +00001694static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001695cdcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001696{
1697 const char *dest;
1698 const char *path;
1699 char *p;
1700 struct stat statb;
1701 int print = 0;
1702
1703 nextopt(nullstr);
1704 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
1705 error("HOME not set");
1706 if (*dest == '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00001707 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001708 if (dest[0] == '-' && dest[1] == '\0') {
1709 dest = bltinlookup("OLDPWD");
1710 if (!dest || !*dest) {
1711 dest = curdir;
1712 }
1713 print = 1;
1714 if (dest)
Eric Andersen2870d962001-07-02 17:27:21 +00001715 print = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00001716 else
Eric Andersen2870d962001-07-02 17:27:21 +00001717 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001718 }
1719 if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
1720 path = nullstr;
1721 while ((p = padvance(&path, dest)) != NULL) {
1722 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1723 if (!print) {
1724 /*
1725 * XXX - rethink
1726 */
1727 if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
1728 p += 2;
1729 print = strcmp(p, dest);
1730 }
1731 if (docd(p, print) >= 0)
1732 return 0;
1733
1734 }
1735 }
1736 error("can't cd to %s", dest);
1737 /* NOTREACHED */
1738}
1739
1740
1741/*
1742 * Actually do the chdir. In an interactive shell, print the
1743 * directory name if "print" is nonzero.
1744 */
1745
1746static int
Eric Andersena3483db2001-10-24 08:01:06 +00001747docd(char *dest, int print)
Eric Andersencb57d552001-06-28 07:25:16 +00001748{
Eric Andersencb57d552001-06-28 07:25:16 +00001749 TRACE(("docd(\"%s\", %d) called\n", dest, print));
Eric Andersencb57d552001-06-28 07:25:16 +00001750 INTOFF;
1751 if (chdir(dest) < 0) {
1752 INTON;
1753 return -1;
1754 }
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001755 hashcd();
1756 /*
1757 * Update curdir (the name of the current directory) in response to a
1758 * cd command. We also call hashcd to let the routines in exec.c know
1759 * that the current directory has changed.
1760 */
1761 /* If dest is NULL, we don't know the current directory */
1762 if (dest == NULL || curdir == nullstr)
1763 setpwd(0, 1);
1764 else
1765 setpwd(dest, 1);
1766
Eric Andersencb57d552001-06-28 07:25:16 +00001767 INTON;
1768 if (print && iflag)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001769 puts(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001770 return 0;
1771}
1772
1773
Eric Andersencb57d552001-06-28 07:25:16 +00001774static int
Eric Andersena3483db2001-10-24 08:01:06 +00001775pwdcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001776{
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001777 puts(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001778 return 0;
1779}
Eric Andersencb57d552001-06-28 07:25:16 +00001780
Eric Andersena3483db2001-10-24 08:01:06 +00001781/* Ask system the current directory */
Eric Andersencb57d552001-06-28 07:25:16 +00001782static void
Eric Andersen2870d962001-07-02 17:27:21 +00001783getpwd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00001784{
Eric Andersen2870d962001-07-02 17:27:21 +00001785 curdir = xgetcwd(0);
1786 if(curdir==0)
1787 curdir = nullstr;
Eric Andersencb57d552001-06-28 07:25:16 +00001788}
1789
1790static void
1791setpwd(const char *val, int setold)
1792{
Eric Andersena3483db2001-10-24 08:01:06 +00001793 char *cated = NULL;
1794
Eric Andersencb57d552001-06-28 07:25:16 +00001795 if (setold) {
1796 setvar("OLDPWD", curdir, VEXPORT);
1797 }
1798 INTOFF;
1799 if (curdir != nullstr) {
Eric Andersena3483db2001-10-24 08:01:06 +00001800 if(val!=NULL && *val != '/')
1801 val = cated = concat_path_file(curdir, val);
Eric Andersencb57d552001-06-28 07:25:16 +00001802 free(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001803 }
Eric Andersena3483db2001-10-24 08:01:06 +00001804 if (!val)
Eric Andersencb57d552001-06-28 07:25:16 +00001805 getpwd();
Eric Andersena3483db2001-10-24 08:01:06 +00001806 else
1807 curdir = simplify_path(val);
1808 free(cated);
Eric Andersencb57d552001-06-28 07:25:16 +00001809 INTON;
1810 setvar("PWD", curdir, VEXPORT);
1811}
1812
Eric Andersencb57d552001-06-28 07:25:16 +00001813/*
1814 * Errors and exceptions.
1815 */
1816
1817/*
1818 * Code to handle exceptions in C.
1819 */
1820
Eric Andersen2870d962001-07-02 17:27:21 +00001821/*
1822 * We enclose jmp_buf in a structure so that we can declare pointers to
1823 * jump locations. The global variable handler contains the location to
1824 * jump to when an exception occurs, and the global variable exception
1825 * contains a code identifying the exeception. To implement nested
1826 * exception handlers, the user should save the value of handler on entry
1827 * to an inner scope, set handler to point to a jmploc structure for the
1828 * inner scope, and restore handler on exit from the scope.
1829 */
1830
1831struct jmploc {
1832 jmp_buf loc;
1833};
1834
1835/* exceptions */
1836#define EXINT 0 /* SIGINT received */
1837#define EXERROR 1 /* a generic error */
1838#define EXSHELLPROC 2 /* execute a shell procedure */
1839#define EXEXEC 3 /* command execution failed */
1840
1841static struct jmploc *handler;
Eric Andersencb57d552001-06-28 07:25:16 +00001842static int exception;
Eric Andersencb57d552001-06-28 07:25:16 +00001843
Eric Andersen2870d962001-07-02 17:27:21 +00001844static void exverror (int, const char *, va_list)
Eric Andersencb57d552001-06-28 07:25:16 +00001845 __attribute__((__noreturn__));
1846
1847/*
1848 * Called to raise an exception. Since C doesn't include exceptions, we
1849 * just do a longjmp to the exception handler. The type of exception is
1850 * stored in the global variable "exception".
1851 */
1852
Eric Andersen2870d962001-07-02 17:27:21 +00001853static void exraise (int) __attribute__((__noreturn__));
1854
Eric Andersencb57d552001-06-28 07:25:16 +00001855static void
Eric Andersen2870d962001-07-02 17:27:21 +00001856exraise(int e)
Eric Andersencb57d552001-06-28 07:25:16 +00001857{
1858#ifdef DEBUG
1859 if (handler == NULL)
1860 abort();
1861#endif
Eric Andersen62483552001-07-10 06:09:16 +00001862 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00001863 exception = e;
1864 longjmp(handler->loc, 1);
1865}
1866
1867
1868/*
1869 * Called from trap.c when a SIGINT is received. (If the user specifies
1870 * that SIGINT is to be trapped or ignored using the trap builtin, then
1871 * this routine is not called.) Suppressint is nonzero when interrupts
1872 * are held using the INTOFF macro. The call to _exit is necessary because
1873 * there is a short period after a fork before the signal handlers are
1874 * set to the appropriate value for the child. (The test for iflag is
1875 * just defensive programming.)
1876 */
1877
1878static void
Eric Andersen2870d962001-07-02 17:27:21 +00001879onint(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00001880 sigset_t mysigset;
1881
1882 if (suppressint) {
1883 intpending++;
1884 return;
1885 }
1886 intpending = 0;
1887 sigemptyset(&mysigset);
1888 sigprocmask(SIG_SETMASK, &mysigset, NULL);
1889 if (rootshell && iflag)
1890 exraise(EXINT);
1891 else {
1892 signal(SIGINT, SIG_DFL);
1893 raise(SIGINT);
1894 }
1895 /* NOTREACHED */
1896}
1897
1898
Eric Andersen2870d962001-07-02 17:27:21 +00001899static char *commandname; /* currently executing command */
1900
Eric Andersencb57d552001-06-28 07:25:16 +00001901/*
1902 * Exverror is called to raise the error exception. If the first argument
1903 * is not NULL then error prints an error message using printf style
1904 * formatting. It then raises the error exception.
1905 */
1906static void
Eric Andersen2870d962001-07-02 17:27:21 +00001907exverror(int cond, const char *msg, va_list ap)
Eric Andersencb57d552001-06-28 07:25:16 +00001908{
1909 CLEAR_PENDING_INT;
1910 INTOFF;
1911
1912#ifdef DEBUG
1913 if (msg)
1914 TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
1915 else
1916 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
1917#endif
1918 if (msg) {
1919 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +00001920 out2fmt("%s: ", commandname);
1921 vfprintf(stderr, msg, ap);
1922 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00001923 }
Eric Andersencb57d552001-06-28 07:25:16 +00001924 exraise(cond);
1925 /* NOTREACHED */
1926}
1927
1928
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001929static void
Eric Andersencb57d552001-06-28 07:25:16 +00001930error(const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00001931{
Eric Andersencb57d552001-06-28 07:25:16 +00001932 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00001933 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00001934 exverror(EXERROR, msg, ap);
1935 /* NOTREACHED */
1936 va_end(ap);
1937}
1938
1939
Eric Andersencb57d552001-06-28 07:25:16 +00001940static void
1941exerror(int cond, const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00001942{
Eric Andersencb57d552001-06-28 07:25:16 +00001943 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00001944 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00001945 exverror(cond, msg, ap);
1946 /* NOTREACHED */
1947 va_end(ap);
1948}
1949
1950
1951
1952/*
1953 * Table of error messages.
1954 */
1955
1956struct errname {
Eric Andersen2870d962001-07-02 17:27:21 +00001957 short errcode; /* error number */
Aaron Lehmann43880332001-12-31 06:16:54 +00001958 short action; /* operation which encountered the error */
Eric Andersencb57d552001-06-28 07:25:16 +00001959};
1960
Eric Andersen2870d962001-07-02 17:27:21 +00001961/*
1962 * Types of operations (passed to the errmsg routine).
1963 */
1964
1965#define E_OPEN 01 /* opening a file */
1966#define E_CREAT 02 /* creating a file */
1967#define E_EXEC 04 /* executing a program */
Eric Andersencb57d552001-06-28 07:25:16 +00001968
1969#define ALL (E_OPEN|E_CREAT|E_EXEC)
1970
1971static const struct errname errormsg[] = {
Eric Andersen2870d962001-07-02 17:27:21 +00001972 { EINTR, ALL },
1973 { EACCES, ALL },
1974 { EIO, ALL },
1975 { ENOENT, E_OPEN },
1976 { ENOENT, E_CREAT },
1977 { ENOENT, E_EXEC },
1978 { ENOTDIR, E_OPEN },
1979 { ENOTDIR, E_CREAT },
1980 { ENOTDIR, E_EXEC },
1981 { EISDIR, ALL },
1982 { EEXIST, E_CREAT },
1983#ifdef EMFILE
1984 { EMFILE, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00001985#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001986 { ENFILE, ALL },
1987 { ENOSPC, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00001988#ifdef EDQUOT
Eric Andersen2870d962001-07-02 17:27:21 +00001989 { EDQUOT, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00001990#endif
1991#ifdef ENOSR
Eric Andersen2870d962001-07-02 17:27:21 +00001992 { ENOSR, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00001993#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001994 { ENXIO, ALL },
1995 { EROFS, ALL },
1996 { ETXTBSY, ALL },
1997#ifdef EAGAIN
1998 { EAGAIN, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00001999#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002000 { ENOMEM, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002001#ifdef ENOLINK
Eric Andersen2870d962001-07-02 17:27:21 +00002002 { ENOLINK, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002003#endif
2004#ifdef EMULTIHOP
Eric Andersen2870d962001-07-02 17:27:21 +00002005 { EMULTIHOP, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002006#endif
2007#ifdef ECOMM
Eric Andersen2870d962001-07-02 17:27:21 +00002008 { ECOMM, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002009#endif
2010#ifdef ESTALE
Eric Andersen2870d962001-07-02 17:27:21 +00002011 { ESTALE, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002012#endif
2013#ifdef ETIMEDOUT
Eric Andersen2870d962001-07-02 17:27:21 +00002014 { ETIMEDOUT, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002015#endif
2016#ifdef ELOOP
Eric Andersen2870d962001-07-02 17:27:21 +00002017 { ELOOP, ALL },
Eric Andersencb57d552001-06-28 07:25:16 +00002018#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002019 { E2BIG, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00002020#ifdef ELIBACC
Eric Andersen2870d962001-07-02 17:27:21 +00002021 { ELIBACC, E_EXEC },
Eric Andersencb57d552001-06-28 07:25:16 +00002022#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002023};
2024
Eric Andersen2870d962001-07-02 17:27:21 +00002025#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname))
Eric Andersencb57d552001-06-28 07:25:16 +00002026
2027/*
2028 * Return a string describing an error. The returned string may be a
2029 * pointer to a static buffer that will be overwritten on the next call.
2030 * Action describes the operation that got the error.
2031 */
2032
2033static const char *
Eric Andersen2870d962001-07-02 17:27:21 +00002034errmsg(int e, int action)
Eric Andersencb57d552001-06-28 07:25:16 +00002035{
2036 struct errname const *ep;
2037 static char buf[12];
2038
Eric Andersen2870d962001-07-02 17:27:21 +00002039 for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) {
Eric Andersencb57d552001-06-28 07:25:16 +00002040 if (ep->errcode == e && (ep->action & action) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00002041 return strerror(e);
Eric Andersencb57d552001-06-28 07:25:16 +00002042 }
Eric Andersen2870d962001-07-02 17:27:21 +00002043
Eric Andersen3102ac42001-07-06 04:26:23 +00002044 snprintf(buf, sizeof buf, "error %d", e);
Eric Andersencb57d552001-06-28 07:25:16 +00002045 return buf;
2046}
2047
2048
Eric Andersend35c5df2002-01-09 15:37:36 +00002049#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersencb57d552001-06-28 07:25:16 +00002050static void
2051__inton() {
2052 if (--suppressint == 0 && intpending) {
2053 onint();
2054 }
2055}
Eric Andersen3102ac42001-07-06 04:26:23 +00002056static void forceinton (void) {
2057 suppressint = 0;
2058 if (intpending)
2059 onint();
2060}
Eric Andersencb57d552001-06-28 07:25:16 +00002061#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002062
2063/* flags in argument to evaltree */
Eric Andersen2870d962001-07-02 17:27:21 +00002064#define EV_EXIT 01 /* exit after evaluating tree */
2065#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2066#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00002067
Eric Andersen2870d962001-07-02 17:27:21 +00002068static int evalskip; /* set if we are skipping commands */
2069static int skipcount; /* number of levels to skip */
2070static int loopnest; /* current loop nesting level */
2071static int funcnest; /* depth of function calls */
Eric Andersencb57d552001-06-28 07:25:16 +00002072
2073
Eric Andersen2870d962001-07-02 17:27:21 +00002074static struct strlist *cmdenviron; /* environment for builtin command */
2075static int exitstatus; /* exit status of last command */
2076static int oexitstatus; /* saved exit status */
Eric Andersencb57d552001-06-28 07:25:16 +00002077
Eric Andersen62483552001-07-10 06:09:16 +00002078static void evalsubshell (const union node *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00002079static void expredir (union node *);
Eric Andersen2870d962001-07-02 17:27:21 +00002080static void eprintlist (struct strlist *);
Eric Andersencb57d552001-06-28 07:25:16 +00002081
Eric Andersen2870d962001-07-02 17:27:21 +00002082static union node *parsecmd(int);
Eric Andersencb57d552001-06-28 07:25:16 +00002083/*
2084 * Called to reset things after an exception.
2085 */
2086
Eric Andersencb57d552001-06-28 07:25:16 +00002087/*
2088 * The eval commmand.
2089 */
Eric Andersen2870d962001-07-02 17:27:21 +00002090static void evalstring (char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00002091
2092static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002093evalcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002094{
Eric Andersen2870d962001-07-02 17:27:21 +00002095 char *p;
2096 char *concat;
2097 char **ap;
Eric Andersencb57d552001-06-28 07:25:16 +00002098
Eric Andersen2870d962001-07-02 17:27:21 +00002099 if (argc > 1) {
2100 p = argv[1];
2101 if (argc > 2) {
2102 STARTSTACKSTR(concat);
2103 ap = argv + 2;
2104 for (;;) {
2105 while (*p)
2106 STPUTC(*p++, concat);
2107 if ((p = *ap++) == NULL)
2108 break;
2109 STPUTC(' ', concat);
2110 }
2111 STPUTC('\0', concat);
2112 p = grabstackstr(concat);
2113 }
2114 evalstring(p, EV_TESTED);
2115 }
2116 return exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00002117}
2118
Eric Andersencb57d552001-06-28 07:25:16 +00002119/*
2120 * Execute a command or commands contained in a string.
2121 */
2122
Eric Andersen2870d962001-07-02 17:27:21 +00002123static void evaltree (union node *, int);
2124static void setinputstring (char *);
2125static void popfile (void);
2126static void setstackmark(struct stackmark *mark);
2127static void popstackmark(struct stackmark *mark);
2128
2129
Eric Andersencb57d552001-06-28 07:25:16 +00002130static void
Eric Andersen2870d962001-07-02 17:27:21 +00002131evalstring(char *s, int flag)
2132{
Eric Andersencb57d552001-06-28 07:25:16 +00002133 union node *n;
2134 struct stackmark smark;
2135
2136 setstackmark(&smark);
2137 setinputstring(s);
2138 while ((n = parsecmd(0)) != NEOF) {
2139 evaltree(n, flag);
2140 popstackmark(&smark);
2141 }
2142 popfile();
2143 popstackmark(&smark);
2144}
2145
Eric Andersen2870d962001-07-02 17:27:21 +00002146static struct builtincmd *find_builtin (const char *);
Eric Andersen62483552001-07-10 06:09:16 +00002147static void expandarg (union node *, struct arglist *, int);
2148static void calcsize (const union node *);
2149static union node *copynode (const union node *);
2150
2151/*
2152 * Make a copy of a parse tree.
2153 */
2154
2155static int funcblocksize; /* size of structures in function */
2156static int funcstringsize; /* size of strings in node */
2157static pointer funcblock; /* block to allocate function from */
2158static char *funcstring; /* block to allocate strings from */
2159
2160
2161static inline union node *
2162copyfunc(union node *n)
2163{
2164 if (n == NULL)
2165 return NULL;
2166 funcblocksize = 0;
2167 funcstringsize = 0;
2168 calcsize(n);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002169 funcblock = xmalloc(funcblocksize + funcstringsize);
Eric Andersen62483552001-07-10 06:09:16 +00002170 funcstring = (char *) funcblock + funcblocksize;
2171 return copynode(n);
2172}
2173
2174/*
2175 * Free a parse tree.
2176 */
Eric Andersencb57d552001-06-28 07:25:16 +00002177
2178static void
Eric Andersen62483552001-07-10 06:09:16 +00002179freefunc(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00002180{
Eric Andersen62483552001-07-10 06:09:16 +00002181 if (n)
2182 ckfree(n);
Eric Andersencb57d552001-06-28 07:25:16 +00002183}
2184
2185
Eric Andersen62483552001-07-10 06:09:16 +00002186/*
2187 * Add a new command entry, replacing any existing command entry for
2188 * the same name.
2189 */
2190
2191static inline void
2192addcmdentry(char *name, struct cmdentry *entry)
2193{
2194 struct tblentry *cmdp;
2195
2196 INTOFF;
2197 cmdp = cmdlookup(name, 1);
2198 if (cmdp->cmdtype == CMDFUNCTION) {
2199 freefunc(cmdp->param.func);
2200 }
2201 cmdp->cmdtype = entry->cmdtype;
2202 cmdp->param = entry->u;
2203 INTON;
2204}
2205
2206static inline void
2207evalloop(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002208{
2209 int status;
2210
2211 loopnest++;
2212 status = 0;
2213 for (;;) {
2214 evaltree(n->nbinary.ch1, EV_TESTED);
2215 if (evalskip) {
Eric Andersen2870d962001-07-02 17:27:21 +00002216skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00002217 evalskip = 0;
2218 continue;
2219 }
2220 if (evalskip == SKIPBREAK && --skipcount <= 0)
2221 evalskip = 0;
2222 break;
2223 }
2224 if (n->type == NWHILE) {
2225 if (exitstatus != 0)
2226 break;
2227 } else {
2228 if (exitstatus == 0)
2229 break;
2230 }
2231 evaltree(n->nbinary.ch2, flags & EV_TESTED);
2232 status = exitstatus;
2233 if (evalskip)
2234 goto skipping;
2235 }
2236 loopnest--;
2237 exitstatus = status;
2238}
2239
Eric Andersencb57d552001-06-28 07:25:16 +00002240static void
Eric Andersen62483552001-07-10 06:09:16 +00002241evalfor(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002242{
2243 struct arglist arglist;
2244 union node *argp;
2245 struct strlist *sp;
2246 struct stackmark smark;
2247
2248 setstackmark(&smark);
2249 arglist.lastp = &arglist.list;
2250 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
2251 oexitstatus = exitstatus;
2252 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2253 if (evalskip)
2254 goto out;
2255 }
2256 *arglist.lastp = NULL;
2257
2258 exitstatus = 0;
2259 loopnest++;
2260 for (sp = arglist.list ; sp ; sp = sp->next) {
2261 setvar(n->nfor.var, sp->text, 0);
2262 evaltree(n->nfor.body, flags & EV_TESTED);
2263 if (evalskip) {
2264 if (evalskip == SKIPCONT && --skipcount <= 0) {
2265 evalskip = 0;
2266 continue;
2267 }
2268 if (evalskip == SKIPBREAK && --skipcount <= 0)
2269 evalskip = 0;
2270 break;
2271 }
2272 }
2273 loopnest--;
2274out:
2275 popstackmark(&smark);
2276}
2277
Eric Andersen62483552001-07-10 06:09:16 +00002278static inline void
2279evalcase(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002280{
2281 union node *cp;
2282 union node *patp;
2283 struct arglist arglist;
2284 struct stackmark smark;
2285
2286 setstackmark(&smark);
2287 arglist.lastp = &arglist.list;
2288 oexitstatus = exitstatus;
2289 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
2290 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
2291 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
2292 if (casematch(patp, arglist.list->text)) {
2293 if (evalskip == 0) {
2294 evaltree(cp->nclist.body, flags);
2295 }
2296 goto out;
2297 }
2298 }
2299 }
2300out:
2301 popstackmark(&smark);
2302}
2303
Eric Andersencb57d552001-06-28 07:25:16 +00002304/*
Eric Andersencb57d552001-06-28 07:25:16 +00002305 * Evaluate a pipeline. All the processes in the pipeline are children
2306 * of the process creating the pipeline. (This differs from some versions
2307 * of the shell, which make the last process in a pipeline the parent
2308 * of all the rest.)
2309 */
2310
Eric Andersen74400cc2001-10-18 04:11:39 +00002311static inline void evalpipe(union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00002312{
2313 struct job *jp;
2314 struct nodelist *lp;
2315 int pipelen;
2316 int prevfd;
2317 int pip[2];
2318
2319 TRACE(("evalpipe(0x%lx) called\n", (long)n));
2320 pipelen = 0;
2321 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
2322 pipelen++;
2323 INTOFF;
2324 jp = makejob(n, pipelen);
2325 prevfd = -1;
2326 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002327 /*
2328 * Search for a command. This is called before we fork so that the
2329 * location of the command will be available in the parent as well as
2330 * the child. The check for "goodname" is an overly conservative
2331 * check that the name will not be subject to expansion.
2332 */
2333
2334 struct cmdentry entry;
2335 union node *lpn = lp->n;
2336 if (lpn->type == NCMD && lpn->ncmd.args && goodname(lpn->ncmd.args->narg.text))
2337 find_command(lpn->ncmd.args->narg.text, &entry, 0, pathval());
2338
Eric Andersencb57d552001-06-28 07:25:16 +00002339 pip[1] = -1;
2340 if (lp->next) {
2341 if (pipe(pip) < 0) {
2342 close(prevfd);
2343 error("Pipe call failed");
2344 }
2345 }
2346 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
2347 INTON;
2348 if (prevfd > 0) {
2349 close(0);
2350 dup_as_newfd(prevfd, 0);
2351 close(prevfd);
2352 if (pip[0] == 0) {
2353 pip[0] = -1;
2354 }
2355 }
2356 if (pip[1] >= 0) {
2357 if (pip[0] >= 0) {
2358 close(pip[0]);
2359 }
2360 if (pip[1] != 1) {
2361 close(1);
2362 dup_as_newfd(pip[1], 1);
2363 close(pip[1]);
2364 }
2365 }
2366 evaltree(lp->n, EV_EXIT);
2367 }
2368 if (prevfd >= 0)
2369 close(prevfd);
2370 prevfd = pip[0];
2371 close(pip[1]);
2372 }
2373 INTON;
2374 if (n->npipe.backgnd == 0) {
2375 INTOFF;
2376 exitstatus = waitforjob(jp);
2377 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
2378 INTON;
2379 }
2380}
2381
Eric Andersen2870d962001-07-02 17:27:21 +00002382static int
2383isassignment(const char *word) {
2384 if (!is_name(*word)) {
2385 return 0;
2386 }
2387 do {
2388 word++;
2389 } while (is_in_name(*word));
2390 return *word == '=';
2391}
2392
Eric Andersen62483552001-07-10 06:09:16 +00002393
Eric Andersencb57d552001-06-28 07:25:16 +00002394static void
Eric Andersen3102ac42001-07-06 04:26:23 +00002395evalcommand(union node *cmd, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002396{
2397 struct stackmark smark;
2398 union node *argp;
2399 struct arglist arglist;
2400 struct arglist varlist;
2401 char **argv;
2402 int argc;
2403 char **envp;
2404 struct strlist *sp;
2405 int mode;
Eric Andersencb57d552001-06-28 07:25:16 +00002406 struct cmdentry cmdentry;
2407 struct job *jp;
2408 char *volatile savecmdname;
2409 volatile struct shparam saveparam;
2410 struct localvar *volatile savelocalvars;
2411 volatile int e;
2412 char *lastarg;
2413 const char *path;
2414 const struct builtincmd *firstbltin;
2415 struct jmploc *volatile savehandler;
2416 struct jmploc jmploc;
2417#if __GNUC__
2418 /* Avoid longjmp clobbering */
2419 (void) &argv;
2420 (void) &argc;
2421 (void) &lastarg;
2422 (void) &flags;
2423#endif
2424
2425 /* First expand the arguments. */
2426 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
2427 setstackmark(&smark);
2428 arglist.lastp = &arglist.list;
2429 varlist.lastp = &varlist.list;
2430 arglist.list = 0;
2431 oexitstatus = exitstatus;
2432 exitstatus = 0;
2433 path = pathval();
2434 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
2435 expandarg(argp, &varlist, EXP_VARTILDE);
2436 }
2437 for (
2438 argp = cmd->ncmd.args; argp && !arglist.list;
2439 argp = argp->narg.next
2440 ) {
2441 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2442 }
2443 if (argp) {
2444 struct builtincmd *bcmd;
Eric Andersen62483552001-07-10 06:09:16 +00002445 int pseudovarflag;
Eric Andersencb57d552001-06-28 07:25:16 +00002446 bcmd = find_builtin(arglist.list->text);
Eric Andersen2870d962001-07-02 17:27:21 +00002447 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
Eric Andersencb57d552001-06-28 07:25:16 +00002448 for (; argp; argp = argp->narg.next) {
2449 if (pseudovarflag && isassignment(argp->narg.text)) {
2450 expandarg(argp, &arglist, EXP_VARTILDE);
2451 continue;
2452 }
2453 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2454 }
2455 }
2456 *arglist.lastp = NULL;
2457 *varlist.lastp = NULL;
2458 expredir(cmd->ncmd.redirect);
2459 argc = 0;
2460 for (sp = arglist.list ; sp ; sp = sp->next)
2461 argc++;
2462 argv = stalloc(sizeof (char *) * (argc + 1));
2463
2464 for (sp = arglist.list ; sp ; sp = sp->next) {
2465 TRACE(("evalcommand arg: %s\n", sp->text));
2466 *argv++ = sp->text;
2467 }
2468 *argv = NULL;
2469 lastarg = NULL;
2470 if (iflag && funcnest == 0 && argc > 0)
2471 lastarg = argv[-1];
2472 argv -= argc;
2473
2474 /* Print the command if xflag is set. */
2475 if (xflag) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002476 out2c('+');
Eric Andersencb57d552001-06-28 07:25:16 +00002477 eprintlist(varlist.list);
2478 eprintlist(arglist.list);
Eric Andersen3102ac42001-07-06 04:26:23 +00002479 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00002480 }
2481
2482 /* Now locate the command. */
2483 if (argc == 0) {
2484 cmdentry.cmdtype = CMDBUILTIN;
2485 firstbltin = cmdentry.u.cmd = BLTINCMD;
2486 } else {
2487 const char *oldpath;
2488 int findflag = DO_ERR;
2489 int oldfindflag;
2490
2491 /*
2492 * Modify the command lookup path, if a PATH= assignment
2493 * is present
2494 */
2495 for (sp = varlist.list ; sp ; sp = sp->next)
2496 if (varequal(sp->text, defpathvar)) {
2497 path = sp->text + 5;
2498 findflag |= DO_BRUTE;
2499 }
2500 oldpath = path;
2501 oldfindflag = findflag;
2502 firstbltin = 0;
2503 for(;;) {
2504 find_command(argv[0], &cmdentry, findflag, path);
Eric Andersen2870d962001-07-02 17:27:21 +00002505 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
Eric Andersencb57d552001-06-28 07:25:16 +00002506 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002507 goto out;
2508 }
2509 /* implement bltin and command here */
2510 if (cmdentry.cmdtype != CMDBUILTIN) {
2511 break;
2512 }
2513 if (!firstbltin) {
2514 firstbltin = cmdentry.u.cmd;
2515 }
2516 if (cmdentry.u.cmd == BLTINCMD) {
2517 for(;;) {
2518 struct builtincmd *bcmd;
2519
2520 argv++;
2521 if (--argc == 0)
2522 goto found;
2523 if (!(bcmd = find_builtin(*argv))) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002524 out2fmt("%s: not found\n", *argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002525 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002526 goto out;
2527 }
2528 cmdentry.u.cmd = bcmd;
2529 if (bcmd != BLTINCMD)
2530 break;
2531 }
2532 }
Eric Andersen2870d962001-07-02 17:27:21 +00002533 if (cmdentry.u.cmd == find_builtin("command")) {
Eric Andersencb57d552001-06-28 07:25:16 +00002534 argv++;
2535 if (--argc == 0) {
2536 goto found;
2537 }
2538 if (*argv[0] == '-') {
2539 if (!equal(argv[0], "-p")) {
2540 argv--;
2541 argc++;
2542 break;
2543 }
2544 argv++;
2545 if (--argc == 0) {
2546 goto found;
2547 }
2548 path = defpath;
2549 findflag |= DO_BRUTE;
2550 } else {
2551 path = oldpath;
2552 findflag = oldfindflag;
2553 }
2554 findflag |= DO_NOFUN;
2555 continue;
2556 }
2557found:
2558 break;
2559 }
2560 }
2561
2562 /* Fork off a child process if necessary. */
2563 if (cmd->ncmd.backgnd
2564 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00002565 ) {
2566 jp = makejob(cmd, 1);
2567 mode = cmd->ncmd.backgnd;
Eric Andersencb57d552001-06-28 07:25:16 +00002568 if (forkshell(jp, cmd, mode) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00002569 goto parent; /* at end of routine */
Eric Andersencb57d552001-06-28 07:25:16 +00002570 flags |= EV_EXIT;
2571 }
2572
2573 /* This is the child process if a fork occurred. */
2574 /* Execute the command. */
2575 if (cmdentry.cmdtype == CMDFUNCTION) {
2576#ifdef DEBUG
2577 trputs("Shell function: "); trargs(argv);
2578#endif
2579 exitstatus = oexitstatus;
2580 redirect(cmd->ncmd.redirect, REDIR_PUSH);
2581 saveparam = shellparam;
2582 shellparam.malloc = 0;
2583 shellparam.nparam = argc - 1;
2584 shellparam.p = argv + 1;
2585 INTOFF;
2586 savelocalvars = localvars;
2587 localvars = NULL;
2588 INTON;
2589 if (setjmp(jmploc.loc)) {
2590 if (exception == EXSHELLPROC) {
2591 freeparam((volatile struct shparam *)
2592 &saveparam);
2593 } else {
2594 saveparam.optind = shellparam.optind;
2595 saveparam.optoff = shellparam.optoff;
2596 freeparam(&shellparam);
2597 shellparam = saveparam;
2598 }
2599 poplocalvars();
2600 localvars = savelocalvars;
2601 handler = savehandler;
2602 longjmp(handler->loc, 1);
2603 }
2604 savehandler = handler;
2605 handler = &jmploc;
2606 for (sp = varlist.list ; sp ; sp = sp->next)
2607 mklocal(sp->text);
2608 funcnest++;
2609 evaltree(cmdentry.u.func, flags & EV_TESTED);
2610 funcnest--;
2611 INTOFF;
2612 poplocalvars();
2613 localvars = savelocalvars;
2614 saveparam.optind = shellparam.optind;
2615 saveparam.optoff = shellparam.optoff;
2616 freeparam(&shellparam);
2617 shellparam = saveparam;
2618 handler = savehandler;
2619 popredir();
2620 INTON;
2621 if (evalskip == SKIPFUNC) {
2622 evalskip = 0;
2623 skipcount = 0;
2624 }
2625 if (flags & EV_EXIT)
2626 exitshell(exitstatus);
2627 } else if (cmdentry.cmdtype == CMDBUILTIN) {
2628#ifdef DEBUG
2629 trputs("builtin command: "); trargs(argv);
2630#endif
2631 mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
Eric Andersencb57d552001-06-28 07:25:16 +00002632 redirect(cmd->ncmd.redirect, mode);
2633 savecmdname = commandname;
Eric Andersen2870d962001-07-02 17:27:21 +00002634 if (IS_BUILTIN_SPECIAL(firstbltin)) {
Eric Andersencb57d552001-06-28 07:25:16 +00002635 listsetvar(varlist.list);
2636 } else {
2637 cmdenviron = varlist.list;
2638 }
2639 e = -1;
2640 if (setjmp(jmploc.loc)) {
2641 e = exception;
2642 exitstatus = (e == EXINT)? SIGINT+128 : 2;
2643 goto cmddone;
2644 }
2645 savehandler = handler;
2646 handler = &jmploc;
2647 commandname = argv[0];
2648 argptr = argv + 1;
Eric Andersen2870d962001-07-02 17:27:21 +00002649 optptr = NULL; /* initialize nextopt */
Eric Andersencb57d552001-06-28 07:25:16 +00002650 exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
2651 flushall();
2652cmddone:
Eric Andersencb57d552001-06-28 07:25:16 +00002653 cmdenviron = NULL;
2654 if (e != EXSHELLPROC) {
2655 commandname = savecmdname;
2656 if (flags & EV_EXIT)
2657 exitshell(exitstatus);
2658 }
2659 handler = savehandler;
2660 if (e != -1) {
2661 if ((e != EXERROR && e != EXEXEC)
2662 || cmdentry.u.cmd == BLTINCMD
2663 || cmdentry.u.cmd == DOTCMD
2664 || cmdentry.u.cmd == EVALCMD
2665 || cmdentry.u.cmd == EXECCMD)
2666 exraise(e);
2667 FORCEINTON;
2668 }
2669 if (cmdentry.u.cmd != EXECCMD)
2670 popredir();
Eric Andersencb57d552001-06-28 07:25:16 +00002671 } else {
2672#ifdef DEBUG
2673 trputs("normal command: "); trargs(argv);
2674#endif
2675 redirect(cmd->ncmd.redirect, 0);
2676 clearredir();
2677 for (sp = varlist.list ; sp ; sp = sp->next)
2678 setvareq(sp->text, VEXPORT|VSTACK);
2679 envp = environment();
2680 shellexec(argv, envp, path, cmdentry.u.index);
2681 }
2682 goto out;
2683
Eric Andersen2870d962001-07-02 17:27:21 +00002684parent: /* parent process gets here (if we forked) */
2685 if (mode == 0) { /* argument to fork */
Eric Andersencb57d552001-06-28 07:25:16 +00002686 INTOFF;
2687 exitstatus = waitforjob(jp);
2688 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002689 }
2690
2691out:
2692 if (lastarg)
2693 setvar("_", lastarg, 0);
2694 popstackmark(&smark);
2695}
2696
Eric Andersen62483552001-07-10 06:09:16 +00002697/*
2698 * Evaluate a parse tree. The value is left in the global variable
2699 * exitstatus.
2700 */
2701static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002702evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00002703{
2704 int checkexit = 0;
2705 if (n == NULL) {
2706 TRACE(("evaltree(NULL) called\n"));
2707 goto out;
2708 }
2709 TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
2710 switch (n->type) {
2711 case NSEMI:
2712 evaltree(n->nbinary.ch1, flags & EV_TESTED);
2713 if (evalskip)
2714 goto out;
2715 evaltree(n->nbinary.ch2, flags);
2716 break;
2717 case NAND:
2718 evaltree(n->nbinary.ch1, EV_TESTED);
2719 if (evalskip || exitstatus != 0)
2720 goto out;
2721 evaltree(n->nbinary.ch2, flags);
2722 break;
2723 case NOR:
2724 evaltree(n->nbinary.ch1, EV_TESTED);
2725 if (evalskip || exitstatus == 0)
2726 goto out;
2727 evaltree(n->nbinary.ch2, flags);
2728 break;
2729 case NREDIR:
2730 expredir(n->nredir.redirect);
2731 redirect(n->nredir.redirect, REDIR_PUSH);
2732 evaltree(n->nredir.n, flags);
2733 popredir();
2734 break;
2735 case NSUBSHELL:
2736 evalsubshell(n, flags);
2737 break;
2738 case NBACKGND:
2739 evalsubshell(n, flags);
2740 break;
2741 case NIF: {
2742 evaltree(n->nif.test, EV_TESTED);
2743 if (evalskip)
2744 goto out;
2745 if (exitstatus == 0)
2746 evaltree(n->nif.ifpart, flags);
2747 else if (n->nif.elsepart)
2748 evaltree(n->nif.elsepart, flags);
2749 else
2750 exitstatus = 0;
2751 break;
2752 }
2753 case NWHILE:
2754 case NUNTIL:
2755 evalloop(n, flags);
2756 break;
2757 case NFOR:
2758 evalfor(n, flags);
2759 break;
2760 case NCASE:
2761 evalcase(n, flags);
2762 break;
2763 case NDEFUN: {
2764 struct builtincmd *bcmd;
2765 struct cmdentry entry;
2766 if (
2767 (bcmd = find_builtin(n->narg.text)) &&
2768 IS_BUILTIN_SPECIAL(bcmd)
2769 ) {
2770 out2fmt("%s is a special built-in\n", n->narg.text);
2771 exitstatus = 1;
2772 break;
2773 }
2774 entry.cmdtype = CMDFUNCTION;
2775 entry.u.func = copyfunc(n->narg.next);
2776 addcmdentry(n->narg.text, &entry);
2777 exitstatus = 0;
2778 break;
2779 }
2780 case NNOT:
2781 evaltree(n->nnot.com, EV_TESTED);
2782 exitstatus = !exitstatus;
2783 break;
Eric Andersencb57d552001-06-28 07:25:16 +00002784
Eric Andersen62483552001-07-10 06:09:16 +00002785 case NPIPE:
2786 evalpipe(n);
2787 checkexit = 1;
2788 break;
2789 case NCMD:
2790 evalcommand(n, flags);
2791 checkexit = 1;
2792 break;
2793#ifdef DEBUG
2794 default:
2795 printf("Node type = %d\n", n->type);
2796 break;
2797#endif
2798 }
2799out:
2800 if (pendingsigs)
2801 dotrap();
2802 if (
2803 flags & EV_EXIT ||
2804 (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
2805 )
2806 exitshell(exitstatus);
2807}
2808
2809/*
2810 * Kick off a subshell to evaluate a tree.
2811 */
2812
2813static void
2814evalsubshell(const union node *n, int flags)
2815{
2816 struct job *jp;
2817 int backgnd = (n->type == NBACKGND);
2818
2819 expredir(n->nredir.redirect);
2820 jp = makejob(n, 1);
2821 if (forkshell(jp, n, backgnd) == 0) {
2822 if (backgnd)
2823 flags &=~ EV_TESTED;
2824 redirect(n->nredir.redirect, 0);
2825 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
2826 }
2827 if (! backgnd) {
2828 INTOFF;
2829 exitstatus = waitforjob(jp);
2830 INTON;
2831 }
2832}
2833
2834/*
2835 * Compute the names of the files in a redirection list.
2836 */
2837
2838static void fixredir(union node *n, const char *text, int err);
2839
2840static void
2841expredir(union node *n)
2842{
2843 union node *redir;
2844
2845 for (redir = n ; redir ; redir = redir->nfile.next) {
2846 struct arglist fn;
2847 fn.lastp = &fn.list;
2848 oexitstatus = exitstatus;
2849 switch (redir->type) {
2850 case NFROMTO:
2851 case NFROM:
2852 case NTO:
2853 case NAPPEND:
2854 case NTOOV:
2855 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
2856 redir->nfile.expfname = fn.list->text;
2857 break;
2858 case NFROMFD:
2859 case NTOFD:
2860 if (redir->ndup.vname) {
2861 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
2862 fixredir(redir, fn.list->text, 1);
2863 }
2864 break;
2865 }
2866 }
2867}
2868
2869
2870/*
2871 * Execute a command inside back quotes. If it's a builtin command, we
2872 * want to save its output in a block obtained from malloc. Otherwise
2873 * we fork off a subprocess and get the output of the command via a pipe.
2874 * Should be called with interrupts off.
2875 */
2876
2877static void
2878evalbackcmd(union node *n, struct backcmd *result)
2879{
2880 int pip[2];
2881 struct job *jp;
2882 struct stackmark smark; /* unnecessary */
2883
2884 setstackmark(&smark);
2885 result->fd = -1;
2886 result->buf = NULL;
2887 result->nleft = 0;
2888 result->jp = NULL;
2889 if (n == NULL) {
2890 exitstatus = 0;
2891 goto out;
2892 }
2893 exitstatus = 0;
2894 if (pipe(pip) < 0)
2895 error("Pipe call failed");
2896 jp = makejob(n, 1);
2897 if (forkshell(jp, n, FORK_NOJOB) == 0) {
2898 FORCEINTON;
2899 close(pip[0]);
2900 if (pip[1] != 1) {
2901 close(1);
2902 dup_as_newfd(pip[1], 1);
2903 close(pip[1]);
2904 }
2905 eflag = 0;
2906 evaltree(n, EV_EXIT);
2907 }
2908 close(pip[1]);
2909 result->fd = pip[0];
2910 result->jp = jp;
2911out:
2912 popstackmark(&smark);
2913 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
2914 result->fd, result->buf, result->nleft, result->jp));
2915}
2916
2917
2918/*
2919 * Execute a simple command.
2920 */
Eric Andersencb57d552001-06-28 07:25:16 +00002921
Eric Andersencb57d552001-06-28 07:25:16 +00002922
Eric Andersencb57d552001-06-28 07:25:16 +00002923/*
2924 * Builtin commands. Builtin commands whose functions are closely
2925 * tied to evaluation are implemented here.
2926 */
2927
2928/*
2929 * No command given, or a bltin command with no arguments. Set the
2930 * specified variables.
2931 */
2932
2933int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002934bltincmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002935{
2936 /*
2937 * Preserve exitstatus of a previous possible redirection
2938 * as POSIX mandates
2939 */
2940 return exitstatus;
2941}
2942
2943
2944/*
2945 * Handle break and continue commands. Break, continue, and return are
2946 * all handled by setting the evalskip flag. The evaluation routines
2947 * above all check this flag, and if it is set they start skipping
2948 * commands rather than executing them. The variable skipcount is
2949 * the number of loops to break/continue, or the number of function
2950 * levels to return. (The latter is always 1.) It should probably
2951 * be an error to break out of more loops than exist, but it isn't
2952 * in the standard shell so we don't make it one here.
2953 */
2954
2955static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002956breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002957{
2958 int n = argc > 1 ? number(argv[1]) : 1;
2959
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00002960 if (n <= 0)
2961 error("Illegal number: %s", argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00002962 if (n > loopnest)
2963 n = loopnest;
2964 if (n > 0) {
2965 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
2966 skipcount = n;
2967 }
2968 return 0;
2969}
2970
2971
2972/*
2973 * The return command.
2974 */
2975
2976static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002977returncmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002978{
2979 int ret = argc > 1 ? number(argv[1]) : oexitstatus;
2980
2981 if (funcnest) {
2982 evalskip = SKIPFUNC;
2983 skipcount = 1;
2984 return ret;
2985 }
2986 else {
2987 /* Do what ksh does; skip the rest of the file */
2988 evalskip = SKIPFILE;
2989 skipcount = 1;
2990 return ret;
2991 }
2992}
2993
2994
Eric Andersen69a20f02001-10-31 10:40:37 +00002995#ifndef CONFIG_FALSE
Eric Andersencb57d552001-06-28 07:25:16 +00002996static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002997false_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002998{
2999 return 1;
3000}
Eric Andersen69a20f02001-10-31 10:40:37 +00003001#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003002
Eric Andersen69a20f02001-10-31 10:40:37 +00003003#ifndef CONFIG_TRUE
Eric Andersencb57d552001-06-28 07:25:16 +00003004static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003005true_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003006{
3007 return 0;
3008}
3009#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003010
3011/*
3012 * Controls whether the shell is interactive or not.
3013 */
3014
3015static void setsignal(int signo);
Eric Andersen2870d962001-07-02 17:27:21 +00003016
Eric Andersend35c5df2002-01-09 15:37:36 +00003017#ifdef CONFIG_ASH_MAIL
Eric Andersenec074692001-10-31 11:05:49 +00003018static void chkmail(int silent);
3019#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003020
3021static void
3022setinteractive(int on)
3023{
3024 static int is_interactive;
Eric Andersen1c039232001-07-07 00:05:55 +00003025 static int do_banner=0;
Eric Andersen2870d962001-07-02 17:27:21 +00003026
3027 if (on == is_interactive)
3028 return;
3029 setsignal(SIGINT);
3030 setsignal(SIGQUIT);
3031 setsignal(SIGTERM);
Eric Andersend35c5df2002-01-09 15:37:36 +00003032#ifdef CONFIG_ASH_MAIL
Eric Andersen2870d962001-07-02 17:27:21 +00003033 chkmail(1);
Eric Andersenec074692001-10-31 11:05:49 +00003034#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003035 is_interactive = on;
Eric Andersen1c039232001-07-07 00:05:55 +00003036 if (do_banner==0 && is_interactive) {
3037 /* Looks like they want an interactive shell */
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003038#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
Eric Andersen1c039232001-07-07 00:05:55 +00003039 printf( "\n\n" BB_BANNER " Built-in shell (ash)\n");
3040 printf( "Enter 'help' for a list of built-in commands.\n\n");
Eric Andersend63dee42001-10-19 00:22:23 +00003041#endif
Eric Andersen1c039232001-07-07 00:05:55 +00003042 do_banner=1;
3043 }
Eric Andersen2870d962001-07-02 17:27:21 +00003044}
3045
3046static void
3047optschanged(void)
3048{
3049 setinteractive(iflag);
3050 setjobctl(mflag);
3051}
3052
Eric Andersencb57d552001-06-28 07:25:16 +00003053
3054static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003055execcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003056{
3057 if (argc > 1) {
3058 struct strlist *sp;
3059
Eric Andersen2870d962001-07-02 17:27:21 +00003060 iflag = 0; /* exit on error */
Eric Andersencb57d552001-06-28 07:25:16 +00003061 mflag = 0;
3062 optschanged();
3063 for (sp = cmdenviron; sp ; sp = sp->next)
3064 setvareq(sp->text, VEXPORT|VSTACK);
3065 shellexec(argv + 1, environment(), pathval(), 0);
3066 }
3067 return 0;
3068}
3069
3070static void
3071eprintlist(struct strlist *sp)
3072{
3073 for (; sp; sp = sp->next) {
Eric Andersen3102ac42001-07-06 04:26:23 +00003074 out2fmt(" %s",sp->text);
Eric Andersencb57d552001-06-28 07:25:16 +00003075 }
3076}
Eric Andersencb57d552001-06-28 07:25:16 +00003077
3078/*
3079 * Exec a program. Never returns. If you change this routine, you may
3080 * have to change the find_command routine as well.
3081 */
3082
Eric Andersen2870d962001-07-02 17:27:21 +00003083static const char *pathopt; /* set by padvance */
3084
Eric Andersencb57d552001-06-28 07:25:16 +00003085static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003086shellexec(char **argv, char **envp, const char *path, int idx)
Eric Andersencb57d552001-06-28 07:25:16 +00003087{
3088 char *cmdname;
3089 int e;
3090
3091 if (strchr(argv[0], '/') != NULL) {
3092 tryexec(argv[0], argv, envp);
3093 e = errno;
3094 } else {
3095 e = ENOENT;
3096 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3097 if (--idx < 0 && pathopt == NULL) {
3098 tryexec(cmdname, argv, envp);
3099 if (errno != ENOENT && errno != ENOTDIR)
3100 e = errno;
3101 }
3102 stunalloc(cmdname);
3103 }
3104 }
3105
3106 /* Map to POSIX errors */
3107 switch (e) {
3108 case EACCES:
3109 exerrno = 126;
3110 break;
3111 case ENOENT:
3112 exerrno = 127;
3113 break;
3114 default:
3115 exerrno = 2;
3116 break;
3117 }
3118 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3119 /* NOTREACHED */
3120}
3121
Eric Andersen2870d962001-07-02 17:27:21 +00003122/*
3123 * Clear traps on a fork.
3124 */
3125static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003126clear_traps(void)
3127{
Eric Andersen2870d962001-07-02 17:27:21 +00003128 char **tp;
3129
3130 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
3131 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
3132 INTOFF;
3133 ckfree(*tp);
3134 *tp = NULL;
3135 if (tp != &trap[0])
3136 setsignal(tp - trap);
3137 INTON;
3138 }
3139 }
3140}
3141
3142
3143static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003144initshellproc(void)
3145{
Eric Andersen2870d962001-07-02 17:27:21 +00003146
Eric Andersend35c5df2002-01-09 15:37:36 +00003147#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00003148 /* from alias.c: */
3149 {
3150 rmaliases();
3151 }
3152#endif
3153 /* from eval.c: */
3154 {
3155 exitstatus = 0;
3156 }
3157
3158 /* from exec.c: */
3159 {
3160 deletefuncs();
3161 }
3162
3163 /* from jobs.c: */
3164 {
3165 backgndpid = -1;
Eric Andersend35c5df2002-01-09 15:37:36 +00003166#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00003167 jobctl = 0;
3168#endif
3169 }
3170
3171 /* from options.c: */
3172 {
3173 int i;
3174
3175 for (i = 0; i < NOPTS; i++)
3176 optent_val(i) = 0;
3177 optschanged();
3178
3179 }
3180
3181 /* from redir.c: */
3182 {
3183 clearredir();
3184 }
3185
3186 /* from trap.c: */
3187 {
3188 char *sm;
3189
3190 clear_traps();
3191 for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
3192 if (*sm == S_IGN)
3193 *sm = S_HARD_IGN;
3194 }
3195 }
3196
3197 /* from var.c: */
3198 {
3199 shprocvar();
3200 }
3201}
3202
3203static int preadbuffer(void);
3204static void pushfile (void);
Eric Andersen2870d962001-07-02 17:27:21 +00003205
3206/*
3207 * Read a character from the script, returning PEOF on end of file.
3208 * Nul characters in the input are silently discarded.
3209 */
3210
Eric Andersend35c5df2002-01-09 15:37:36 +00003211#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +00003212#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
3213static int
3214pgetc(void)
3215{
3216 return pgetc_macro();
3217}
3218#else
3219static int
3220pgetc_macro(void)
3221{
3222 return --parsenleft >= 0? *parsenextc++ : preadbuffer();
3223}
3224
3225static inline int
3226pgetc(void)
3227{
3228 return pgetc_macro();
3229}
3230#endif
3231
3232
3233/*
3234 * Undo the last call to pgetc. Only one character may be pushed back.
3235 * PEOF may be pushed back.
3236 */
3237
Eric Andersen74400cc2001-10-18 04:11:39 +00003238static void pungetc(void)
3239{
Eric Andersen2870d962001-07-02 17:27:21 +00003240 parsenleft++;
3241 parsenextc--;
3242}
3243
3244
3245static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003246popfile(void)
3247{
Eric Andersen2870d962001-07-02 17:27:21 +00003248 struct parsefile *pf = parsefile;
3249
3250 INTOFF;
3251 if (pf->fd >= 0)
3252 close(pf->fd);
3253 if (pf->buf)
3254 ckfree(pf->buf);
3255 while (pf->strpush)
3256 popstring();
3257 parsefile = pf->prev;
3258 ckfree(pf);
3259 parsenleft = parsefile->nleft;
3260 parselleft = parsefile->lleft;
3261 parsenextc = parsefile->nextc;
3262 plinno = parsefile->linno;
3263 INTON;
3264}
3265
3266
3267/*
3268 * Return to top level.
3269 */
3270
3271static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003272popallfiles(void)
3273{
Eric Andersen2870d962001-07-02 17:27:21 +00003274 while (parsefile != &basepf)
3275 popfile();
3276}
3277
3278/*
3279 * Close the file(s) that the shell is reading commands from. Called
3280 * after a fork is done.
3281 */
3282
Eric Andersen74400cc2001-10-18 04:11:39 +00003283static void closescript(void)
3284{
Eric Andersen2870d962001-07-02 17:27:21 +00003285 popallfiles();
3286 if (parsefile->fd > 0) {
3287 close(parsefile->fd);
3288 parsefile->fd = 0;
3289 }
3290}
3291
3292
3293/*
3294 * Like setinputfile, but takes an open file descriptor. Call this with
3295 * interrupts off.
3296 */
3297
Eric Andersen74400cc2001-10-18 04:11:39 +00003298static void setinputfd(int fd, int push)
Eric Andersen2870d962001-07-02 17:27:21 +00003299{
3300 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
3301 if (push) {
3302 pushfile();
3303 parsefile->buf = 0;
3304 } else {
3305 closescript();
3306 while (parsefile->strpush)
3307 popstring();
3308 }
3309 parsefile->fd = fd;
3310 if (parsefile->buf == NULL)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003311 parsefile->buf = xmalloc(BUFSIZ);
Eric Andersen2870d962001-07-02 17:27:21 +00003312 parselleft = parsenleft = 0;
3313 plinno = 1;
3314}
3315
3316
3317/*
3318 * Set the input to take input from a file. If push is set, push the
3319 * old input onto the stack first.
3320 */
3321
3322static void
3323setinputfile(const char *fname, int push)
3324{
3325 int fd;
3326 int myfileno2;
3327
3328 INTOFF;
3329 if ((fd = open(fname, O_RDONLY)) < 0)
3330 error("Can't open %s", fname);
3331 if (fd < 10) {
3332 myfileno2 = dup_as_newfd(fd, 10);
3333 close(fd);
3334 if (myfileno2 < 0)
3335 error("Out of file descriptors");
3336 fd = myfileno2;
3337 }
3338 setinputfd(fd, push);
3339 INTON;
3340}
3341
Eric Andersencb57d552001-06-28 07:25:16 +00003342
3343static void
Eric Andersen62483552001-07-10 06:09:16 +00003344tryexec(char *cmd, char **argv, char **envp)
3345{
Eric Andersencb57d552001-06-28 07:25:16 +00003346 int e;
Eric Andersencb57d552001-06-28 07:25:16 +00003347
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003348#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
Eric Andersen3102ac42001-07-06 04:26:23 +00003349 char *name = cmd;
3350 char** argv_l=argv;
3351 int argc_l;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003352#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
Eric Andersen3102ac42001-07-06 04:26:23 +00003353 name = get_last_path_component(name);
3354#endif
3355 argv_l=envp;
3356 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
3357 putenv(*argv_l);
3358 argv_l=argv;
Eric Andersen62483552001-07-10 06:09:16 +00003359 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
Eric Andersen3102ac42001-07-06 04:26:23 +00003360 optind = 1;
3361 run_applet_by_name(name, argc_l, argv);
3362#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003363 execve(cmd, argv, envp);
Eric Andersencb57d552001-06-28 07:25:16 +00003364 e = errno;
3365 if (e == ENOEXEC) {
3366 INTOFF;
3367 initshellproc();
3368 setinputfile(cmd, 0);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003369 commandname = arg0 = xstrdup(argv[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00003370 setparam(argv + 1);
3371 exraise(EXSHELLPROC);
3372 }
3373 errno = e;
3374}
3375
Eric Andersen2870d962001-07-02 17:27:21 +00003376static char *commandtext (const union node *);
Eric Andersencb57d552001-06-28 07:25:16 +00003377
3378/*
3379 * Do a path search. The variable path (passed by reference) should be
3380 * set to the start of the path before the first call; padvance will update
3381 * this value as it proceeds. Successive calls to padvance will return
3382 * the possible path expansions in sequence. If an option (indicated by
3383 * a percent sign) appears in the path entry then the global variable
3384 * pathopt will be set to point to it; otherwise pathopt will be set to
3385 * NULL.
3386 */
3387
3388static const char *pathopt;
3389
Eric Andersen2870d962001-07-02 17:27:21 +00003390static void growstackblock(void);
3391
3392
Eric Andersencb57d552001-06-28 07:25:16 +00003393static char *
Eric Andersen2870d962001-07-02 17:27:21 +00003394padvance(const char **path, const char *name)
3395{
Eric Andersencb57d552001-06-28 07:25:16 +00003396 const char *p;
3397 char *q;
3398 const char *start;
3399 int len;
3400
3401 if (*path == NULL)
3402 return NULL;
3403 start = *path;
3404 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
Eric Andersen2870d962001-07-02 17:27:21 +00003405 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +00003406 while (stackblocksize() < len)
3407 growstackblock();
3408 q = stackblock();
3409 if (p != start) {
3410 memcpy(q, start, p - start);
3411 q += p - start;
3412 *q++ = '/';
3413 }
3414 strcpy(q, name);
3415 pathopt = NULL;
3416 if (*p == '%') {
3417 pathopt = ++p;
3418 while (*p && *p != ':') p++;
3419 }
3420 if (*p == ':')
3421 *path = p + 1;
3422 else
3423 *path = NULL;
3424 return stalloc(len);
3425}
3426
Eric Andersen62483552001-07-10 06:09:16 +00003427/*
3428 * Wrapper around strcmp for qsort/bsearch/...
3429 */
3430static int
3431pstrcmp(const void *a, const void *b)
3432{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003433 return strcmp((const char *) a, (*(const char *const *) b) + 1);
Eric Andersen62483552001-07-10 06:09:16 +00003434}
3435
3436/*
3437 * Find a keyword is in a sorted array.
3438 */
3439
3440static const char *const *
3441findkwd(const char *s)
3442{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003443 return bsearch(s, tokname_array + KWDOFFSET,
3444 (sizeof(tokname_array)/sizeof(const char *)) - KWDOFFSET,
3445 sizeof(const char *), pstrcmp);
Eric Andersen62483552001-07-10 06:09:16 +00003446}
Eric Andersencb57d552001-06-28 07:25:16 +00003447
3448
3449/*** Command hashing code ***/
3450
3451
3452static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003453hashcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003454{
3455 struct tblentry **pp;
3456 struct tblentry *cmdp;
3457 int c;
3458 int verbose;
3459 struct cmdentry entry;
3460 char *name;
Eric Andersend35c5df2002-01-09 15:37:36 +00003461#ifdef CONFIG_ASH_ALIAS
Eric Andersen62483552001-07-10 06:09:16 +00003462 const struct alias *ap;
3463#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003464
3465 verbose = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003466 while ((c = nextopt("rvV")) != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00003467 if (c == 'r') {
3468 clearcmdentry(0);
3469 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00003470 } else if (c == 'v' || c == 'V') {
3471 verbose = c;
Eric Andersencb57d552001-06-28 07:25:16 +00003472 }
3473 }
3474 if (*argptr == NULL) {
3475 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3476 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3477 if (cmdp->cmdtype != CMDBUILTIN) {
3478 printentry(cmdp, verbose);
3479 }
3480 }
3481 }
3482 return 0;
3483 }
3484 c = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003485 while ((name = *argptr++) != NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00003486 if ((cmdp = cmdlookup(name, 0)) != NULL
3487 && (cmdp->cmdtype == CMDNORMAL
3488 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
3489 delete_cmd_entry();
Eric Andersend35c5df2002-01-09 15:37:36 +00003490#ifdef CONFIG_ASH_ALIAS
Eric Andersen62483552001-07-10 06:09:16 +00003491 /* Then look at the aliases */
Eric Andersenec074692001-10-31 11:05:49 +00003492 if ((ap = *__lookupalias(name)) != NULL) {
Eric Andersen62483552001-07-10 06:09:16 +00003493 if (verbose=='v')
3494 printf("%s is an alias for %s\n", name, ap->val);
3495 else
3496 printalias(ap);
3497 continue;
3498 }
3499#endif
3500 /* First look at the keywords */
3501 if (findkwd(name)!=0) {
3502 if (verbose=='v')
3503 printf("%s is a shell keyword\n", name);
3504 else
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003505 puts(name);
Eric Andersen62483552001-07-10 06:09:16 +00003506 continue;
3507 }
3508
Eric Andersencb57d552001-06-28 07:25:16 +00003509 find_command(name, &entry, DO_ERR, pathval());
3510 if (entry.cmdtype == CMDUNKNOWN) c = 1;
3511 else if (verbose) {
3512 cmdp = cmdlookup(name, 0);
Eric Andersen62483552001-07-10 06:09:16 +00003513 if (cmdp) printentry(cmdp, verbose=='v');
Eric Andersencb57d552001-06-28 07:25:16 +00003514 flushall();
3515 }
Eric Andersencb57d552001-06-28 07:25:16 +00003516 }
3517 return c;
3518}
3519
Eric Andersencb57d552001-06-28 07:25:16 +00003520static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003521printentry(struct tblentry *cmdp, int verbose)
3522{
Eric Andersencb57d552001-06-28 07:25:16 +00003523 int idx;
3524 const char *path;
3525 char *name;
3526
Eric Andersen62483552001-07-10 06:09:16 +00003527 printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00003528 if (cmdp->cmdtype == CMDNORMAL) {
3529 idx = cmdp->param.index;
3530 path = pathval();
3531 do {
3532 name = padvance(&path, cmdp->cmdname);
3533 stunalloc(name);
3534 } while (--idx >= 0);
Eric Andersen62483552001-07-10 06:09:16 +00003535 if(verbose)
3536 out1str(name);
Eric Andersencb57d552001-06-28 07:25:16 +00003537 } else if (cmdp->cmdtype == CMDBUILTIN) {
Eric Andersen62483552001-07-10 06:09:16 +00003538 if(verbose)
3539 out1str("a shell builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00003540 } else if (cmdp->cmdtype == CMDFUNCTION) {
Eric Andersencb57d552001-06-28 07:25:16 +00003541 if (verbose) {
3542 INTOFF;
Eric Andersen62483552001-07-10 06:09:16 +00003543 out1str("a function\n");
Eric Andersencb57d552001-06-28 07:25:16 +00003544 name = commandtext(cmdp->param.func);
Eric Andersen62483552001-07-10 06:09:16 +00003545 printf("%s() {\n %s\n}", cmdp->cmdname, name);
Eric Andersencb57d552001-06-28 07:25:16 +00003546 ckfree(name);
3547 INTON;
3548 }
3549#ifdef DEBUG
3550 } else {
3551 error("internal error: cmdtype %d", cmdp->cmdtype);
3552#endif
3553 }
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003554 puts(cmdp->rehash ? "*" : nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00003555}
3556
3557
3558
Eric Andersen1c039232001-07-07 00:05:55 +00003559/*** List the available builtins ***/
3560
3561
3562static int helpcmd(int argc, char** argv)
3563{
3564 int col, i;
Eric Andersen1c039232001-07-07 00:05:55 +00003565
Eric Andersen62483552001-07-10 06:09:16 +00003566 printf("\nBuilt-in commands:\n-------------------\n");
3567 for (col=0, i=0; i < NUMBUILTINS; i++) {
3568 col += printf("%c%s", ((col == 0) ? '\t' : ' '),
3569 builtincmds[i].name+1);
Eric Andersen1c039232001-07-07 00:05:55 +00003570 if (col > 60) {
3571 printf("\n");
3572 col = 0;
3573 }
3574 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003575#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
Eric Andersen1c039232001-07-07 00:05:55 +00003576 {
Eric Andersen1c039232001-07-07 00:05:55 +00003577 extern const struct BB_applet applets[];
3578 extern const size_t NUM_APPLETS;
3579
Eric Andersen62483552001-07-10 06:09:16 +00003580 for (i=0; i < NUM_APPLETS; i++) {
3581
3582 col += printf("%c%s", ((col == 0) ? '\t' : ' '),
3583 applets[i].name);
Eric Andersen1c039232001-07-07 00:05:55 +00003584 if (col > 60) {
3585 printf("\n");
3586 col = 0;
3587 }
3588 }
3589 }
3590#endif
3591 printf("\n\n");
3592 return EXIT_SUCCESS;
3593}
3594
Eric Andersencb57d552001-06-28 07:25:16 +00003595/*
3596 * Resolve a command name. If you change this routine, you may have to
3597 * change the shellexec routine as well.
3598 */
3599
Eric Andersen2870d962001-07-02 17:27:21 +00003600static int prefix (const char *, const char *);
3601
Eric Andersencb57d552001-06-28 07:25:16 +00003602static void
Eric Andersen2870d962001-07-02 17:27:21 +00003603find_command(const char *name, struct cmdentry *entry, int act, const char *path)
Eric Andersencb57d552001-06-28 07:25:16 +00003604{
3605 struct tblentry *cmdp;
3606 int idx;
3607 int prev;
3608 char *fullname;
3609 struct stat statb;
3610 int e;
3611 int bltin;
3612 int firstchange;
3613 int updatetbl;
Eric Andersen62483552001-07-10 06:09:16 +00003614 int regular;
Eric Andersencb57d552001-06-28 07:25:16 +00003615 struct builtincmd *bcmd;
3616
3617 /* If name contains a slash, don't use the hash table */
3618 if (strchr(name, '/') != NULL) {
3619 if (act & DO_ABS) {
3620 while (stat(name, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003621 if (errno != ENOENT && errno != ENOTDIR)
3622 e = errno;
3623 entry->cmdtype = CMDUNKNOWN;
3624 entry->u.index = -1;
3625 return;
3626 }
3627 entry->cmdtype = CMDNORMAL;
3628 entry->u.index = -1;
3629 return;
3630 }
3631 entry->cmdtype = CMDNORMAL;
3632 entry->u.index = 0;
3633 return;
3634 }
3635
3636 updatetbl = 1;
3637 if (act & DO_BRUTE) {
3638 firstchange = path_change(path, &bltin);
3639 } else {
3640 bltin = builtinloc;
3641 firstchange = 9999;
3642 }
3643
3644 /* If name is in the table, and not invalidated by cd, we're done */
3645 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
3646 if (cmdp->cmdtype == CMDFUNCTION) {
3647 if (act & DO_NOFUN) {
3648 updatetbl = 0;
3649 } else {
3650 goto success;
3651 }
3652 } else if (act & DO_BRUTE) {
3653 if ((cmdp->cmdtype == CMDNORMAL &&
3654 cmdp->param.index >= firstchange) ||
3655 (cmdp->cmdtype == CMDBUILTIN &&
3656 ((builtinloc < 0 && bltin >= 0) ?
3657 bltin : builtinloc) >= firstchange)) {
3658 /* need to recompute the entry */
3659 } else {
3660 goto success;
3661 }
3662 } else {
3663 goto success;
3664 }
3665 }
3666
3667 bcmd = find_builtin(name);
Eric Andersen2870d962001-07-02 17:27:21 +00003668 regular = bcmd && IS_BUILTIN_REGULAR(bcmd);
Eric Andersencb57d552001-06-28 07:25:16 +00003669
3670 if (regular) {
3671 if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
Eric Andersen2870d962001-07-02 17:27:21 +00003672 goto success;
Eric Andersencb57d552001-06-28 07:25:16 +00003673 }
3674 } else if (act & DO_BRUTE) {
3675 if (firstchange == 0) {
3676 updatetbl = 0;
3677 }
3678 }
3679
3680 /* If %builtin not in path, check for builtin next */
3681 if (regular || (bltin < 0 && bcmd)) {
3682builtin:
3683 if (!updatetbl) {
3684 entry->cmdtype = CMDBUILTIN;
3685 entry->u.cmd = bcmd;
3686 return;
3687 }
3688 INTOFF;
3689 cmdp = cmdlookup(name, 1);
3690 cmdp->cmdtype = CMDBUILTIN;
3691 cmdp->param.cmd = bcmd;
3692 INTON;
3693 goto success;
3694 }
3695
3696 /* We have to search path. */
Eric Andersen2870d962001-07-02 17:27:21 +00003697 prev = -1; /* where to start */
3698 if (cmdp && cmdp->rehash) { /* doing a rehash */
Eric Andersencb57d552001-06-28 07:25:16 +00003699 if (cmdp->cmdtype == CMDBUILTIN)
3700 prev = builtinloc;
3701 else
3702 prev = cmdp->param.index;
3703 }
3704
3705 e = ENOENT;
3706 idx = -1;
3707loop:
3708 while ((fullname = padvance(&path, name)) != NULL) {
3709 stunalloc(fullname);
3710 idx++;
3711 if (idx >= firstchange) {
3712 updatetbl = 0;
3713 }
3714 if (pathopt) {
3715 if (prefix("builtin", pathopt)) {
3716 if ((bcmd = find_builtin(name))) {
3717 goto builtin;
3718 }
3719 continue;
3720 } else if (!(act & DO_NOFUN) &&
3721 prefix("func", pathopt)) {
3722 /* handled below */
3723 } else {
Eric Andersen2870d962001-07-02 17:27:21 +00003724 continue; /* ignore unimplemented options */
Eric Andersencb57d552001-06-28 07:25:16 +00003725 }
3726 }
3727 /* if rehash, don't redo absolute path names */
3728 if (fullname[0] == '/' && idx <= prev &&
3729 idx < firstchange) {
3730 if (idx < prev)
3731 continue;
3732 TRACE(("searchexec \"%s\": no change\n", name));
3733 goto success;
3734 }
3735 while (stat(fullname, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003736 if (errno != ENOENT && errno != ENOTDIR)
3737 e = errno;
3738 goto loop;
3739 }
Eric Andersen2870d962001-07-02 17:27:21 +00003740 e = EACCES; /* if we fail, this will be the error */
Eric Andersencb57d552001-06-28 07:25:16 +00003741 if (!S_ISREG(statb.st_mode))
3742 continue;
Eric Andersen2870d962001-07-02 17:27:21 +00003743 if (pathopt) { /* this is a %func directory */
Eric Andersencb57d552001-06-28 07:25:16 +00003744 stalloc(strlen(fullname) + 1);
3745 readcmdfile(fullname);
3746 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
3747 error("%s not defined in %s", name, fullname);
3748 stunalloc(fullname);
3749 goto success;
3750 }
Eric Andersencb57d552001-06-28 07:25:16 +00003751 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
3752 /* If we aren't called with DO_BRUTE and cmdp is set, it must
3753 be a function and we're being called with DO_NOFUN */
3754 if (!updatetbl) {
3755 entry->cmdtype = CMDNORMAL;
3756 entry->u.index = idx;
3757 return;
3758 }
3759 INTOFF;
3760 cmdp = cmdlookup(name, 1);
3761 cmdp->cmdtype = CMDNORMAL;
3762 cmdp->param.index = idx;
3763 INTON;
3764 goto success;
3765 }
3766
3767 /* We failed. If there was an entry for this command, delete it */
3768 if (cmdp && updatetbl)
3769 delete_cmd_entry();
3770 if (act & DO_ERR)
Eric Andersen3102ac42001-07-06 04:26:23 +00003771 out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
Eric Andersencb57d552001-06-28 07:25:16 +00003772 entry->cmdtype = CMDUNKNOWN;
3773 return;
3774
3775success:
3776 cmdp->rehash = 0;
3777 entry->cmdtype = cmdp->cmdtype;
3778 entry->u = cmdp->param;
3779}
3780
3781
3782
3783/*
3784 * Search the table of builtin commands.
3785 */
3786
Eric Andersen2870d962001-07-02 17:27:21 +00003787static int
3788bstrcmp(const void *name, const void *b)
3789{
3790 return strcmp((const char *)name, (*(const char *const *) b)+1);
3791}
3792
3793static struct builtincmd *
3794find_builtin(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00003795{
3796 struct builtincmd *bp;
3797
Eric Andersen2870d962001-07-02 17:27:21 +00003798 bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
3799 bstrcmp
Eric Andersencb57d552001-06-28 07:25:16 +00003800 );
3801 return bp;
3802}
3803
3804
3805/*
3806 * Called when a cd is done. Marks all commands so the next time they
3807 * are executed they will be rehashed.
3808 */
3809
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003810static inline void
3811hashcd(void)
3812{
Eric Andersencb57d552001-06-28 07:25:16 +00003813 struct tblentry **pp;
3814 struct tblentry *cmdp;
3815
3816 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3817 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3818 if (cmdp->cmdtype == CMDNORMAL
3819 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
3820 cmdp->rehash = 1;
3821 }
3822 }
3823}
3824
3825
3826
3827/*
3828 * Called before PATH is changed. The argument is the new value of PATH;
3829 * pathval() still returns the old value at this point. Called with
3830 * interrupts off.
3831 */
3832
3833static void
Eric Andersen2870d962001-07-02 17:27:21 +00003834changepath(const char *newval)
Eric Andersencb57d552001-06-28 07:25:16 +00003835{
3836 int firstchange;
3837 int bltin;
3838
3839 firstchange = path_change(newval, &bltin);
3840 if (builtinloc < 0 && bltin >= 0)
Eric Andersen2870d962001-07-02 17:27:21 +00003841 builtinloc = bltin; /* zap builtins */
Eric Andersencb57d552001-06-28 07:25:16 +00003842 clearcmdentry(firstchange);
3843 builtinloc = bltin;
3844}
3845
3846
3847/*
3848 * Clear out command entries. The argument specifies the first entry in
3849 * PATH which has changed.
3850 */
3851
3852static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003853clearcmdentry(int firstchange)
Eric Andersencb57d552001-06-28 07:25:16 +00003854{
3855 struct tblentry **tblp;
3856 struct tblentry **pp;
3857 struct tblentry *cmdp;
3858
3859 INTOFF;
3860 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
3861 pp = tblp;
3862 while ((cmdp = *pp) != NULL) {
3863 if ((cmdp->cmdtype == CMDNORMAL &&
3864 cmdp->param.index >= firstchange)
3865 || (cmdp->cmdtype == CMDBUILTIN &&
3866 builtinloc >= firstchange)) {
3867 *pp = cmdp->next;
3868 ckfree(cmdp);
3869 } else {
3870 pp = &cmdp->next;
3871 }
3872 }
3873 }
3874 INTON;
3875}
3876
3877
3878/*
3879 * Delete all functions.
3880 */
3881
Eric Andersencb57d552001-06-28 07:25:16 +00003882static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003883deletefuncs(void)
3884{
Eric Andersencb57d552001-06-28 07:25:16 +00003885 struct tblentry **tblp;
3886 struct tblentry **pp;
3887 struct tblentry *cmdp;
3888
3889 INTOFF;
3890 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
3891 pp = tblp;
3892 while ((cmdp = *pp) != NULL) {
3893 if (cmdp->cmdtype == CMDFUNCTION) {
3894 *pp = cmdp->next;
3895 freefunc(cmdp->param.func);
3896 ckfree(cmdp);
3897 } else {
3898 pp = &cmdp->next;
3899 }
3900 }
3901 }
3902 INTON;
3903}
3904
3905
3906
3907/*
3908 * Locate a command in the command hash table. If "add" is nonzero,
3909 * add the command to the table if it is not already present. The
3910 * variable "lastcmdentry" is set to point to the address of the link
3911 * pointing to the entry, so that delete_cmd_entry can delete the
3912 * entry.
3913 */
3914
Eric Andersen2870d962001-07-02 17:27:21 +00003915static struct tblentry **lastcmdentry;
Eric Andersencb57d552001-06-28 07:25:16 +00003916
3917static struct tblentry *
Eric Andersen2870d962001-07-02 17:27:21 +00003918cmdlookup(const char *name, int add)
Eric Andersencb57d552001-06-28 07:25:16 +00003919{
3920 int hashval;
Eric Andersen2870d962001-07-02 17:27:21 +00003921 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +00003922 struct tblentry *cmdp;
3923 struct tblentry **pp;
3924
3925 p = name;
3926 hashval = *p << 4;
3927 while (*p)
3928 hashval += *p++;
3929 hashval &= 0x7FFF;
3930 pp = &cmdtable[hashval % CMDTABLESIZE];
3931 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3932 if (equal(cmdp->cmdname, name))
3933 break;
3934 pp = &cmdp->next;
3935 }
3936 if (add && cmdp == NULL) {
3937 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003938 cmdp = *pp = xmalloc(sizeof (struct tblentry) - ARB
Eric Andersencb57d552001-06-28 07:25:16 +00003939 + strlen(name) + 1);
3940 cmdp->next = NULL;
3941 cmdp->cmdtype = CMDUNKNOWN;
3942 cmdp->rehash = 0;
3943 strcpy(cmdp->cmdname, name);
3944 INTON;
3945 }
3946 lastcmdentry = pp;
3947 return cmdp;
3948}
3949
3950/*
3951 * Delete the command entry returned on the last lookup.
3952 */
3953
3954static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003955delete_cmd_entry()
3956{
Eric Andersencb57d552001-06-28 07:25:16 +00003957 struct tblentry *cmdp;
3958
3959 INTOFF;
3960 cmdp = *lastcmdentry;
3961 *lastcmdentry = cmdp->next;
3962 ckfree(cmdp);
3963 INTON;
3964}
3965
3966
3967
Eric Andersencb57d552001-06-28 07:25:16 +00003968
3969
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003970static const unsigned char nodesize[26] = {
Eric Andersen62483552001-07-10 06:09:16 +00003971 ALIGN(sizeof (struct nbinary)),
3972 ALIGN(sizeof (struct ncmd)),
3973 ALIGN(sizeof (struct npipe)),
3974 ALIGN(sizeof (struct nredir)),
3975 ALIGN(sizeof (struct nredir)),
3976 ALIGN(sizeof (struct nredir)),
3977 ALIGN(sizeof (struct nbinary)),
3978 ALIGN(sizeof (struct nbinary)),
3979 ALIGN(sizeof (struct nif)),
3980 ALIGN(sizeof (struct nbinary)),
3981 ALIGN(sizeof (struct nbinary)),
3982 ALIGN(sizeof (struct nfor)),
3983 ALIGN(sizeof (struct ncase)),
3984 ALIGN(sizeof (struct nclist)),
3985 ALIGN(sizeof (struct narg)),
3986 ALIGN(sizeof (struct narg)),
3987 ALIGN(sizeof (struct nfile)),
3988 ALIGN(sizeof (struct nfile)),
3989 ALIGN(sizeof (struct nfile)),
3990 ALIGN(sizeof (struct nfile)),
3991 ALIGN(sizeof (struct nfile)),
3992 ALIGN(sizeof (struct ndup)),
3993 ALIGN(sizeof (struct ndup)),
3994 ALIGN(sizeof (struct nhere)),
3995 ALIGN(sizeof (struct nhere)),
3996 ALIGN(sizeof (struct nnot)),
3997};
Eric Andersencb57d552001-06-28 07:25:16 +00003998
Eric Andersencb57d552001-06-28 07:25:16 +00003999
4000
4001/*
4002 * Delete a function if it exists.
4003 */
4004
4005static void
Eric Andersen2870d962001-07-02 17:27:21 +00004006unsetfunc(char *name)
4007{
Eric Andersencb57d552001-06-28 07:25:16 +00004008 struct tblentry *cmdp;
4009
4010 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
4011 freefunc(cmdp->param.func);
4012 delete_cmd_entry();
4013 }
4014}
4015
Eric Andersen2870d962001-07-02 17:27:21 +00004016
4017/*
Eric Andersencb57d552001-06-28 07:25:16 +00004018 * Locate and print what a word is...
4019 */
4020
4021static int
Eric Andersen62483552001-07-10 06:09:16 +00004022typecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004023{
4024 int i;
4025 int err = 0;
Eric Andersen62483552001-07-10 06:09:16 +00004026 char *argv_a[2];
4027
4028 argv_a[1] = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004029
4030 for (i = 1; i < argc; i++) {
Eric Andersen62483552001-07-10 06:09:16 +00004031 argv_a[0] = argv[i];
4032 argptr = argv_a;
4033 optptr = "v";
4034 err |= hashcmd(argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00004035 }
4036 return err;
4037}
4038
Eric Andersend35c5df2002-01-09 15:37:36 +00004039#ifdef CONFIG_ASH_CMDCMD
Eric Andersencb57d552001-06-28 07:25:16 +00004040static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004041commandcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00004042{
4043 int c;
4044 int default_path = 0;
4045 int verify_only = 0;
4046 int verbose_verify_only = 0;
4047
4048 while ((c = nextopt("pvV")) != '\0')
4049 switch (c) {
4050 case 'p':
4051 default_path = 1;
4052 break;
4053 case 'v':
4054 verify_only = 1;
4055 break;
4056 case 'V':
4057 verbose_verify_only = 1;
4058 break;
Eric Andersencb57d552001-06-28 07:25:16 +00004059 }
4060
4061 if (default_path + verify_only + verbose_verify_only > 1 ||
4062 !*argptr) {
Eric Andersen62483552001-07-10 06:09:16 +00004063 out2str(
4064 "command [-p] command [arg ...]\n"
4065 "command {-v|-V} command\n");
Eric Andersencb57d552001-06-28 07:25:16 +00004066 return EX_USAGE;
4067 }
4068
Eric Andersencb57d552001-06-28 07:25:16 +00004069 if (verify_only || verbose_verify_only) {
Eric Andersen62483552001-07-10 06:09:16 +00004070 char *argv_a[2];
4071
4072 argv_a[1] = 0;
4073 argv_a[0] = *argptr;
4074 argptr = argv_a;
4075 optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
4076 return hashcmd(argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00004077 }
Eric Andersencb57d552001-06-28 07:25:16 +00004078
4079 return 0;
4080}
Eric Andersen2870d962001-07-02 17:27:21 +00004081#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004082
4083static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004084path_change(const char *newval, int *bltin)
Eric Andersencb57d552001-06-28 07:25:16 +00004085{
4086 const char *old, *new;
4087 int idx;
4088 int firstchange;
4089
4090 old = pathval();
4091 new = newval;
Eric Andersen2870d962001-07-02 17:27:21 +00004092 firstchange = 9999; /* assume no change */
Eric Andersencb57d552001-06-28 07:25:16 +00004093 idx = 0;
4094 *bltin = -1;
4095 for (;;) {
4096 if (*old != *new) {
4097 firstchange = idx;
4098 if ((*old == '\0' && *new == ':')
4099 || (*old == ':' && *new == '\0'))
4100 firstchange++;
Eric Andersen2870d962001-07-02 17:27:21 +00004101 old = new; /* ignore subsequent differences */
Eric Andersencb57d552001-06-28 07:25:16 +00004102 }
4103 if (*new == '\0')
4104 break;
4105 if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
4106 *bltin = idx;
4107 if (*new == ':') {
4108 idx++;
4109 }
4110 new++, old++;
4111 }
4112 if (builtinloc >= 0 && *bltin < 0)
4113 firstchange = 0;
4114 return firstchange;
4115}
Eric Andersencb57d552001-06-28 07:25:16 +00004116/*
4117 * Routines to expand arguments to commands. We have to deal with
4118 * backquotes, shell variables, and file metacharacters.
4119 */
4120/*
4121 * _rmescape() flags
4122 */
Eric Andersen2870d962001-07-02 17:27:21 +00004123#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4124#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Eric Andersencb57d552001-06-28 07:25:16 +00004125
4126/*
4127 * Structure specifying which parts of the string should be searched
4128 * for IFS characters.
4129 */
4130
4131struct ifsregion {
Eric Andersen2870d962001-07-02 17:27:21 +00004132 struct ifsregion *next; /* next region in list */
4133 int begoff; /* offset of start of region */
4134 int endoff; /* offset of end of region */
4135 int nulonly; /* search for nul bytes only */
Eric Andersencb57d552001-06-28 07:25:16 +00004136};
4137
4138
Eric Andersen2870d962001-07-02 17:27:21 +00004139static char *expdest; /* output of current string */
4140static struct nodelist *argbackq; /* list of back quote expressions */
4141static struct ifsregion ifsfirst; /* first struct in list of ifs regions */
4142static struct ifsregion *ifslastp; /* last struct in list */
4143static struct arglist exparg; /* holds expanded arg list */
Eric Andersencb57d552001-06-28 07:25:16 +00004144
Eric Andersen2870d962001-07-02 17:27:21 +00004145static void argstr (char *, int);
4146static char *exptilde (char *, int);
4147static void expbackq (union node *, int, int);
4148static int subevalvar (char *, char *, int, int, int, int, int);
Eric Andersen2870d962001-07-02 17:27:21 +00004149static int varisset (char *, int);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004150static void strtodest (const char *, int, int);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004151static inline void varvalue (char *, int, int);
Eric Andersen2870d962001-07-02 17:27:21 +00004152static void recordregion (int, int, int);
4153static void removerecordregions (int);
4154static void ifsbreakup (char *, struct arglist *);
4155static void ifsfree (void);
4156static void expandmeta (struct strlist *, int);
Eric Andersen62483552001-07-10 06:09:16 +00004157#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00004158#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
4159#if !defined(GLOB_BROKEN)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004160static inline void addglob (const glob_t *);
Eric Andersencb57d552001-06-28 07:25:16 +00004161#endif
4162#endif
Eric Andersen62483552001-07-10 06:09:16 +00004163#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersen2870d962001-07-02 17:27:21 +00004164static void expmeta (char *, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004165#endif
Eric Andersen62483552001-07-10 06:09:16 +00004166#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersen2870d962001-07-02 17:27:21 +00004167static struct strlist *expsort (struct strlist *);
4168static struct strlist *msort (struct strlist *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004169#endif
Eric Andersen2870d962001-07-02 17:27:21 +00004170static int patmatch (char *, char *, int);
Eric Andersen62483552001-07-10 06:09:16 +00004171#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00004172static int patmatch2 (char *, char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004173#else
Eric Andersen2870d962001-07-02 17:27:21 +00004174static int pmatch (char *, char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004175#define patmatch2 patmatch
4176#endif
Eric Andersen2870d962001-07-02 17:27:21 +00004177static char *cvtnum (int, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004178
4179/*
4180 * Expand shell variables and backquotes inside a here document.
4181 */
4182
Eric Andersen2870d962001-07-02 17:27:21 +00004183/* arg: the document, fd: where to write the expanded version */
Eric Andersen62483552001-07-10 06:09:16 +00004184static inline void
Eric Andersen2870d962001-07-02 17:27:21 +00004185expandhere(union node *arg, int fd)
4186{
Eric Andersencb57d552001-06-28 07:25:16 +00004187 herefd = fd;
4188 expandarg(arg, (struct arglist *)NULL, 0);
4189 xwrite(fd, stackblock(), expdest - stackblock());
4190}
4191
4192
4193/*
4194 * Perform variable substitution and command substitution on an argument,
4195 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4196 * perform splitting and file name expansion. When arglist is NULL, perform
4197 * here document expansion.
4198 */
4199
4200static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004201expandarg(union node *arg, struct arglist *arglist, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004202{
4203 struct strlist *sp;
4204 char *p;
4205
4206 argbackq = arg->narg.backquote;
4207 STARTSTACKSTR(expdest);
4208 ifsfirst.next = NULL;
4209 ifslastp = NULL;
4210 argstr(arg->narg.text, flag);
4211 if (arglist == NULL) {
Eric Andersen2870d962001-07-02 17:27:21 +00004212 return; /* here document expanded */
Eric Andersencb57d552001-06-28 07:25:16 +00004213 }
4214 STPUTC('\0', expdest);
4215 p = grabstackstr(expdest);
4216 exparg.lastp = &exparg.list;
4217 /*
4218 * TODO - EXP_REDIR
4219 */
4220 if (flag & EXP_FULL) {
4221 ifsbreakup(p, &exparg);
4222 *exparg.lastp = NULL;
4223 exparg.lastp = &exparg.list;
4224 expandmeta(exparg.list, flag);
4225 } else {
4226 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
4227 rmescapes(p);
4228 sp = (struct strlist *)stalloc(sizeof (struct strlist));
4229 sp->text = p;
4230 *exparg.lastp = sp;
4231 exparg.lastp = &sp->next;
4232 }
4233 ifsfree();
4234 *exparg.lastp = NULL;
4235 if (exparg.list) {
4236 *arglist->lastp = exparg.list;
4237 arglist->lastp = exparg.lastp;
4238 }
4239}
4240
4241
Eric Andersen62483552001-07-10 06:09:16 +00004242/*
4243 * Expand a variable, and return a pointer to the next character in the
4244 * input string.
4245 */
4246
Eric Andersen74400cc2001-10-18 04:11:39 +00004247static inline char * evalvar(char *p, int flag)
Eric Andersen62483552001-07-10 06:09:16 +00004248{
4249 int subtype;
4250 int varflags;
4251 char *var;
4252 const char *val;
4253 int patloc;
4254 int c;
4255 int set;
4256 int special;
4257 int startloc;
4258 int varlen;
4259 int easy;
4260 int quotes = flag & (EXP_FULL | EXP_CASE);
4261
4262 varflags = *p++;
4263 subtype = varflags & VSTYPE;
4264 var = p;
4265 special = 0;
4266 if (! is_name(*p))
4267 special = 1;
4268 p = strchr(p, '=') + 1;
4269again: /* jump here after setting a variable with ${var=text} */
4270 if (special) {
4271 set = varisset(var, varflags & VSNUL);
4272 val = NULL;
4273 } else {
4274 val = lookupvar(var);
4275 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
4276 val = NULL;
4277 set = 0;
4278 } else
4279 set = 1;
4280 }
4281 varlen = 0;
4282 startloc = expdest - stackblock();
4283 if (set && subtype != VSPLUS) {
4284 /* insert the value of the variable */
4285 if (special) {
4286 varvalue(var, varflags & VSQUOTE, flag);
4287 if (subtype == VSLENGTH) {
4288 varlen = expdest - stackblock() - startloc;
4289 STADJUST(-varlen, expdest);
4290 }
4291 } else {
4292 if (subtype == VSLENGTH) {
4293 varlen = strlen(val);
4294 } else {
4295 strtodest(
4296 val,
4297 varflags & VSQUOTE ?
4298 DQSYNTAX : BASESYNTAX,
4299 quotes
4300 );
4301 }
4302 }
4303 }
4304
4305 if (subtype == VSPLUS)
4306 set = ! set;
4307
4308 easy = ((varflags & VSQUOTE) == 0 ||
4309 (*var == '@' && shellparam.nparam != 1));
4310
4311
4312 switch (subtype) {
4313 case VSLENGTH:
4314 expdest = cvtnum(varlen, expdest);
4315 goto record;
4316
4317 case VSNORMAL:
4318 if (!easy)
4319 break;
4320record:
4321 recordregion(startloc, expdest - stackblock(),
4322 varflags & VSQUOTE);
4323 break;
4324
4325 case VSPLUS:
4326 case VSMINUS:
4327 if (!set) {
4328 argstr(p, flag);
4329 break;
4330 }
4331 if (easy)
4332 goto record;
4333 break;
4334
4335 case VSTRIMLEFT:
4336 case VSTRIMLEFTMAX:
4337 case VSTRIMRIGHT:
4338 case VSTRIMRIGHTMAX:
4339 if (!set)
4340 break;
4341 /*
4342 * Terminate the string and start recording the pattern
4343 * right after it
4344 */
4345 STPUTC('\0', expdest);
4346 patloc = expdest - stackblock();
4347 if (subevalvar(p, NULL, patloc, subtype,
4348 startloc, varflags, quotes) == 0) {
4349 int amount = (expdest - stackblock() - patloc) + 1;
4350 STADJUST(-amount, expdest);
4351 }
4352 /* Remove any recorded regions beyond start of variable */
4353 removerecordregions(startloc);
4354 goto record;
4355
4356 case VSASSIGN:
4357 case VSQUESTION:
4358 if (!set) {
4359 if (subevalvar(p, var, 0, subtype, startloc,
4360 varflags, quotes)) {
4361 varflags &= ~VSNUL;
4362 /*
4363 * Remove any recorded regions beyond
4364 * start of variable
4365 */
4366 removerecordregions(startloc);
4367 goto again;
4368 }
4369 break;
4370 }
4371 if (easy)
4372 goto record;
4373 break;
4374
4375#ifdef DEBUG
4376 default:
4377 abort();
4378#endif
4379 }
4380
4381 if (subtype != VSNORMAL) { /* skip to end of alternative */
4382 int nesting = 1;
4383 for (;;) {
4384 if ((c = *p++) == CTLESC)
4385 p++;
4386 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
4387 if (set)
4388 argbackq = argbackq->next;
4389 } else if (c == CTLVAR) {
4390 if ((*p++ & VSTYPE) != VSNORMAL)
4391 nesting++;
4392 } else if (c == CTLENDVAR) {
4393 if (--nesting == 0)
4394 break;
4395 }
4396 }
4397 }
4398 return p;
4399}
4400
Eric Andersencb57d552001-06-28 07:25:16 +00004401
4402/*
4403 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4404 * characters to allow for further processing. Otherwise treat
4405 * $@ like $* since no splitting will be performed.
4406 */
4407
4408static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004409argstr(char *p, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004410{
4411 char c;
Eric Andersen2870d962001-07-02 17:27:21 +00004412 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
Eric Andersencb57d552001-06-28 07:25:16 +00004413 int firsteq = 1;
4414
4415 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
4416 p = exptilde(p, flag);
4417 for (;;) {
4418 switch (c = *p++) {
4419 case '\0':
4420 case CTLENDVAR: /* ??? */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004421 return;
Eric Andersencb57d552001-06-28 07:25:16 +00004422 case CTLQUOTEMARK:
4423 /* "$@" syntax adherence hack */
4424 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
4425 break;
4426 if ((flag & EXP_FULL) != 0)
4427 STPUTC(c, expdest);
4428 break;
4429 case CTLESC:
4430 if (quotes)
4431 STPUTC(c, expdest);
4432 c = *p++;
4433 STPUTC(c, expdest);
4434 break;
4435 case CTLVAR:
4436 p = evalvar(p, flag);
4437 break;
4438 case CTLBACKQ:
4439 case CTLBACKQ|CTLQUOTE:
4440 expbackq(argbackq->n, c & CTLQUOTE, flag);
4441 argbackq = argbackq->next;
4442 break;
Eric Andersend35c5df2002-01-09 15:37:36 +00004443#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00004444 case CTLENDARI:
4445 expari(flag);
4446 break;
Eric Andersen2870d962001-07-02 17:27:21 +00004447#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004448 case ':':
4449 case '=':
4450 /*
4451 * sort of a hack - expand tildes in variable
4452 * assignments (after the first '=' and after ':'s).
4453 */
4454 STPUTC(c, expdest);
4455 if (flag & EXP_VARTILDE && *p == '~') {
4456 if (c == '=') {
4457 if (firsteq)
4458 firsteq = 0;
4459 else
4460 break;
4461 }
4462 p = exptilde(p, flag);
4463 }
4464 break;
4465 default:
4466 STPUTC(c, expdest);
4467 }
4468 }
Eric Andersencb57d552001-06-28 07:25:16 +00004469 return;
4470}
4471
4472static char *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004473exptilde(char *p, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004474{
4475 char c, *startp = p;
4476 struct passwd *pw;
4477 const char *home;
4478 int quotes = flag & (EXP_FULL | EXP_CASE);
4479
4480 while ((c = *p) != '\0') {
4481 switch(c) {
4482 case CTLESC:
4483 return (startp);
4484 case CTLQUOTEMARK:
4485 return (startp);
4486 case ':':
4487 if (flag & EXP_VARTILDE)
4488 goto done;
4489 break;
4490 case '/':
4491 goto done;
4492 }
4493 p++;
4494 }
4495done:
4496 *p = '\0';
4497 if (*(startp+1) == '\0') {
4498 if ((home = lookupvar("HOME")) == NULL)
4499 goto lose;
4500 } else {
4501 if ((pw = getpwnam(startp+1)) == NULL)
4502 goto lose;
4503 home = pw->pw_dir;
4504 }
4505 if (*home == '\0')
4506 goto lose;
4507 *p = c;
4508 strtodest(home, SQSYNTAX, quotes);
4509 return (p);
4510lose:
4511 *p = c;
4512 return (startp);
4513}
4514
4515
Eric Andersen2870d962001-07-02 17:27:21 +00004516static void
4517removerecordregions(int endoff)
Eric Andersencb57d552001-06-28 07:25:16 +00004518{
4519 if (ifslastp == NULL)
4520 return;
4521
4522 if (ifsfirst.endoff > endoff) {
4523 while (ifsfirst.next != NULL) {
4524 struct ifsregion *ifsp;
4525 INTOFF;
4526 ifsp = ifsfirst.next->next;
4527 ckfree(ifsfirst.next);
4528 ifsfirst.next = ifsp;
4529 INTON;
4530 }
4531 if (ifsfirst.begoff > endoff)
4532 ifslastp = NULL;
4533 else {
4534 ifslastp = &ifsfirst;
4535 ifsfirst.endoff = endoff;
4536 }
4537 return;
4538 }
Eric Andersen2870d962001-07-02 17:27:21 +00004539
Eric Andersencb57d552001-06-28 07:25:16 +00004540 ifslastp = &ifsfirst;
4541 while (ifslastp->next && ifslastp->next->begoff < endoff)
4542 ifslastp=ifslastp->next;
4543 while (ifslastp->next != NULL) {
4544 struct ifsregion *ifsp;
4545 INTOFF;
4546 ifsp = ifslastp->next->next;
4547 ckfree(ifslastp->next);
4548 ifslastp->next = ifsp;
4549 INTON;
4550 }
4551 if (ifslastp->endoff > endoff)
4552 ifslastp->endoff = endoff;
4553}
4554
4555
Eric Andersend35c5df2002-01-09 15:37:36 +00004556#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00004557/*
4558 * Expand arithmetic expression. Backup to start of expression,
4559 * evaluate, place result in (backed up) result, adjust string position.
4560 */
4561static void
Eric Andersen2870d962001-07-02 17:27:21 +00004562expari(int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004563{
4564 char *p, *start;
Eric Andersen34506362001-08-02 05:02:46 +00004565 int errcode;
Eric Andersencb57d552001-06-28 07:25:16 +00004566 int result;
4567 int begoff;
4568 int quotes = flag & (EXP_FULL | EXP_CASE);
4569 int quoted;
4570
Eric Andersen2870d962001-07-02 17:27:21 +00004571 /* ifsfree(); */
Eric Andersencb57d552001-06-28 07:25:16 +00004572
4573 /*
4574 * This routine is slightly over-complicated for
4575 * efficiency. First we make sure there is
4576 * enough space for the result, which may be bigger
4577 * than the expression if we add exponentation. Next we
4578 * scan backwards looking for the start of arithmetic. If the
4579 * next previous character is a CTLESC character, then we
4580 * have to rescan starting from the beginning since CTLESC
4581 * characters have to be processed left to right.
4582 */
4583 CHECKSTRSPACE(10, expdest);
4584 USTPUTC('\0', expdest);
4585 start = stackblock();
4586 p = expdest - 1;
4587 while (*p != CTLARI && p >= start)
4588 --p;
4589 if (*p != CTLARI)
4590 error("missing CTLARI (shouldn't happen)");
4591 if (p > start && *(p-1) == CTLESC)
4592 for (p = start; *p != CTLARI; p++)
4593 if (*p == CTLESC)
4594 p++;
4595
4596 if (p[1] == '"')
4597 quoted=1;
4598 else
4599 quoted=0;
4600 begoff = p - start;
4601 removerecordregions(begoff);
4602 if (quotes)
4603 rmescapes(p+2);
Eric Andersen34506362001-08-02 05:02:46 +00004604 result = arith(p+2, &errcode);
4605 if (errcode < 0) {
4606 if(errcode == -2)
4607 error("divide by zero");
4608 else
4609 error("syntax error: \"%s\"\n", p+2);
4610 }
Eric Andersen3102ac42001-07-06 04:26:23 +00004611 snprintf(p, 12, "%d", result);
Eric Andersencb57d552001-06-28 07:25:16 +00004612
4613 while (*p++)
4614 ;
4615
4616 if (quoted == 0)
4617 recordregion(begoff, p - 1 - start, 0);
4618 result = expdest - p + 1;
4619 STADJUST(-result, expdest);
4620}
Eric Andersen2870d962001-07-02 17:27:21 +00004621#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004622
4623/*
4624 * Expand stuff in backwards quotes.
4625 */
4626
4627static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004628expbackq(union node *cmd, int quoted, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004629{
4630 volatile struct backcmd in;
4631 int i;
4632 char buf[128];
4633 char *p;
4634 char *dest = expdest;
4635 volatile struct ifsregion saveifs;
4636 struct ifsregion *volatile savelastp;
4637 struct nodelist *volatile saveargbackq;
4638 char lastc;
4639 int startloc = dest - stackblock();
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004640 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Eric Andersencb57d552001-06-28 07:25:16 +00004641 volatile int saveherefd;
4642 int quotes = flag & (EXP_FULL | EXP_CASE);
4643 struct jmploc jmploc;
4644 struct jmploc *volatile savehandler;
4645 int ex;
4646
4647#if __GNUC__
4648 /* Avoid longjmp clobbering */
4649 (void) &dest;
4650 (void) &syntax;
4651#endif
4652
4653 in.fd = -1;
4654 in.buf = 0;
4655 in.jp = 0;
4656
4657 INTOFF;
4658 saveifs = ifsfirst;
4659 savelastp = ifslastp;
4660 saveargbackq = argbackq;
4661 saveherefd = herefd;
4662 herefd = -1;
4663 if ((ex = setjmp(jmploc.loc))) {
4664 goto err1;
4665 }
4666 savehandler = handler;
4667 handler = &jmploc;
4668 INTON;
4669 p = grabstackstr(dest);
4670 evalbackcmd(cmd, (struct backcmd *) &in);
4671 ungrabstackstr(p, dest);
4672err1:
4673 INTOFF;
4674 ifsfirst = saveifs;
4675 ifslastp = savelastp;
4676 argbackq = saveargbackq;
4677 herefd = saveherefd;
4678 if (ex) {
4679 goto err2;
4680 }
4681
4682 p = in.buf;
4683 lastc = '\0';
4684 for (;;) {
4685 if (--in.nleft < 0) {
4686 if (in.fd < 0)
4687 break;
Eric Andersen7467c8d2001-07-12 20:26:32 +00004688 i = safe_read(in.fd, buf, sizeof buf);
Eric Andersencb57d552001-06-28 07:25:16 +00004689 TRACE(("expbackq: read returns %d\n", i));
4690 if (i <= 0)
4691 break;
4692 p = buf;
4693 in.nleft = i - 1;
4694 }
4695 lastc = *p++;
4696 if (lastc != '\0') {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004697 if (quotes && SIT(lastc, syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00004698 STPUTC(CTLESC, dest);
4699 STPUTC(lastc, dest);
4700 }
4701 }
4702
4703 /* Eat all trailing newlines */
4704 for (; dest > stackblock() && dest[-1] == '\n';)
4705 STUNPUTC(dest);
4706
4707err2:
4708 if (in.fd >= 0)
4709 close(in.fd);
4710 if (in.buf)
4711 ckfree(in.buf);
4712 if (in.jp)
4713 exitstatus = waitforjob(in.jp);
4714 handler = savehandler;
4715 if (ex) {
4716 longjmp(handler->loc, 1);
4717 }
4718 if (quoted == 0)
4719 recordregion(startloc, dest - stackblock(), 0);
4720 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4721 (dest - stackblock()) - startloc,
4722 (dest - stackblock()) - startloc,
4723 stackblock() + startloc));
4724 expdest = dest;
4725 INTON;
4726}
4727
Eric Andersencb57d552001-06-28 07:25:16 +00004728static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004729subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
Eric Andersencb57d552001-06-28 07:25:16 +00004730{
4731 char *startp;
4732 char *loc = NULL;
4733 char *q;
4734 int c = 0;
4735 int saveherefd = herefd;
4736 struct nodelist *saveargbackq = argbackq;
4737 int amount;
4738
4739 herefd = -1;
4740 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
4741 STACKSTRNUL(expdest);
4742 herefd = saveherefd;
4743 argbackq = saveargbackq;
4744 startp = stackblock() + startloc;
4745 if (str == NULL)
4746 str = stackblock() + strloc;
4747
4748 switch (subtype) {
4749 case VSASSIGN:
4750 setvar(str, startp, 0);
4751 amount = startp - expdest;
4752 STADJUST(amount, expdest);
4753 varflags &= ~VSNUL;
4754 if (c != 0)
4755 *loc = c;
4756 return 1;
4757
4758 case VSQUESTION:
4759 if (*p != CTLENDVAR) {
Eric Andersen3102ac42001-07-06 04:26:23 +00004760 out2fmt(snlfmt, startp);
Eric Andersencb57d552001-06-28 07:25:16 +00004761 error((char *)NULL);
4762 }
4763 error("%.*s: parameter %snot set", p - str - 1,
4764 str, (varflags & VSNUL) ? "null or "
4765 : nullstr);
4766 /* NOTREACHED */
4767
4768 case VSTRIMLEFT:
4769 for (loc = startp; loc < str; loc++) {
4770 c = *loc;
4771 *loc = '\0';
4772 if (patmatch2(str, startp, quotes))
4773 goto recordleft;
4774 *loc = c;
4775 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00004776 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00004777 }
4778 return 0;
4779
4780 case VSTRIMLEFTMAX:
4781 for (loc = str - 1; loc >= startp;) {
4782 c = *loc;
4783 *loc = '\0';
4784 if (patmatch2(str, startp, quotes))
4785 goto recordleft;
4786 *loc = c;
4787 loc--;
4788 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
4789 for (q = startp; q < loc; q++)
4790 if (*q == CTLESC)
4791 q++;
4792 if (q > loc)
4793 loc--;
4794 }
4795 }
4796 return 0;
4797
4798 case VSTRIMRIGHT:
Eric Andersen2870d962001-07-02 17:27:21 +00004799 for (loc = str - 1; loc >= startp;) {
Eric Andersencb57d552001-06-28 07:25:16 +00004800 if (patmatch2(str, loc, quotes))
4801 goto recordright;
4802 loc--;
Eric Andersen2870d962001-07-02 17:27:21 +00004803 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
Eric Andersencb57d552001-06-28 07:25:16 +00004804 for (q = startp; q < loc; q++)
4805 if (*q == CTLESC)
4806 q++;
4807 if (q > loc)
4808 loc--;
4809 }
4810 }
4811 return 0;
4812
4813 case VSTRIMRIGHTMAX:
4814 for (loc = startp; loc < str - 1; loc++) {
4815 if (patmatch2(str, loc, quotes))
4816 goto recordright;
4817 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00004818 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00004819 }
4820 return 0;
4821
4822#ifdef DEBUG
4823 default:
4824 abort();
4825#endif
4826 }
4827
4828recordleft:
4829 *loc = c;
4830 amount = ((str - 1) - (loc - startp)) - expdest;
4831 STADJUST(amount, expdest);
4832 while (loc != str - 1)
4833 *startp++ = *loc++;
4834 return 1;
4835
4836recordright:
4837 amount = loc - expdest;
4838 STADJUST(amount, expdest);
4839 STPUTC('\0', expdest);
4840 STADJUST(-1, expdest);
4841 return 1;
4842}
4843
4844
4845/*
Eric Andersencb57d552001-06-28 07:25:16 +00004846 * Test whether a specialized variable is set.
4847 */
4848
4849static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004850varisset(char *name, int nulok)
Eric Andersencb57d552001-06-28 07:25:16 +00004851{
4852 if (*name == '!')
4853 return backgndpid != -1;
4854 else if (*name == '@' || *name == '*') {
4855 if (*shellparam.p == NULL)
4856 return 0;
4857
4858 if (nulok) {
4859 char **av;
4860
4861 for (av = shellparam.p; *av; av++)
4862 if (**av != '\0')
4863 return 1;
4864 return 0;
4865 }
4866 } else if (is_digit(*name)) {
4867 char *ap;
4868 int num = atoi(name);
4869
4870 if (num > shellparam.nparam)
4871 return 0;
4872
4873 if (num == 0)
4874 ap = arg0;
4875 else
4876 ap = shellparam.p[num - 1];
4877
4878 if (nulok && (ap == NULL || *ap == '\0'))
4879 return 0;
4880 }
4881 return 1;
4882}
4883
Eric Andersencb57d552001-06-28 07:25:16 +00004884/*
4885 * Put a string on the stack.
4886 */
4887
4888static void
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004889strtodest(const char *p, int syntax, int quotes)
Eric Andersencb57d552001-06-28 07:25:16 +00004890{
4891 while (*p) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004892 if (quotes && SIT(*p,syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00004893 STPUTC(CTLESC, expdest);
4894 STPUTC(*p++, expdest);
4895 }
4896}
4897
Eric Andersencb57d552001-06-28 07:25:16 +00004898/*
4899 * Add the value of a specialized variable to the stack string.
4900 */
4901
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004902static inline void
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004903varvalue(char *name, int quoted, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00004904{
4905 int num;
4906 char *p;
4907 int i;
4908 int sep;
4909 int sepq = 0;
4910 char **ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004911 int syntax;
Eric Andersencb57d552001-06-28 07:25:16 +00004912 int allow_split = flags & EXP_FULL;
4913 int quotes = flags & (EXP_FULL | EXP_CASE);
4914
4915 syntax = quoted ? DQSYNTAX : BASESYNTAX;
4916 switch (*name) {
4917 case '$':
4918 num = rootpid;
4919 goto numvar;
4920 case '?':
4921 num = oexitstatus;
4922 goto numvar;
4923 case '#':
4924 num = shellparam.nparam;
4925 goto numvar;
4926 case '!':
4927 num = backgndpid;
4928numvar:
4929 expdest = cvtnum(num, expdest);
4930 break;
4931 case '-':
4932 for (i = 0 ; i < NOPTS ; i++) {
Eric Andersen2870d962001-07-02 17:27:21 +00004933 if (optent_val(i))
4934 STPUTC(optent_letter(optlist[i]), expdest);
Eric Andersencb57d552001-06-28 07:25:16 +00004935 }
4936 break;
4937 case '@':
4938 if (allow_split && quoted) {
4939 sep = 1 << CHAR_BIT;
4940 goto param;
4941 }
4942 /* fall through */
4943 case '*':
4944 sep = ifsset() ? ifsval()[0] : ' ';
4945 if (quotes) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004946 sepq = SIT(sep,syntax) == CCTL;
Eric Andersencb57d552001-06-28 07:25:16 +00004947 }
4948param:
4949 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
4950 strtodest(p, syntax, quotes);
4951 if (*ap && sep) {
4952 if (sepq)
4953 STPUTC(CTLESC, expdest);
4954 STPUTC(sep, expdest);
4955 }
4956 }
4957 break;
4958 case '0':
4959 strtodest(arg0, syntax, quotes);
4960 break;
4961 default:
4962 num = atoi(name);
4963 if (num > 0 && num <= shellparam.nparam) {
4964 strtodest(shellparam.p[num - 1], syntax, quotes);
4965 }
4966 break;
4967 }
4968}
4969
4970
Eric Andersencb57d552001-06-28 07:25:16 +00004971/*
4972 * Record the fact that we have to scan this region of the
4973 * string for IFS characters.
4974 */
4975
4976static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004977recordregion(int start, int end, int nulonly)
Eric Andersencb57d552001-06-28 07:25:16 +00004978{
4979 struct ifsregion *ifsp;
4980
4981 if (ifslastp == NULL) {
4982 ifsp = &ifsfirst;
4983 } else {
4984 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004985 ifsp = (struct ifsregion *)xmalloc(sizeof (struct ifsregion));
Eric Andersencb57d552001-06-28 07:25:16 +00004986 ifsp->next = NULL;
4987 ifslastp->next = ifsp;
4988 INTON;
4989 }
4990 ifslastp = ifsp;
4991 ifslastp->begoff = start;
4992 ifslastp->endoff = end;
4993 ifslastp->nulonly = nulonly;
4994}
4995
4996
4997
4998/*
4999 * Break the argument string into pieces based upon IFS and add the
5000 * strings to the argument list. The regions of the string to be
5001 * searched for IFS characters have been stored by recordregion.
5002 */
5003static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005004ifsbreakup(char *string, struct arglist *arglist)
5005{
Eric Andersencb57d552001-06-28 07:25:16 +00005006 struct ifsregion *ifsp;
5007 struct strlist *sp;
5008 char *start;
5009 char *p;
5010 char *q;
5011 const char *ifs, *realifs;
5012 int ifsspc;
5013 int nulonly;
5014
5015
5016 start = string;
5017 ifsspc = 0;
5018 nulonly = 0;
5019 realifs = ifsset() ? ifsval() : defifs;
5020 if (ifslastp != NULL) {
5021 ifsp = &ifsfirst;
5022 do {
5023 p = string + ifsp->begoff;
5024 nulonly = ifsp->nulonly;
5025 ifs = nulonly ? nullstr : realifs;
5026 ifsspc = 0;
5027 while (p < string + ifsp->endoff) {
5028 q = p;
5029 if (*p == CTLESC)
5030 p++;
5031 if (strchr(ifs, *p)) {
5032 if (!nulonly)
5033 ifsspc = (strchr(defifs, *p) != NULL);
5034 /* Ignore IFS whitespace at start */
5035 if (q == start && ifsspc) {
5036 p++;
5037 start = p;
5038 continue;
5039 }
5040 *q = '\0';
5041 sp = (struct strlist *)stalloc(sizeof *sp);
5042 sp->text = start;
5043 *arglist->lastp = sp;
5044 arglist->lastp = &sp->next;
5045 p++;
5046 if (!nulonly) {
5047 for (;;) {
5048 if (p >= string + ifsp->endoff) {
5049 break;
5050 }
5051 q = p;
5052 if (*p == CTLESC)
5053 p++;
5054 if (strchr(ifs, *p) == NULL ) {
5055 p = q;
5056 break;
5057 } else if (strchr(defifs, *p) == NULL) {
5058 if (ifsspc) {
5059 p++;
5060 ifsspc = 0;
5061 } else {
5062 p = q;
5063 break;
5064 }
5065 } else
5066 p++;
5067 }
5068 }
5069 start = p;
5070 } else
5071 p++;
5072 }
5073 } while ((ifsp = ifsp->next) != NULL);
5074 if (!(*start || (!ifsspc && start > string && nulonly))) {
5075 return;
5076 }
5077 }
5078
5079 sp = (struct strlist *)stalloc(sizeof *sp);
5080 sp->text = start;
5081 *arglist->lastp = sp;
5082 arglist->lastp = &sp->next;
5083}
5084
5085static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005086ifsfree(void)
Eric Andersencb57d552001-06-28 07:25:16 +00005087{
5088 while (ifsfirst.next != NULL) {
5089 struct ifsregion *ifsp;
5090 INTOFF;
5091 ifsp = ifsfirst.next->next;
5092 ckfree(ifsfirst.next);
5093 ifsfirst.next = ifsp;
5094 INTON;
5095 }
5096 ifslastp = NULL;
5097 ifsfirst.next = NULL;
5098}
5099
Eric Andersen2870d962001-07-02 17:27:21 +00005100/*
5101 * Add a file name to the list.
5102 */
Eric Andersencb57d552001-06-28 07:25:16 +00005103
Eric Andersen2870d962001-07-02 17:27:21 +00005104static void
5105addfname(const char *name)
5106{
Eric Andersen2870d962001-07-02 17:27:21 +00005107 struct strlist *sp;
5108
Eric Andersen2870d962001-07-02 17:27:21 +00005109 sp = (struct strlist *)stalloc(sizeof *sp);
Aaron Lehmann95877b62001-12-31 06:00:57 +00005110 sp->text = sstrdup(name);
Eric Andersen2870d962001-07-02 17:27:21 +00005111 *exparg.lastp = sp;
5112 exparg.lastp = &sp->next;
5113}
Eric Andersencb57d552001-06-28 07:25:16 +00005114
5115/*
5116 * Expand shell metacharacters. At this point, the only control characters
5117 * should be escapes. The results are stored in the list exparg.
5118 */
5119
Eric Andersen62483552001-07-10 06:09:16 +00005120#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00005121static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005122expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005123{
5124 const char *p;
5125 glob_t pglob;
5126 /* TODO - EXP_REDIR */
5127
5128 while (str) {
5129 if (fflag)
5130 goto nometa;
5131 p = preglob(str->text);
5132 INTOFF;
Eric Andersen34506362001-08-02 05:02:46 +00005133 switch (glob(p, 0, 0, &pglob)) {
Eric Andersencb57d552001-06-28 07:25:16 +00005134 case 0:
Eric Andersen34506362001-08-02 05:02:46 +00005135 if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0]))
Eric Andersencb57d552001-06-28 07:25:16 +00005136 goto nometa2;
5137 addglob(&pglob);
5138 globfree(&pglob);
5139 INTON;
5140 break;
5141 case GLOB_NOMATCH:
5142nometa2:
5143 globfree(&pglob);
5144 INTON;
5145nometa:
5146 *exparg.lastp = str;
5147 rmescapes(str->text);
5148 exparg.lastp = &str->next;
5149 break;
Eric Andersen2870d962001-07-02 17:27:21 +00005150 default: /* GLOB_NOSPACE */
Eric Andersencb57d552001-06-28 07:25:16 +00005151 error("Out of space");
5152 }
5153 str = str->next;
5154 }
5155}
5156
5157
5158/*
5159 * Add the result of glob(3) to the list.
5160 */
5161
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005162static inline void
5163addglob(const glob_t *pglob)
Eric Andersencb57d552001-06-28 07:25:16 +00005164{
5165 char **p = pglob->gl_pathv;
5166
5167 do {
5168 addfname(*p);
5169 } while (*++p);
5170}
5171
5172
Eric Andersen2870d962001-07-02 17:27:21 +00005173#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005174static char *expdir;
5175
5176
5177static void
Eric Andersenceef50b2001-12-21 11:22:26 +00005178expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005179{
5180 char *p;
5181 struct strlist **savelastp;
5182 struct strlist *sp;
5183 char c;
5184 /* TODO - EXP_REDIR */
5185
5186 while (str) {
5187 if (fflag)
5188 goto nometa;
5189 p = str->text;
Eric Andersen2870d962001-07-02 17:27:21 +00005190 for (;;) { /* fast check for meta chars */
Eric Andersencb57d552001-06-28 07:25:16 +00005191 if ((c = *p++) == '\0')
5192 goto nometa;
5193 if (c == '*' || c == '?' || c == '[' || c == '!')
5194 break;
5195 }
5196 savelastp = exparg.lastp;
5197 INTOFF;
5198 if (expdir == NULL) {
5199 int i = strlen(str->text);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005200 expdir = xmalloc(i < 2048 ? 2048 : i); /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00005201 }
5202
5203 expmeta(expdir, str->text);
5204 ckfree(expdir);
5205 expdir = NULL;
5206 INTON;
5207 if (exparg.lastp == savelastp) {
5208 /*
5209 * no matches
5210 */
5211nometa:
5212 *exparg.lastp = str;
5213 rmescapes(str->text);
5214 exparg.lastp = &str->next;
5215 } else {
5216 *exparg.lastp = NULL;
5217 *savelastp = sp = expsort(*savelastp);
5218 while (sp->next != NULL)
5219 sp = sp->next;
5220 exparg.lastp = &sp->next;
5221 }
5222 str = str->next;
5223 }
5224}
5225
5226
5227/*
5228 * Do metacharacter (i.e. *, ?, [...]) expansion.
5229 */
5230
5231static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005232expmeta(char *enddir, char *name)
5233{
Eric Andersencb57d552001-06-28 07:25:16 +00005234 char *p;
5235 const char *cp;
5236 char *q;
5237 char *start;
5238 char *endname;
5239 int metaflag;
5240 struct stat statb;
5241 DIR *dirp;
5242 struct dirent *dp;
5243 int atend;
5244 int matchdot;
5245
5246 metaflag = 0;
5247 start = name;
5248 for (p = name ; ; p++) {
5249 if (*p == '*' || *p == '?')
5250 metaflag = 1;
5251 else if (*p == '[') {
5252 q = p + 1;
5253 if (*q == '!')
5254 q++;
5255 for (;;) {
5256 while (*q == CTLQUOTEMARK)
5257 q++;
5258 if (*q == CTLESC)
5259 q++;
5260 if (*q == '/' || *q == '\0')
5261 break;
5262 if (*++q == ']') {
5263 metaflag = 1;
5264 break;
5265 }
5266 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005267 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
Eric Andersencb57d552001-06-28 07:25:16 +00005268 metaflag = 1;
5269 } else if (*p == '\0')
5270 break;
5271 else if (*p == CTLQUOTEMARK)
5272 continue;
5273 else if (*p == CTLESC)
5274 p++;
5275 if (*p == '/') {
5276 if (metaflag)
5277 break;
5278 start = p + 1;
5279 }
5280 }
Eric Andersen2870d962001-07-02 17:27:21 +00005281 if (metaflag == 0) { /* we've reached the end of the file name */
Eric Andersencb57d552001-06-28 07:25:16 +00005282 if (enddir != expdir)
5283 metaflag++;
5284 for (p = name ; ; p++) {
5285 if (*p == CTLQUOTEMARK)
5286 continue;
5287 if (*p == CTLESC)
5288 p++;
5289 *enddir++ = *p;
5290 if (*p == '\0')
5291 break;
5292 }
5293 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
5294 addfname(expdir);
5295 return;
5296 }
5297 endname = p;
5298 if (start != name) {
5299 p = name;
5300 while (p < start) {
5301 while (*p == CTLQUOTEMARK)
5302 p++;
5303 if (*p == CTLESC)
5304 p++;
5305 *enddir++ = *p++;
5306 }
5307 }
5308 if (enddir == expdir) {
5309 cp = ".";
5310 } else if (enddir == expdir + 1 && *expdir == '/') {
5311 cp = "/";
5312 } else {
5313 cp = expdir;
5314 enddir[-1] = '\0';
5315 }
5316 if ((dirp = opendir(cp)) == NULL)
5317 return;
5318 if (enddir != expdir)
5319 enddir[-1] = '/';
5320 if (*endname == 0) {
5321 atend = 1;
5322 } else {
5323 atend = 0;
5324 *endname++ = '\0';
5325 }
5326 matchdot = 0;
5327 p = start;
5328 while (*p == CTLQUOTEMARK)
5329 p++;
5330 if (*p == CTLESC)
5331 p++;
5332 if (*p == '.')
5333 matchdot++;
5334 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
5335 if (dp->d_name[0] == '.' && ! matchdot)
5336 continue;
5337 if (patmatch(start, dp->d_name, 0)) {
5338 if (atend) {
Eric Andersen2870d962001-07-02 17:27:21 +00005339 strcpy(enddir, dp->d_name);
Eric Andersencb57d552001-06-28 07:25:16 +00005340 addfname(expdir);
5341 } else {
5342 for (p = enddir, cp = dp->d_name;
5343 (*p++ = *cp++) != '\0';)
5344 continue;
5345 p[-1] = '/';
5346 expmeta(p, endname);
5347 }
5348 }
5349 }
5350 closedir(dirp);
5351 if (! atend)
5352 endname[-1] = '/';
5353}
Eric Andersen2870d962001-07-02 17:27:21 +00005354#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005355
5356
Eric Andersencb57d552001-06-28 07:25:16 +00005357
Eric Andersen62483552001-07-10 06:09:16 +00005358#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersencb57d552001-06-28 07:25:16 +00005359/*
5360 * Sort the results of file name expansion. It calculates the number of
5361 * strings to sort and then calls msort (short for merge sort) to do the
5362 * work.
5363 */
5364
5365static struct strlist *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005366expsort(struct strlist *str)
5367{
Eric Andersencb57d552001-06-28 07:25:16 +00005368 int len;
5369 struct strlist *sp;
5370
5371 len = 0;
5372 for (sp = str ; sp ; sp = sp->next)
5373 len++;
5374 return msort(str, len);
5375}
5376
5377
5378static struct strlist *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005379msort(struct strlist *list, int len)
Eric Andersencb57d552001-06-28 07:25:16 +00005380{
5381 struct strlist *p, *q = NULL;
5382 struct strlist **lpp;
5383 int half;
5384 int n;
5385
5386 if (len <= 1)
5387 return list;
5388 half = len >> 1;
5389 p = list;
5390 for (n = half ; --n >= 0 ; ) {
5391 q = p;
5392 p = p->next;
5393 }
Eric Andersen2870d962001-07-02 17:27:21 +00005394 q->next = NULL; /* terminate first half of list */
5395 q = msort(list, half); /* sort first half of list */
5396 p = msort(p, len - half); /* sort second half */
Eric Andersencb57d552001-06-28 07:25:16 +00005397 lpp = &list;
5398 for (;;) {
5399 if (strcmp(p->text, q->text) < 0) {
5400 *lpp = p;
5401 lpp = &p->next;
5402 if ((p = *lpp) == NULL) {
5403 *lpp = q;
5404 break;
5405 }
5406 } else {
5407 *lpp = q;
5408 lpp = &q->next;
5409 if ((q = *lpp) == NULL) {
5410 *lpp = p;
5411 break;
5412 }
5413 }
5414 }
5415 return list;
5416}
5417#endif
5418
5419
5420
5421/*
5422 * Returns true if the pattern matches the string.
5423 */
5424
Eric Andersen62483552001-07-10 06:09:16 +00005425#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00005426/* squoted: string might have quote chars */
Eric Andersencb57d552001-06-28 07:25:16 +00005427static int
Eric Andersen2870d962001-07-02 17:27:21 +00005428patmatch(char *pattern, char *string, int squoted)
5429{
Eric Andersencb57d552001-06-28 07:25:16 +00005430 const char *p;
5431 char *q;
5432
5433 p = preglob(pattern);
5434 q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string;
5435
5436 return !fnmatch(p, q, 0);
5437}
5438
5439
5440static int
Eric Andersen2870d962001-07-02 17:27:21 +00005441patmatch2(char *pattern, char *string, int squoted)
5442{
Eric Andersencb57d552001-06-28 07:25:16 +00005443 char *p;
5444 int res;
5445
5446 sstrnleft--;
5447 p = grabstackstr(expdest);
5448 res = patmatch(pattern, string, squoted);
5449 ungrabstackstr(p, expdest);
5450 return res;
5451}
5452#else
5453static int
Eric Andersen2870d962001-07-02 17:27:21 +00005454patmatch(char *pattern, char *string, int squoted) {
5455 return pmatch(pattern, string, squoted);
Eric Andersencb57d552001-06-28 07:25:16 +00005456}
5457
5458
5459static int
Eric Andersen2870d962001-07-02 17:27:21 +00005460pmatch(char *pattern, char *string, int squoted)
5461{
Eric Andersencb57d552001-06-28 07:25:16 +00005462 char *p, *q;
5463 char c;
5464
5465 p = pattern;
5466 q = string;
5467 for (;;) {
5468 switch (c = *p++) {
5469 case '\0':
5470 goto breakloop;
5471 case CTLESC:
5472 if (squoted && *q == CTLESC)
5473 q++;
5474 if (*q++ != *p++)
5475 return 0;
5476 break;
5477 case CTLQUOTEMARK:
5478 continue;
5479 case '?':
5480 if (squoted && *q == CTLESC)
5481 q++;
5482 if (*q++ == '\0')
5483 return 0;
5484 break;
5485 case '*':
5486 c = *p;
5487 while (c == CTLQUOTEMARK || c == '*')
5488 c = *++p;
5489 if (c != CTLESC && c != CTLQUOTEMARK &&
5490 c != '?' && c != '*' && c != '[') {
5491 while (*q != c) {
5492 if (squoted && *q == CTLESC &&
5493 q[1] == c)
5494 break;
5495 if (*q == '\0')
5496 return 0;
5497 if (squoted && *q == CTLESC)
5498 q++;
5499 q++;
5500 }
5501 }
5502 do {
5503 if (pmatch(p, q, squoted))
5504 return 1;
5505 if (squoted && *q == CTLESC)
5506 q++;
5507 } while (*q++ != '\0');
5508 return 0;
5509 case '[': {
5510 char *endp;
5511 int invert, found;
5512 char chr;
5513
5514 endp = p;
5515 if (*endp == '!')
5516 endp++;
5517 for (;;) {
5518 while (*endp == CTLQUOTEMARK)
5519 endp++;
5520 if (*endp == '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00005521 goto dft; /* no matching ] */
Eric Andersencb57d552001-06-28 07:25:16 +00005522 if (*endp == CTLESC)
5523 endp++;
5524 if (*++endp == ']')
5525 break;
5526 }
5527 invert = 0;
5528 if (*p == '!') {
5529 invert++;
5530 p++;
5531 }
5532 found = 0;
5533 chr = *q++;
5534 if (squoted && chr == CTLESC)
5535 chr = *q++;
5536 if (chr == '\0')
5537 return 0;
5538 c = *p++;
5539 do {
5540 if (c == CTLQUOTEMARK)
5541 continue;
5542 if (c == CTLESC)
5543 c = *p++;
5544 if (*p == '-' && p[1] != ']') {
5545 p++;
5546 while (*p == CTLQUOTEMARK)
5547 p++;
5548 if (*p == CTLESC)
5549 p++;
5550 if (chr >= c && chr <= *p)
5551 found = 1;
5552 p++;
5553 } else {
5554 if (chr == c)
5555 found = 1;
5556 }
5557 } while ((c = *p++) != ']');
5558 if (found == invert)
5559 return 0;
5560 break;
5561 }
Eric Andersen2870d962001-07-02 17:27:21 +00005562dft: default:
Eric Andersencb57d552001-06-28 07:25:16 +00005563 if (squoted && *q == CTLESC)
5564 q++;
5565 if (*q++ != c)
5566 return 0;
5567 break;
5568 }
5569 }
5570breakloop:
5571 if (*q != '\0')
5572 return 0;
5573 return 1;
5574}
5575#endif
5576
5577
5578
5579/*
5580 * Remove any CTLESC characters from a string.
5581 */
5582
Eric Andersen62483552001-07-10 06:09:16 +00005583#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00005584static char *
Eric Andersen2870d962001-07-02 17:27:21 +00005585_rmescapes(char *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005586{
5587 char *p, *q, *r;
5588 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5589
5590 p = strpbrk(str, qchars);
5591 if (!p) {
5592 return str;
5593 }
5594 q = p;
5595 r = str;
5596 if (flag & RMESCAPE_ALLOC) {
5597 size_t len = p - str;
5598 q = r = stalloc(strlen(p) + len + 1);
5599 if (len > 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00005600 memcpy(q, str, len);
5601 q += len;
Eric Andersencb57d552001-06-28 07:25:16 +00005602 }
5603 }
5604 while (*p) {
5605 if (*p == CTLQUOTEMARK) {
5606 p++;
5607 continue;
5608 }
5609 if (*p == CTLESC) {
5610 p++;
5611 if (flag & RMESCAPE_GLOB && *p != '/') {
5612 *q++ = '\\';
5613 }
5614 }
5615 *q++ = *p++;
5616 }
5617 *q = '\0';
5618 return r;
5619}
5620#else
5621static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005622rmescapes(char *str)
Eric Andersencb57d552001-06-28 07:25:16 +00005623{
5624 char *p, *q;
5625
5626 p = str;
5627 while (*p != CTLESC && *p != CTLQUOTEMARK) {
5628 if (*p++ == '\0')
5629 return;
5630 }
5631 q = p;
5632 while (*p) {
5633 if (*p == CTLQUOTEMARK) {
5634 p++;
5635 continue;
5636 }
5637 if (*p == CTLESC)
5638 p++;
5639 *q++ = *p++;
5640 }
5641 *q = '\0';
5642}
5643#endif
5644
5645
5646
5647/*
5648 * See if a pattern matches in a case statement.
5649 */
5650
5651static int
Eric Andersen2870d962001-07-02 17:27:21 +00005652casematch(union node *pattern, const char *val)
5653{
Eric Andersencb57d552001-06-28 07:25:16 +00005654 struct stackmark smark;
5655 int result;
5656 char *p;
5657
5658 setstackmark(&smark);
5659 argbackq = pattern->narg.backquote;
5660 STARTSTACKSTR(expdest);
5661 ifslastp = NULL;
5662 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5663 STPUTC('\0', expdest);
5664 p = grabstackstr(expdest);
Eric Andersen2870d962001-07-02 17:27:21 +00005665 result = patmatch(p, (char *)val, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00005666 popstackmark(&smark);
5667 return result;
5668}
5669
5670/*
5671 * Our own itoa().
5672 */
5673
5674static char *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005675cvtnum(int num, char *buf)
5676{
Eric Andersencb57d552001-06-28 07:25:16 +00005677 int len;
5678
5679 CHECKSTRSPACE(32, buf);
5680 len = sprintf(buf, "%d", num);
5681 STADJUST(len, buf);
5682 return buf;
5683}
Eric Andersencb57d552001-06-28 07:25:16 +00005684/*
5685 * Editline and history functions (and glue).
5686 */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005687static int histcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005688{
5689 error("not compiled with history support");
5690 /* NOTREACHED */
5691}
5692
5693
Eric Andersencb57d552001-06-28 07:25:16 +00005694struct redirtab {
5695 struct redirtab *next;
Russ Dill4db35642001-07-26 05:58:40 +00005696 short renamed[10]; /* Current ash support only 0-9 descriptors */
Eric Andersen34506362001-08-02 05:02:46 +00005697 /* char on arm (and others) can't be negative */
Eric Andersencb57d552001-06-28 07:25:16 +00005698};
5699
Eric Andersen2870d962001-07-02 17:27:21 +00005700static struct redirtab *redirlist;
Eric Andersencb57d552001-06-28 07:25:16 +00005701
5702extern char **environ;
5703
5704
5705
5706/*
5707 * Initialization code.
5708 */
5709
5710static void
Eric Andersen2870d962001-07-02 17:27:21 +00005711init(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00005712
5713 /* from cd.c: */
5714 {
Eric Andersena3483db2001-10-24 08:01:06 +00005715 curdir = nullstr;
Eric Andersencb57d552001-06-28 07:25:16 +00005716 setpwd(0, 0);
5717 }
5718
5719 /* from input.c: */
5720 {
5721 basepf.nextc = basepf.buf = basebuf;
5722 }
5723
Eric Andersencb57d552001-06-28 07:25:16 +00005724 /* from var.c: */
5725 {
5726 char **envp;
5727 char ppid[32];
5728
5729 initvar();
5730 for (envp = environ ; *envp ; envp++) {
5731 if (strchr(*envp, '=')) {
5732 setvareq(*envp, VEXPORT|VTEXTFIXED);
5733 }
5734 }
5735
Eric Andersen3102ac42001-07-06 04:26:23 +00005736 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
Eric Andersencb57d552001-06-28 07:25:16 +00005737 setvar("PPID", ppid, 0);
5738 }
5739}
5740
5741
5742
5743/*
5744 * This routine is called when an error or an interrupt occurs in an
5745 * interactive shell and control is returned to the main command loop.
5746 */
5747
Eric Andersen2870d962001-07-02 17:27:21 +00005748/* 1 == check for aliases, 2 == also check for assignments */
Eric Andersen7467c8d2001-07-12 20:26:32 +00005749static int checkalias; /* also used in no alias mode for check assignments */
Eric Andersen2870d962001-07-02 17:27:21 +00005750
Eric Andersencb57d552001-06-28 07:25:16 +00005751static void
Eric Andersen2870d962001-07-02 17:27:21 +00005752reset(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00005753
5754 /* from eval.c: */
5755 {
5756 evalskip = 0;
5757 loopnest = 0;
5758 funcnest = 0;
5759 }
5760
5761 /* from input.c: */
5762 {
5763 if (exception != EXSHELLPROC)
Eric Andersen2870d962001-07-02 17:27:21 +00005764 parselleft = parsenleft = 0; /* clear input buffer */
Eric Andersencb57d552001-06-28 07:25:16 +00005765 popallfiles();
5766 }
5767
5768 /* from parser.c: */
5769 {
5770 tokpushback = 0;
5771 checkkwd = 0;
5772 checkalias = 0;
5773 }
5774
5775 /* from redir.c: */
5776 {
5777 while (redirlist)
5778 popredir();
5779 }
5780
Eric Andersencb57d552001-06-28 07:25:16 +00005781}
5782
5783
5784
5785/*
Eric Andersencb57d552001-06-28 07:25:16 +00005786 * This file implements the input routines used by the parser.
5787 */
5788
Eric Andersenbdfd0d72001-10-24 05:00:29 +00005789#ifdef CONFIG_FEATURE_COMMAND_EDITING
Eric Andersencb57d552001-06-28 07:25:16 +00005790static const char * cmdedit_prompt;
5791static inline void putprompt(const char *s) {
5792 cmdedit_prompt = s;
5793}
5794#else
5795static inline void putprompt(const char *s) {
5796 out2str(s);
5797}
5798#endif
5799
Eric Andersen2870d962001-07-02 17:27:21 +00005800#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00005801
Eric Andersencb57d552001-06-28 07:25:16 +00005802
Eric Andersencb57d552001-06-28 07:25:16 +00005803
Eric Andersen2870d962001-07-02 17:27:21 +00005804/*
5805 * Same as pgetc(), but ignores PEOA.
5806 */
Eric Andersencb57d552001-06-28 07:25:16 +00005807
Eric Andersend35c5df2002-01-09 15:37:36 +00005808#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00005809static int
Eric Andersen74400cc2001-10-18 04:11:39 +00005810pgetc2(void)
Eric Andersen2870d962001-07-02 17:27:21 +00005811{
5812 int c;
5813 do {
5814 c = pgetc_macro();
5815 } while (c == PEOA);
5816 return c;
Eric Andersencb57d552001-06-28 07:25:16 +00005817}
Eric Andersen2870d962001-07-02 17:27:21 +00005818#else
Eric Andersend35c5df2002-01-09 15:37:36 +00005819static inline int pgetc2(void) { return pgetc_macro(); }
Eric Andersencb57d552001-06-28 07:25:16 +00005820#endif
5821
Eric Andersencb57d552001-06-28 07:25:16 +00005822/*
5823 * Read a line from the script.
5824 */
5825
Eric Andersen62483552001-07-10 06:09:16 +00005826static inline char *
Eric Andersen2870d962001-07-02 17:27:21 +00005827pfgets(char *line, int len)
Eric Andersencb57d552001-06-28 07:25:16 +00005828{
5829 char *p = line;
5830 int nleft = len;
5831 int c;
5832
5833 while (--nleft > 0) {
5834 c = pgetc2();
5835 if (c == PEOF) {
5836 if (p == line)
5837 return NULL;
5838 break;
5839 }
5840 *p++ = c;
5841 if (c == '\n')
5842 break;
5843 }
5844 *p = '\0';
5845 return line;
5846}
5847
Eric Andersen62483552001-07-10 06:09:16 +00005848static inline int
Eric Andersen2870d962001-07-02 17:27:21 +00005849preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00005850{
5851 int nr;
5852 char *buf = parsefile->buf;
5853 parsenextc = buf;
5854
5855retry:
Eric Andersenbdfd0d72001-10-24 05:00:29 +00005856#ifdef CONFIG_FEATURE_COMMAND_EDITING
Eric Andersencb57d552001-06-28 07:25:16 +00005857 {
Eric Andersen34506362001-08-02 05:02:46 +00005858 if (!iflag || parsefile->fd)
Eric Andersen7467c8d2001-07-12 20:26:32 +00005859 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersen2870d962001-07-02 17:27:21 +00005860 else {
Eric Andersen044228d2001-07-17 01:12:36 +00005861 nr = cmdedit_read_input((char*)cmdedit_prompt, buf);
Eric Andersencb57d552001-06-28 07:25:16 +00005862 }
5863 }
5864#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00005865 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00005866#endif
5867
5868 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00005869 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
5870 int flags = fcntl(0, F_GETFL, 0);
5871 if (flags >= 0 && flags & O_NONBLOCK) {
5872 flags &=~ O_NONBLOCK;
5873 if (fcntl(0, F_SETFL, flags) >= 0) {
5874 out2str("sh: turning off NDELAY mode\n");
5875 goto retry;
5876 }
5877 }
5878 }
5879 }
5880 return nr;
5881}
5882
Eric Andersen2870d962001-07-02 17:27:21 +00005883static void
5884popstring(void)
5885{
5886 struct strpush *sp = parsefile->strpush;
5887
5888 INTOFF;
Eric Andersend35c5df2002-01-09 15:37:36 +00005889#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00005890 if (sp->ap) {
5891 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
5892 if (!checkalias) {
5893 checkalias = 1;
5894 }
5895 }
5896 if (sp->string != sp->ap->val) {
5897 ckfree(sp->string);
5898 }
5899
5900 sp->ap->flag &= ~ALIASINUSE;
5901 if (sp->ap->flag & ALIASDEAD) {
5902 unalias(sp->ap->name);
5903 }
5904 }
5905#endif
5906 parsenextc = sp->prevstring;
5907 parsenleft = sp->prevnleft;
5908/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
5909 parsefile->strpush = sp->prev;
5910 if (sp != &(parsefile->basestrpush))
5911 ckfree(sp);
5912 INTON;
5913}
5914
5915
Eric Andersencb57d552001-06-28 07:25:16 +00005916/*
5917 * Refill the input buffer and return the next input character:
5918 *
5919 * 1) If a string was pushed back on the input, pop it;
5920 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
5921 * from a string so we can't refill the buffer, return EOF.
5922 * 3) If the is more stuff in this buffer, use it else call read to fill it.
5923 * 4) Process input up to the next newline, deleting nul characters.
5924 */
5925
5926static int
Eric Andersen2870d962001-07-02 17:27:21 +00005927preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00005928{
5929 char *p, *q;
5930 int more;
5931 char savec;
5932
5933 while (parsefile->strpush) {
Eric Andersend35c5df2002-01-09 15:37:36 +00005934#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00005935 if (parsenleft == -1 && parsefile->strpush->ap &&
5936 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00005937 return PEOA;
5938 }
Eric Andersen2870d962001-07-02 17:27:21 +00005939#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005940 popstring();
5941 if (--parsenleft >= 0)
5942 return (*parsenextc++);
5943 }
5944 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
5945 return PEOF;
Eric Andersen3102ac42001-07-06 04:26:23 +00005946 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00005947
5948again:
5949 if (parselleft <= 0) {
5950 if ((parselleft = preadfd()) <= 0) {
5951 parselleft = parsenleft = EOF_NLEFT;
5952 return PEOF;
5953 }
5954 }
5955
5956 q = p = parsenextc;
5957
5958 /* delete nul characters */
5959 for (more = 1; more;) {
5960 switch (*p) {
5961 case '\0':
Eric Andersen2870d962001-07-02 17:27:21 +00005962 p++; /* Skip nul */
Eric Andersencb57d552001-06-28 07:25:16 +00005963 goto check;
5964
5965
5966 case '\n':
5967 parsenleft = q - parsenextc;
5968 more = 0; /* Stop processing here */
5969 break;
5970 }
5971
5972 *q++ = *p++;
5973check:
5974 if (--parselleft <= 0 && more) {
5975 parsenleft = q - parsenextc - 1;
5976 if (parsenleft < 0)
5977 goto again;
5978 more = 0;
5979 }
5980 }
5981
5982 savec = *q;
5983 *q = '\0';
5984
5985 if (vflag) {
5986 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00005987 }
5988
5989 *q = savec;
5990
5991 return *parsenextc++;
5992}
5993
Eric Andersencb57d552001-06-28 07:25:16 +00005994
5995/*
5996 * Push a string back onto the input at this current parsefile level.
5997 * We handle aliases this way.
5998 */
5999static void
Eric Andersen2870d962001-07-02 17:27:21 +00006000pushstring(char *s, int len, void *ap)
6001{
Eric Andersencb57d552001-06-28 07:25:16 +00006002 struct strpush *sp;
6003
6004 INTOFF;
6005/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6006 if (parsefile->strpush) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006007 sp = xmalloc(sizeof (struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00006008 sp->prev = parsefile->strpush;
6009 parsefile->strpush = sp;
6010 } else
6011 sp = parsefile->strpush = &(parsefile->basestrpush);
6012 sp->prevstring = parsenextc;
6013 sp->prevnleft = parsenleft;
Eric Andersend35c5df2002-01-09 15:37:36 +00006014#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00006015 sp->ap = (struct alias *)ap;
6016 if (ap) {
6017 ((struct alias *)ap)->flag |= ALIASINUSE;
6018 sp->string = s;
6019 }
Eric Andersen2870d962001-07-02 17:27:21 +00006020#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006021 parsenextc = s;
6022 parsenleft = len;
6023 INTON;
6024}
6025
Eric Andersencb57d552001-06-28 07:25:16 +00006026
Eric Andersencb57d552001-06-28 07:25:16 +00006027/*
6028 * Like setinputfile, but takes input from a string.
6029 */
6030
6031static void
Eric Andersen62483552001-07-10 06:09:16 +00006032setinputstring(char *string)
6033{
Eric Andersencb57d552001-06-28 07:25:16 +00006034 INTOFF;
6035 pushfile();
6036 parsenextc = string;
6037 parsenleft = strlen(string);
6038 parsefile->buf = NULL;
6039 plinno = 1;
6040 INTON;
6041}
6042
6043
6044
6045/*
6046 * To handle the "." command, a stack of input files is used. Pushfile
6047 * adds a new entry to the stack and popfile restores the previous level.
6048 */
6049
6050static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006051pushfile(void)
6052{
Eric Andersencb57d552001-06-28 07:25:16 +00006053 struct parsefile *pf;
6054
6055 parsefile->nleft = parsenleft;
6056 parsefile->lleft = parselleft;
6057 parsefile->nextc = parsenextc;
6058 parsefile->linno = plinno;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006059 pf = (struct parsefile *)xmalloc(sizeof (struct parsefile));
Eric Andersencb57d552001-06-28 07:25:16 +00006060 pf->prev = parsefile;
6061 pf->fd = -1;
6062 pf->strpush = NULL;
6063 pf->basestrpush.prev = NULL;
6064 parsefile = pf;
6065}
6066
Eric Andersend35c5df2002-01-09 15:37:36 +00006067#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00006068static void restartjob (struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00006069#endif
Eric Andersen2870d962001-07-02 17:27:21 +00006070static void freejob (struct job *);
6071static struct job *getjob (const char *);
6072static int dowait (int, struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00006073static void waitonint(int);
6074
6075
Eric Andersen2870d962001-07-02 17:27:21 +00006076/*
6077 * We keep track of whether or not fd0 has been redirected. This is for
6078 * background commands, where we want to redirect fd0 to /dev/null only
6079 * if it hasn't already been redirected.
6080*/
6081static int fd0_redirected = 0;
6082
6083/* Return true if fd 0 has already been redirected at least once. */
6084static inline int
Eric Andersen74400cc2001-10-18 04:11:39 +00006085fd0_redirected_p (void)
6086{
Eric Andersen2870d962001-07-02 17:27:21 +00006087 return fd0_redirected != 0;
6088}
6089
Eric Andersen62483552001-07-10 06:09:16 +00006090static void dupredirect (const union node *, int, int fd1dup);
Eric Andersen2870d962001-07-02 17:27:21 +00006091
Eric Andersend35c5df2002-01-09 15:37:36 +00006092#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006093/*
6094 * Turn job control on and off.
6095 *
6096 * Note: This code assumes that the third arg to ioctl is a character
6097 * pointer, which is true on Berkeley systems but not System V. Since
6098 * System V doesn't have job control yet, this isn't a problem now.
6099 */
6100
Eric Andersen2870d962001-07-02 17:27:21 +00006101
Eric Andersencb57d552001-06-28 07:25:16 +00006102
6103static void setjobctl(int enable)
6104{
6105#ifdef OLD_TTY_DRIVER
6106 int ldisc;
6107#endif
6108
6109 if (enable == jobctl || rootshell == 0)
6110 return;
6111 if (enable) {
6112 do { /* while we are in the background */
6113#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006114 if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00006115#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006116 initialpgrp = tcgetpgrp(2);
Eric Andersencb57d552001-06-28 07:25:16 +00006117 if (initialpgrp < 0) {
6118#endif
Eric Andersen8c145dc2001-07-10 16:57:09 +00006119 out2str("sh: can't access tty; job control turned off\n");
Eric Andersencb57d552001-06-28 07:25:16 +00006120 mflag = 0;
6121 return;
6122 }
6123 if (initialpgrp == -1)
6124 initialpgrp = getpgrp();
6125 else if (initialpgrp != getpgrp()) {
6126 killpg(initialpgrp, SIGTTIN);
6127 continue;
6128 }
6129 } while (0);
6130#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006131 if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
Eric Andersen8c145dc2001-07-10 16:57:09 +00006132 out2str("sh: need new tty driver to run job control; job control turned off\n");
Eric Andersencb57d552001-06-28 07:25:16 +00006133 mflag = 0;
6134 return;
6135 }
6136#endif
6137 setsignal(SIGTSTP);
6138 setsignal(SIGTTOU);
6139 setsignal(SIGTTIN);
6140 setpgid(0, rootpid);
6141#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006142 ioctl(2, TIOCSPGRP, (char *)&rootpid);
Eric Andersencb57d552001-06-28 07:25:16 +00006143#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006144 tcsetpgrp(2, rootpid);
Eric Andersencb57d552001-06-28 07:25:16 +00006145#endif
Eric Andersen8c145dc2001-07-10 16:57:09 +00006146 } else { /* turning job control off */
Eric Andersencb57d552001-06-28 07:25:16 +00006147 setpgid(0, initialpgrp);
6148#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006149 ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006150#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006151 tcsetpgrp(2, initialpgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006152#endif
6153 setsignal(SIGTSTP);
6154 setsignal(SIGTTOU);
6155 setsignal(SIGTTIN);
6156 }
6157 jobctl = enable;
6158}
6159#endif
6160
6161
Eric Andersend35c5df2002-01-09 15:37:36 +00006162#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006163static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006164killcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006165{
6166 int signo = -1;
6167 int list = 0;
6168 int i;
6169 pid_t pid;
6170 struct job *jp;
6171
6172 if (argc <= 1) {
6173usage:
6174 error(
6175"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6176"kill -l [exitstatus]"
6177 );
6178 }
6179
6180 if (*argv[1] == '-') {
6181 signo = decode_signal(argv[1] + 1, 1);
6182 if (signo < 0) {
6183 int c;
6184
6185 while ((c = nextopt("ls:")) != '\0')
6186 switch (c) {
6187 case 'l':
6188 list = 1;
6189 break;
6190 case 's':
6191 signo = decode_signal(optionarg, 1);
6192 if (signo < 0) {
6193 error(
6194 "invalid signal number or name: %s",
6195 optionarg
6196 );
6197 }
Eric Andersen2870d962001-07-02 17:27:21 +00006198 break;
Eric Andersencb57d552001-06-28 07:25:16 +00006199#ifdef DEBUG
6200 default:
6201 error(
6202 "nextopt returned character code 0%o", c);
6203#endif
6204 }
6205 } else
6206 argptr++;
6207 }
6208
6209 if (!list && signo < 0)
6210 signo = SIGTERM;
6211
6212 if ((signo < 0 || !*argptr) ^ list) {
6213 goto usage;
6214 }
6215
6216 if (list) {
Eric Andersen34506362001-08-02 05:02:46 +00006217 const char *name;
6218
Eric Andersencb57d552001-06-28 07:25:16 +00006219 if (!*argptr) {
6220 out1str("0\n");
6221 for (i = 1; i < NSIG; i++) {
Eric Andersen34506362001-08-02 05:02:46 +00006222 name = u_signal_names(0, &i, 1);
6223 if(name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006224 puts(name);
Eric Andersencb57d552001-06-28 07:25:16 +00006225 }
6226 return 0;
6227 }
Eric Andersen34506362001-08-02 05:02:46 +00006228 name = u_signal_names(*argptr, &signo, -1);
6229 if (name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006230 puts(name);
Eric Andersencb57d552001-06-28 07:25:16 +00006231 else
6232 error("invalid signal number or exit status: %s",
6233 *argptr);
6234 return 0;
6235 }
6236
6237 do {
6238 if (**argptr == '%') {
6239 jp = getjob(*argptr);
6240 if (jp->jobctl == 0)
6241 error("job %s not created under job control",
6242 *argptr);
6243 pid = -jp->ps[0].pid;
6244 } else
6245 pid = atoi(*argptr);
6246 if (kill(pid, signo) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00006247 error("%s: %m", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00006248 } while (*++argptr);
6249
6250 return 0;
6251}
6252
6253static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006254fgcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006255{
6256 struct job *jp;
6257 int pgrp;
6258 int status;
6259
6260 jp = getjob(argv[1]);
6261 if (jp->jobctl == 0)
6262 error("job not created under job control");
6263 pgrp = jp->ps[0].pid;
6264#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006265 ioctl(2, TIOCSPGRP, (char *)&pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006266#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006267 tcsetpgrp(2, pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006268#endif
6269 restartjob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00006270 status = waitforjob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00006271 return status;
6272}
6273
6274
6275static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006276bgcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006277{
6278 struct job *jp;
6279
6280 do {
6281 jp = getjob(*++argv);
6282 if (jp->jobctl == 0)
6283 error("job not created under job control");
6284 restartjob(jp);
6285 } while (--argc > 1);
6286 return 0;
6287}
6288
6289
6290static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006291restartjob(struct job *jp)
Eric Andersencb57d552001-06-28 07:25:16 +00006292{
6293 struct procstat *ps;
6294 int i;
6295
6296 if (jp->state == JOBDONE)
6297 return;
6298 INTOFF;
6299 killpg(jp->ps[0].pid, SIGCONT);
6300 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
6301 if (WIFSTOPPED(ps->status)) {
6302 ps->status = -1;
6303 jp->state = 0;
6304 }
6305 }
6306 INTON;
6307}
6308#endif
6309
Eric Andersen2870d962001-07-02 17:27:21 +00006310static void showjobs(int change);
6311
Eric Andersencb57d552001-06-28 07:25:16 +00006312
6313static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006314jobscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006315{
6316 showjobs(0);
6317 return 0;
6318}
6319
6320
6321/*
6322 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6323 * statuses have changed since the last call to showjobs.
6324 *
6325 * If the shell is interrupted in the process of creating a job, the
6326 * result may be a job structure containing zero processes. Such structures
6327 * will be freed here.
6328 */
6329
6330static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006331showjobs(int change)
Eric Andersencb57d552001-06-28 07:25:16 +00006332{
6333 int jobno;
6334 int procno;
6335 int i;
6336 struct job *jp;
6337 struct procstat *ps;
6338 int col;
6339 char s[64];
6340
6341 TRACE(("showjobs(%d) called\n", change));
6342 while (dowait(0, (struct job *)NULL) > 0);
6343 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
6344 if (! jp->used)
6345 continue;
6346 if (jp->nprocs == 0) {
6347 freejob(jp);
6348 continue;
6349 }
6350 if (change && ! jp->changed)
6351 continue;
6352 procno = jp->nprocs;
Eric Andersen2870d962001-07-02 17:27:21 +00006353 for (ps = jp->ps ; ; ps++) { /* for each process */
Eric Andersencb57d552001-06-28 07:25:16 +00006354 if (ps == jp->ps)
Eric Andersen3102ac42001-07-06 04:26:23 +00006355 snprintf(s, 64, "[%d] %ld ", jobno,
Eric Andersencb57d552001-06-28 07:25:16 +00006356 (long)ps->pid);
6357 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006358 snprintf(s, 64, " %ld ",
Eric Andersencb57d552001-06-28 07:25:16 +00006359 (long)ps->pid);
6360 out1str(s);
6361 col = strlen(s);
6362 s[0] = '\0';
6363 if (ps->status == -1) {
6364 /* don't print anything */
6365 } else if (WIFEXITED(ps->status)) {
Eric Andersen3102ac42001-07-06 04:26:23 +00006366 snprintf(s, 64, "Exit %d",
Eric Andersencb57d552001-06-28 07:25:16 +00006367 WEXITSTATUS(ps->status));
6368 } else {
Eric Andersend35c5df2002-01-09 15:37:36 +00006369#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00006370 if (WIFSTOPPED(ps->status))
Eric Andersencb57d552001-06-28 07:25:16 +00006371 i = WSTOPSIG(ps->status);
6372 else /* WIFSIGNALED(ps->status) */
6373#endif
6374 i = WTERMSIG(ps->status);
6375 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
Eric Andersen2870d962001-07-02 17:27:21 +00006376 strcpy(s, sys_siglist[i & 0x7F]);
Eric Andersencb57d552001-06-28 07:25:16 +00006377 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006378 snprintf(s, 64, "Signal %d", i & 0x7F);
Eric Andersencb57d552001-06-28 07:25:16 +00006379 if (WCOREDUMP(ps->status))
6380 strcat(s, " (core dumped)");
6381 }
6382 out1str(s);
6383 col += strlen(s);
Eric Andersen62483552001-07-10 06:09:16 +00006384 printf(
Eric Andersencb57d552001-06-28 07:25:16 +00006385 "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
6386 ps->cmd
6387 );
6388 if (--procno <= 0)
6389 break;
6390 }
6391 jp->changed = 0;
6392 if (jp->state == JOBDONE) {
6393 freejob(jp);
6394 }
6395 }
6396}
6397
6398
6399/*
6400 * Mark a job structure as unused.
6401 */
6402
6403static void
Eric Andersen62483552001-07-10 06:09:16 +00006404freejob(struct job *jp)
6405{
6406 const struct procstat *ps;
Eric Andersencb57d552001-06-28 07:25:16 +00006407 int i;
6408
6409 INTOFF;
6410 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
6411 if (ps->cmd != nullstr)
6412 ckfree(ps->cmd);
6413 }
6414 if (jp->ps != &jp->ps0)
6415 ckfree(jp->ps);
6416 jp->used = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006417#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006418 if (curjob == jp - jobtab + 1)
6419 curjob = 0;
6420#endif
6421 INTON;
6422}
6423
6424
6425
6426static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006427waitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006428{
6429 struct job *job;
6430 int status, retval;
6431 struct job *jp;
6432
6433 if (--argc > 0) {
6434start:
6435 job = getjob(*++argv);
6436 } else {
6437 job = NULL;
6438 }
Eric Andersen2870d962001-07-02 17:27:21 +00006439 for (;;) { /* loop until process terminated or stopped */
Eric Andersencb57d552001-06-28 07:25:16 +00006440 if (job != NULL) {
6441 if (job->state) {
6442 status = job->ps[job->nprocs - 1].status;
6443 if (! iflag)
6444 freejob(job);
6445 if (--argc) {
6446 goto start;
6447 }
6448 if (WIFEXITED(status))
6449 retval = WEXITSTATUS(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006450#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006451 else if (WIFSTOPPED(status))
6452 retval = WSTOPSIG(status) + 128;
6453#endif
6454 else {
6455 /* XXX: limits number of signals */
6456 retval = WTERMSIG(status) + 128;
6457 }
6458 return retval;
6459 }
6460 } else {
6461 for (jp = jobtab ; ; jp++) {
Eric Andersen2870d962001-07-02 17:27:21 +00006462 if (jp >= jobtab + njobs) { /* no running procs */
Eric Andersencb57d552001-06-28 07:25:16 +00006463 return 0;
6464 }
6465 if (jp->used && jp->state == 0)
6466 break;
6467 }
6468 }
6469 if (dowait(2, 0) < 0 && errno == EINTR) {
6470 return 129;
6471 }
6472 }
6473}
6474
6475
6476
6477/*
6478 * Convert a job name to a job structure.
6479 */
6480
6481static struct job *
Eric Andersen2870d962001-07-02 17:27:21 +00006482getjob(const char *name)
6483{
Eric Andersencb57d552001-06-28 07:25:16 +00006484 int jobno;
6485 struct job *jp;
6486 int pid;
6487 int i;
6488
6489 if (name == NULL) {
Eric Andersend35c5df2002-01-09 15:37:36 +00006490#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006491currentjob:
6492 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
6493 error("No current job");
6494 return &jobtab[jobno - 1];
6495#else
6496 error("No current job");
6497#endif
6498 } else if (name[0] == '%') {
6499 if (is_digit(name[1])) {
6500 jobno = number(name + 1);
6501 if (jobno > 0 && jobno <= njobs
6502 && jobtab[jobno - 1].used != 0)
6503 return &jobtab[jobno - 1];
Eric Andersend35c5df2002-01-09 15:37:36 +00006504#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006505 } else if (name[1] == '%' && name[2] == '\0') {
6506 goto currentjob;
6507#endif
6508 } else {
6509 struct job *found = NULL;
6510 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6511 if (jp->used && jp->nprocs > 0
6512 && prefix(name + 1, jp->ps[0].cmd)) {
6513 if (found)
6514 error("%s: ambiguous", name);
6515 found = jp;
6516 }
6517 }
6518 if (found)
6519 return found;
6520 }
Eric Andersen2870d962001-07-02 17:27:21 +00006521 } else if (is_number(name, &pid)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006522 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
6523 if (jp->used && jp->nprocs > 0
6524 && jp->ps[jp->nprocs - 1].pid == pid)
6525 return jp;
6526 }
6527 }
6528 error("No such job: %s", name);
6529 /* NOTREACHED */
6530}
6531
6532
6533
6534/*
6535 * Return a new job structure,
6536 */
6537
Eric Andersen2870d962001-07-02 17:27:21 +00006538static struct job *
Eric Andersen62483552001-07-10 06:09:16 +00006539makejob(const union node *node, int nprocs)
Eric Andersencb57d552001-06-28 07:25:16 +00006540{
6541 int i;
6542 struct job *jp;
6543
6544 for (i = njobs, jp = jobtab ; ; jp++) {
6545 if (--i < 0) {
6546 INTOFF;
6547 if (njobs == 0) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006548 jobtab = xmalloc(4 * sizeof jobtab[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00006549 } else {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006550 jp = xmalloc((njobs + 4) * sizeof jobtab[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00006551 memcpy(jp, jobtab, njobs * sizeof jp[0]);
6552 /* Relocate `ps' pointers */
6553 for (i = 0; i < njobs; i++)
6554 if (jp[i].ps == &jobtab[i].ps0)
6555 jp[i].ps = &jp[i].ps0;
6556 ckfree(jobtab);
6557 jobtab = jp;
6558 }
6559 jp = jobtab + njobs;
6560 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
6561 INTON;
6562 break;
6563 }
6564 if (jp->used == 0)
6565 break;
6566 }
6567 INTOFF;
6568 jp->state = 0;
6569 jp->used = 1;
6570 jp->changed = 0;
6571 jp->nprocs = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006572#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006573 jp->jobctl = jobctl;
6574#endif
6575 if (nprocs > 1) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006576 jp->ps = xmalloc(nprocs * sizeof (struct procstat));
Eric Andersencb57d552001-06-28 07:25:16 +00006577 } else {
6578 jp->ps = &jp->ps0;
6579 }
6580 INTON;
6581 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
6582 jp - jobtab + 1));
6583 return jp;
6584}
6585
6586
6587/*
6588 * Fork of a subshell. If we are doing job control, give the subshell its
6589 * own process group. Jp is a job structure that the job is to be added to.
6590 * N is the command that will be evaluated by the child. Both jp and n may
6591 * be NULL. The mode parameter can be one of the following:
Eric Andersen2870d962001-07-02 17:27:21 +00006592 * FORK_FG - Fork off a foreground process.
6593 * FORK_BG - Fork off a background process.
6594 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
6595 * process group even if job control is on.
Eric Andersencb57d552001-06-28 07:25:16 +00006596 *
6597 * When job control is turned off, background processes have their standard
6598 * input redirected to /dev/null (except for the second and later processes
6599 * in a pipeline).
6600 */
6601
Eric Andersen2870d962001-07-02 17:27:21 +00006602
6603
Eric Andersencb57d552001-06-28 07:25:16 +00006604static int
Eric Andersen62483552001-07-10 06:09:16 +00006605forkshell(struct job *jp, const union node *n, int mode)
Eric Andersencb57d552001-06-28 07:25:16 +00006606{
6607 int pid;
Eric Andersend35c5df2002-01-09 15:37:36 +00006608#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006609 int pgrp;
Eric Andersen62483552001-07-10 06:09:16 +00006610#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006611 const char *devnull = _PATH_DEVNULL;
6612 const char *nullerr = "Can't open %s";
6613
6614 TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
6615 mode));
6616 INTOFF;
Eric Andersen72f9a422001-10-28 05:12:20 +00006617#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
Eric Andersencb57d552001-06-28 07:25:16 +00006618 pid = fork();
Eric Andersen72f9a422001-10-28 05:12:20 +00006619#else
6620 pid = vfork();
6621#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006622 if (pid == -1) {
6623 TRACE(("Fork failed, errno=%d\n", errno));
6624 INTON;
6625 error("Cannot fork");
6626 }
6627 if (pid == 0) {
6628 struct job *p;
6629 int wasroot;
6630 int i;
6631
6632 TRACE(("Child shell %d\n", getpid()));
6633 wasroot = rootshell;
6634 rootshell = 0;
6635 closescript();
6636 INTON;
6637 clear_traps();
Eric Andersend35c5df2002-01-09 15:37:36 +00006638#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00006639 jobctl = 0; /* do job control only in root shell */
Eric Andersencb57d552001-06-28 07:25:16 +00006640 if (wasroot && mode != FORK_NOJOB && mflag) {
6641 if (jp == NULL || jp->nprocs == 0)
6642 pgrp = getpid();
6643 else
6644 pgrp = jp->ps[0].pid;
6645 setpgid(0, pgrp);
6646 if (mode == FORK_FG) {
6647 /*** this causes superfluous TIOCSPGRPS ***/
6648#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006649 if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006650 error("TIOCSPGRP failed, errno=%d", errno);
6651#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006652 if (tcsetpgrp(2, pgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006653 error("tcsetpgrp failed, errno=%d", errno);
6654#endif
6655 }
6656 setsignal(SIGTSTP);
6657 setsignal(SIGTTOU);
6658 } else if (mode == FORK_BG) {
Eric Andersencb57d552001-06-28 07:25:16 +00006659#else
6660 if (mode == FORK_BG) {
Aaron Lehmann1a698662001-12-31 06:12:48 +00006661#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006662 ignoresig(SIGINT);
6663 ignoresig(SIGQUIT);
6664 if ((jp == NULL || jp->nprocs == 0) &&
6665 ! fd0_redirected_p ()) {
6666 close(0);
6667 if (open(devnull, O_RDONLY) != 0)
6668 error(nullerr, devnull);
6669 }
6670 }
Eric Andersencb57d552001-06-28 07:25:16 +00006671 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
6672 if (p->used)
6673 freejob(p);
6674 if (wasroot && iflag) {
6675 setsignal(SIGINT);
6676 setsignal(SIGQUIT);
6677 setsignal(SIGTERM);
6678 }
6679 return pid;
6680 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006681#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006682 if (rootshell && mode != FORK_NOJOB && mflag) {
6683 if (jp == NULL || jp->nprocs == 0)
6684 pgrp = pid;
6685 else
6686 pgrp = jp->ps[0].pid;
6687 setpgid(pid, pgrp);
6688 }
Eric Andersen62483552001-07-10 06:09:16 +00006689#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006690 if (mode == FORK_BG)
Eric Andersen2870d962001-07-02 17:27:21 +00006691 backgndpid = pid; /* set $! */
Eric Andersencb57d552001-06-28 07:25:16 +00006692 if (jp) {
6693 struct procstat *ps = &jp->ps[jp->nprocs++];
6694 ps->pid = pid;
6695 ps->status = -1;
6696 ps->cmd = nullstr;
6697 if (iflag && rootshell && n)
6698 ps->cmd = commandtext(n);
6699 }
6700 INTON;
6701 TRACE(("In parent shell: child = %d\n", pid));
6702 return pid;
6703}
6704
6705
6706
6707/*
6708 * Wait for job to finish.
6709 *
6710 * Under job control we have the problem that while a child process is
6711 * running interrupts generated by the user are sent to the child but not
6712 * to the shell. This means that an infinite loop started by an inter-
6713 * active user may be hard to kill. With job control turned off, an
6714 * interactive user may place an interactive program inside a loop. If
6715 * the interactive program catches interrupts, the user doesn't want
6716 * these interrupts to also abort the loop. The approach we take here
6717 * is to have the shell ignore interrupt signals while waiting for a
6718 * forground process to terminate, and then send itself an interrupt
6719 * signal if the child process was terminated by an interrupt signal.
6720 * Unfortunately, some programs want to do a bit of cleanup and then
6721 * exit on interrupt; unless these processes terminate themselves by
6722 * sending a signal to themselves (instead of calling exit) they will
6723 * confuse this approach.
6724 */
6725
6726static int
Eric Andersen62483552001-07-10 06:09:16 +00006727waitforjob(struct job *jp)
6728{
Eric Andersend35c5df2002-01-09 15:37:36 +00006729#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006730 int mypgrp = getpgrp();
6731#endif
6732 int status;
6733 int st;
6734 struct sigaction act, oact;
6735
6736 INTOFF;
6737 intreceived = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006738#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006739 if (!jobctl) {
6740#else
6741 if (!iflag) {
6742#endif
6743 sigaction(SIGINT, 0, &act);
6744 act.sa_handler = waitonint;
6745 sigaction(SIGINT, &act, &oact);
6746 }
6747 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
6748 while (jp->state == 0) {
6749 dowait(1, jp);
6750 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006751#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006752 if (!jobctl) {
6753#else
6754 if (!iflag) {
6755#endif
6756 sigaction(SIGINT, &oact, 0);
6757 if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT);
6758 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006759#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006760 if (jp->jobctl) {
6761#ifdef OLD_TTY_DRIVER
Eric Andersen3102ac42001-07-06 04:26:23 +00006762 if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006763 error("TIOCSPGRP failed, errno=%d\n", errno);
6764#else
Eric Andersen3102ac42001-07-06 04:26:23 +00006765 if (tcsetpgrp(2, mypgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006766 error("tcsetpgrp failed, errno=%d\n", errno);
6767#endif
6768 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006769 if (jp->state == CONFIG_ASH_JOB_CONTROLTOPPED)
Eric Andersencb57d552001-06-28 07:25:16 +00006770 curjob = jp - jobtab + 1;
6771#endif
6772 status = jp->ps[jp->nprocs - 1].status;
6773 /* convert to 8 bits */
6774 if (WIFEXITED(status))
6775 st = WEXITSTATUS(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006776#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006777 else if (WIFSTOPPED(status))
6778 st = WSTOPSIG(status) + 128;
6779#endif
6780 else
6781 st = WTERMSIG(status) + 128;
Eric Andersend35c5df2002-01-09 15:37:36 +00006782#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006783 if (jp->jobctl) {
6784 /*
6785 * This is truly gross.
6786 * If we're doing job control, then we did a TIOCSPGRP which
6787 * caused us (the shell) to no longer be in the controlling
6788 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
6789 * intuit from the subprocess exit status whether a SIGINT
6790 * occured, and if so interrupt ourselves. Yuck. - mycroft
6791 */
6792 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
6793 raise(SIGINT);
6794 }
Eric Andersen2870d962001-07-02 17:27:21 +00006795 if (jp->state == JOBDONE)
6796
Eric Andersencb57d552001-06-28 07:25:16 +00006797#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006798 freejob(jp);
6799 INTON;
6800 return st;
6801}
6802
6803
6804
6805/*
6806 * Wait for a process to terminate.
6807 */
6808
Eric Andersen62483552001-07-10 06:09:16 +00006809/*
6810 * Do a wait system call. If job control is compiled in, we accept
6811 * stopped processes. If block is zero, we return a value of zero
6812 * rather than blocking.
6813 *
6814 * System V doesn't have a non-blocking wait system call. It does
6815 * have a SIGCLD signal that is sent to a process when one of it's
6816 * children dies. The obvious way to use SIGCLD would be to install
6817 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
6818 * was received, and have waitproc bump another counter when it got
6819 * the status of a process. Waitproc would then know that a wait
6820 * system call would not block if the two counters were different.
6821 * This approach doesn't work because if a process has children that
6822 * have not been waited for, System V will send it a SIGCLD when it
6823 * installs a signal handler for SIGCLD. What this means is that when
6824 * a child exits, the shell will be sent SIGCLD signals continuously
6825 * until is runs out of stack space, unless it does a wait call before
6826 * restoring the signal handler. The code below takes advantage of
6827 * this (mis)feature by installing a signal handler for SIGCLD and
6828 * then checking to see whether it was called. If there are any
6829 * children to be waited for, it will be.
6830 *
6831 */
6832
6833static inline int
6834waitproc(int block, int *status)
6835{
6836 int flags;
6837
6838 flags = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006839#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen62483552001-07-10 06:09:16 +00006840 if (jobctl)
6841 flags |= WUNTRACED;
6842#endif
6843 if (block == 0)
6844 flags |= WNOHANG;
6845 return wait3(status, flags, (struct rusage *)NULL);
6846}
6847
Eric Andersencb57d552001-06-28 07:25:16 +00006848static int
Eric Andersen62483552001-07-10 06:09:16 +00006849dowait(int block, struct job *job)
Eric Andersencb57d552001-06-28 07:25:16 +00006850{
6851 int pid;
6852 int status;
6853 struct procstat *sp;
6854 struct job *jp;
6855 struct job *thisjob;
6856 int done;
6857 int stopped;
6858 int core;
6859 int sig;
6860
6861 TRACE(("dowait(%d) called\n", block));
6862 do {
6863 pid = waitproc(block, &status);
6864 TRACE(("wait returns %d, status=%d\n", pid, status));
6865 } while (!(block & 2) && pid == -1 && errno == EINTR);
6866 if (pid <= 0)
6867 return pid;
6868 INTOFF;
6869 thisjob = NULL;
6870 for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
6871 if (jp->used) {
6872 done = 1;
6873 stopped = 1;
6874 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
6875 if (sp->pid == -1)
6876 continue;
6877 if (sp->pid == pid) {
6878 TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
6879 sp->status = status;
6880 thisjob = jp;
6881 }
6882 if (sp->status == -1)
6883 stopped = 0;
6884 else if (WIFSTOPPED(sp->status))
6885 done = 0;
6886 }
Eric Andersen2870d962001-07-02 17:27:21 +00006887 if (stopped) { /* stopped or done */
Eric Andersend35c5df2002-01-09 15:37:36 +00006888 int state = done? JOBDONE : CONFIG_ASH_JOB_CONTROLTOPPED;
Eric Andersencb57d552001-06-28 07:25:16 +00006889 if (jp->state != state) {
6890 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
6891 jp->state = state;
Eric Andersend35c5df2002-01-09 15:37:36 +00006892#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006893 if (done && curjob == jp - jobtab + 1)
Eric Andersen2870d962001-07-02 17:27:21 +00006894 curjob = 0; /* no current job */
Eric Andersencb57d552001-06-28 07:25:16 +00006895#endif
6896 }
6897 }
6898 }
6899 }
6900 INTON;
6901 if (! rootshell || ! iflag || (job && thisjob == job)) {
6902 core = WCOREDUMP(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006903#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006904 if (WIFSTOPPED(status)) sig = WSTOPSIG(status);
6905 else
6906#endif
6907 if (WIFEXITED(status)) sig = 0;
6908 else sig = WTERMSIG(status);
6909
6910 if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
6911 if (thisjob != job)
Eric Andersen3102ac42001-07-06 04:26:23 +00006912 out2fmt("%d: ", pid);
Eric Andersend35c5df2002-01-09 15:37:36 +00006913#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006914 if (sig == SIGTSTP && rootshell && iflag)
Eric Andersen3102ac42001-07-06 04:26:23 +00006915 out2fmt("%%%ld ",
Eric Andersencb57d552001-06-28 07:25:16 +00006916 (long)(job - jobtab + 1));
6917#endif
6918 if (sig < NSIG && sys_siglist[sig])
6919 out2str(sys_siglist[sig]);
6920 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006921 out2fmt("Signal %d", sig);
Eric Andersencb57d552001-06-28 07:25:16 +00006922 if (core)
6923 out2str(" - core dumped");
6924 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00006925 } else {
Eric Andersen2870d962001-07-02 17:27:21 +00006926 TRACE(("Not printing status: status=%d, sig=%d\n",
Eric Andersencb57d552001-06-28 07:25:16 +00006927 status, sig));
6928 }
6929 } else {
6930 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
6931 if (thisjob)
6932 thisjob->changed = 1;
6933 }
6934 return pid;
6935}
6936
6937
6938
Eric Andersencb57d552001-06-28 07:25:16 +00006939
6940/*
6941 * return 1 if there are stopped jobs, otherwise 0
6942 */
Eric Andersencb57d552001-06-28 07:25:16 +00006943static int
Eric Andersen2870d962001-07-02 17:27:21 +00006944stoppedjobs(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006945{
6946 int jobno;
6947 struct job *jp;
6948
6949 if (job_warning)
6950 return (0);
6951 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
6952 if (jp->used == 0)
6953 continue;
Eric Andersend35c5df2002-01-09 15:37:36 +00006954 if (jp->state == CONFIG_ASH_JOB_CONTROLTOPPED) {
Eric Andersencb57d552001-06-28 07:25:16 +00006955 out2str("You have stopped jobs.\n");
6956 job_warning = 2;
6957 return (1);
6958 }
6959 }
6960
6961 return (0);
6962}
6963
6964/*
6965 * Return a string identifying a command (to be printed by the
6966 * jobs command.
6967 */
6968
6969static char *cmdnextc;
6970static int cmdnleft;
Eric Andersen2870d962001-07-02 17:27:21 +00006971#define MAXCMDTEXT 200
Eric Andersencb57d552001-06-28 07:25:16 +00006972
Eric Andersen2870d962001-07-02 17:27:21 +00006973static void
6974cmdputs(const char *s)
6975{
6976 const char *p;
6977 char *q;
6978 char c;
6979 int subtype = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00006980
Eric Andersen2870d962001-07-02 17:27:21 +00006981 if (cmdnleft <= 0)
6982 return;
6983 p = s;
6984 q = cmdnextc;
6985 while ((c = *p++) != '\0') {
6986 if (c == CTLESC)
6987 *q++ = *p++;
6988 else if (c == CTLVAR) {
6989 *q++ = '$';
6990 if (--cmdnleft > 0)
6991 *q++ = '{';
6992 subtype = *p++;
6993 } else if (c == '=' && subtype != 0) {
6994 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
6995 subtype = 0;
6996 } else if (c == CTLENDVAR) {
6997 *q++ = '}';
6998 } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
6999 cmdnleft++; /* ignore it */
7000 else
7001 *q++ = c;
7002 if (--cmdnleft <= 0) {
7003 *q++ = '.';
7004 *q++ = '.';
7005 *q++ = '.';
7006 break;
7007 }
7008 }
7009 cmdnextc = q;
Eric Andersencb57d552001-06-28 07:25:16 +00007010}
7011
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00007012#define CMDTXT_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007013#ifdef CMDTXT_TABLE
7014/*
7015 * To collect a lot of redundant code in cmdtxt() case statements, we
7016 * implement a mini language here. Each type of node struct has an
7017 * associated instruction sequence that operates on its members via
7018 * their offsets. The instruction are pack in unsigned chars with
7019 * format IIDDDDDE where the bits are
7020 * I : part of the instruction opcode, which are
7021 * 00 : member is a pointer to another node -- process it recursively
7022 * 40 : member is a pointer to a char string -- output it
7023 * 80 : output the string whose index is stored in the data field
7024 * CC : flag signaling that this case needs external processing
7025 * D : data - either the (shifted) index of a fixed string to output or
7026 * the actual offset of the member to operate on in the struct
7027 * (since we assume bit 0 is set, the offset is not shifted)
7028 * E : flag signaling end of instruction sequence
7029 *
7030 * WARNING: In order to handle larger offsets for 64bit archs, this code
7031 * assumes that no offset can be an odd number and stores the
7032 * end-of-instructions flag in bit 0.
7033 */
Eric Andersencb57d552001-06-28 07:25:16 +00007034
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007035#define CMDTXT_NOMORE 0x01 /* NOTE: no offset should be odd */
7036#define CMDTXT_CHARPTR 0x40
7037#define CMDTXT_STRING 0x80
7038#define CMDTXT_SPECIAL 0xC0
7039#define CMDTXT_OFFSETMASK 0x3E
7040
7041static const char * const cmdtxt_strings[] = {
7042 /* 0 1 2 3 4 5 6 7 */
7043 "; ", "(", ")", " && ", " || ", "if ", "; then ", "...",
7044 /* 8 9 10 11 12 13 */
7045 "while ", "; do ", "; done", "until ", "for ", " in ...",
7046 /* 14 15 16 17 */
7047 "case ", "???", "() ...", "<<..."
7048};
7049
7050static const char * const redir_strings[] = {
7051 ">", "<", "<>", ">>", ">|", ">&", "<&"
7052};
7053
7054static const unsigned char cmdtxt_ops[] = {
7055#define CMDTXT_NSEMI 0
7056 offsetof(union node, nbinary.ch1),
7057 0|CMDTXT_STRING,
7058 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7059#define CMDTXT_NCMD (CMDTXT_NSEMI + 3)
7060#define CMDTXT_NPIPE (CMDTXT_NCMD)
7061#define CMDTXT_NCASE (CMDTXT_NCMD)
7062#define CMDTXT_NTO (CMDTXT_NCMD)
7063#define CMDTXT_NFROM (CMDTXT_NCMD)
7064#define CMDTXT_NFROMTO (CMDTXT_NCMD)
7065#define CMDTXT_NAPPEND (CMDTXT_NCMD)
7066#define CMDTXT_NTOOV (CMDTXT_NCMD)
7067#define CMDTXT_NTOFD (CMDTXT_NCMD)
7068#define CMDTXT_NFROMFD (CMDTXT_NCMD)
7069 CMDTXT_SPECIAL,
7070#define CMDTXT_NREDIR (CMDTXT_NPIPE + 1)
7071#define CMDTXT_NBACKGND (CMDTXT_NREDIR)
7072 offsetof(union node, nredir.n)|CMDTXT_NOMORE,
7073#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1)
7074 (1*2)|CMDTXT_STRING,
7075 offsetof(union node, nredir.n),
7076 (2*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7077#define CMDTXT_NAND (CMDTXT_NSUBSHELL + 3)
7078 offsetof(union node, nbinary.ch1),
7079 (3*2)|CMDTXT_STRING,
7080 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7081#define CMDTXT_NOR (CMDTXT_NAND + 3)
7082 offsetof(union node, nbinary.ch1),
7083 (4*2)|CMDTXT_STRING,
7084 offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
7085#define CMDTXT_NIF (CMDTXT_NOR + 3)
7086 (5*2)|CMDTXT_STRING,
7087 offsetof(union node, nif.test),
7088 (6*2)|CMDTXT_STRING,
7089 offsetof(union node, nif.ifpart),
7090 (7*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7091#define CMDTXT_NWHILE (CMDTXT_NIF + 5)
7092 (8*2)|CMDTXT_STRING,
7093 offsetof(union node, nbinary.ch1),
7094 (9*2)|CMDTXT_STRING,
7095 offsetof(union node, nbinary.ch2),
7096 (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7097#define CMDTXT_NUNTIL (CMDTXT_NWHILE + 5)
7098 (11*2)|CMDTXT_STRING,
7099 offsetof(union node, nbinary.ch1),
7100 (9*2)|CMDTXT_STRING,
7101 offsetof(union node, nbinary.ch2),
7102 (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7103#define CMDTXT_NFOR (CMDTXT_NUNTIL + 5)
7104 (12*2)|CMDTXT_STRING,
7105 offsetof(union node, nfor.var)|CMDTXT_CHARPTR,
7106 (13*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7107#define CMDTXT_NCLIST (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */
7108#define CMDTXT_NNOT (CMDTXT_NCLIST) /* TODO: IS THIS CORRECT??? */
7109 (15*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7110#define CMDTXT_NDEFUN (CMDTXT_NCLIST + 1)
7111 offsetof(union node, narg.text)|CMDTXT_CHARPTR,
7112 (16*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7113#define CMDTXT_NARG (CMDTXT_NDEFUN + 2)
7114 offsetof(union node, narg.text)|CMDTXT_CHARPTR|CMDTXT_NOMORE,
7115#define CMDTXT_NHERE (CMDTXT_NARG + 1)
7116#define CMDTXT_NXHERE (CMDTXT_NHERE)
7117 (17*2)|CMDTXT_STRING|CMDTXT_NOMORE,
7118};
7119
7120#if CMDTXT_NXHERE != 36
7121#error CMDTXT_NXHERE
7122#endif
7123
7124static const unsigned char cmdtxt_ops_index[26] = {
7125 CMDTXT_NSEMI,
7126 CMDTXT_NCMD,
7127 CMDTXT_NPIPE,
7128 CMDTXT_NREDIR,
7129 CMDTXT_NBACKGND,
7130 CMDTXT_NSUBSHELL,
7131 CMDTXT_NAND,
7132 CMDTXT_NOR,
7133 CMDTXT_NIF,
7134 CMDTXT_NWHILE,
7135 CMDTXT_NUNTIL,
7136 CMDTXT_NFOR,
7137 CMDTXT_NCASE,
7138 CMDTXT_NCLIST,
7139 CMDTXT_NDEFUN,
7140 CMDTXT_NARG,
7141 CMDTXT_NTO,
7142 CMDTXT_NFROM,
7143 CMDTXT_NFROMTO,
7144 CMDTXT_NAPPEND,
7145 CMDTXT_NTOOV,
7146 CMDTXT_NTOFD,
7147 CMDTXT_NFROMFD,
7148 CMDTXT_NHERE,
7149 CMDTXT_NXHERE,
7150 CMDTXT_NNOT,
7151};
7152
7153static void
7154cmdtxt(const union node *n)
7155{
7156 const char *p;
7157
7158 if (n == NULL)
7159 return;
7160
7161 p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type];
7162 if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */
7163 do {
7164 if (*p & CMDTXT_STRING) { /* output fixed string */
7165 cmdputs(cmdtxt_strings[((int)(*p & CMDTXT_OFFSETMASK) >> 1)]);
Manuel Novoa III c639a352001-08-12 17:32:56 +00007166 } else {
7167 const char *pf = ((const char *) n)
7168 + ((int)(*p & CMDTXT_OFFSETMASK));
7169 if (*p & CMDTXT_CHARPTR) { /* output dynamic string */
7170 cmdputs(*((const char **) pf));
7171 } else { /* output field */
7172 cmdtxt(*((const union node **) pf));
7173 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007174 }
7175 } while (!(*p++ & CMDTXT_NOMORE));
7176 } else if (n->type == NCMD) {
7177 union node *np;
7178 for (np = n->ncmd.args ; np ; np = np->narg.next) {
7179 cmdtxt(np);
7180 if (np->narg.next)
7181 cmdputs(spcstr);
7182 }
7183 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
7184 cmdputs(spcstr);
7185 cmdtxt(np);
7186 }
7187 } else if (n->type == NPIPE) {
7188 struct nodelist *lp;
7189 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
7190 cmdtxt(lp->n);
7191 if (lp->next)
7192 cmdputs(" | ");
7193 }
7194 } else if (n->type == NCASE) {
7195 cmdputs(cmdtxt_strings[14]);
7196 cmdputs(n->ncase.expr->narg.text);
7197 cmdputs(cmdtxt_strings[13]);
7198 } else {
7199#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22)
7200#error Assumption violated regarding range and ordering of NTO ... NFROMFD!
7201#endif
7202 char s[2];
7203
7204#ifdef DEBUG
7205 assert((n->type >= NTO) && (n->type <= NFROMFD));
7206#endif
7207
7208 p = redir_strings[n->type - NTO];
7209 if (n->nfile.fd != ('>' == *p)) {
7210 s[0] = n->nfile.fd + '0';
7211 s[1] = '\0';
7212 cmdputs(s);
7213 }
7214 cmdputs(p);
7215 if (n->type >= NTOFD) {
7216 s[0] = n->ndup.dupfd + '0';
7217 s[1] = '\0';
7218 cmdputs(s);
7219 } else {
7220 cmdtxt(n->nfile.fname);
7221 }
7222 }
7223}
7224#else /* CMDTXT_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00007225static void
Eric Andersen2870d962001-07-02 17:27:21 +00007226cmdtxt(const union node *n)
7227{
Eric Andersencb57d552001-06-28 07:25:16 +00007228 union node *np;
7229 struct nodelist *lp;
7230 const char *p;
7231 int i;
7232 char s[2];
7233
7234 if (n == NULL)
7235 return;
7236 switch (n->type) {
7237 case NSEMI:
7238 cmdtxt(n->nbinary.ch1);
7239 cmdputs("; ");
7240 cmdtxt(n->nbinary.ch2);
7241 break;
7242 case NAND:
7243 cmdtxt(n->nbinary.ch1);
7244 cmdputs(" && ");
7245 cmdtxt(n->nbinary.ch2);
7246 break;
7247 case NOR:
7248 cmdtxt(n->nbinary.ch1);
7249 cmdputs(" || ");
7250 cmdtxt(n->nbinary.ch2);
7251 break;
7252 case NPIPE:
7253 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
7254 cmdtxt(lp->n);
7255 if (lp->next)
7256 cmdputs(" | ");
7257 }
7258 break;
7259 case NSUBSHELL:
7260 cmdputs("(");
7261 cmdtxt(n->nredir.n);
7262 cmdputs(")");
7263 break;
7264 case NREDIR:
7265 case NBACKGND:
7266 cmdtxt(n->nredir.n);
7267 break;
7268 case NIF:
7269 cmdputs("if ");
7270 cmdtxt(n->nif.test);
7271 cmdputs("; then ");
7272 cmdtxt(n->nif.ifpart);
7273 cmdputs("...");
7274 break;
7275 case NWHILE:
7276 cmdputs("while ");
7277 goto until;
7278 case NUNTIL:
7279 cmdputs("until ");
7280until:
7281 cmdtxt(n->nbinary.ch1);
7282 cmdputs("; do ");
7283 cmdtxt(n->nbinary.ch2);
7284 cmdputs("; done");
7285 break;
7286 case NFOR:
7287 cmdputs("for ");
7288 cmdputs(n->nfor.var);
7289 cmdputs(" in ...");
7290 break;
7291 case NCASE:
7292 cmdputs("case ");
7293 cmdputs(n->ncase.expr->narg.text);
7294 cmdputs(" in ...");
7295 break;
7296 case NDEFUN:
7297 cmdputs(n->narg.text);
7298 cmdputs("() ...");
7299 break;
7300 case NCMD:
7301 for (np = n->ncmd.args ; np ; np = np->narg.next) {
7302 cmdtxt(np);
7303 if (np->narg.next)
7304 cmdputs(spcstr);
7305 }
7306 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
7307 cmdputs(spcstr);
7308 cmdtxt(np);
7309 }
7310 break;
7311 case NARG:
7312 cmdputs(n->narg.text);
7313 break;
7314 case NTO:
7315 p = ">"; i = 1; goto redir;
7316 case NAPPEND:
7317 p = ">>"; i = 1; goto redir;
7318 case NTOFD:
7319 p = ">&"; i = 1; goto redir;
7320 case NTOOV:
7321 p = ">|"; i = 1; goto redir;
7322 case NFROM:
7323 p = "<"; i = 0; goto redir;
7324 case NFROMFD:
7325 p = "<&"; i = 0; goto redir;
7326 case NFROMTO:
7327 p = "<>"; i = 0; goto redir;
7328redir:
7329 if (n->nfile.fd != i) {
7330 s[0] = n->nfile.fd + '0';
7331 s[1] = '\0';
7332 cmdputs(s);
7333 }
7334 cmdputs(p);
7335 if (n->type == NTOFD || n->type == NFROMFD) {
7336 s[0] = n->ndup.dupfd + '0';
7337 s[1] = '\0';
7338 cmdputs(s);
7339 } else {
7340 cmdtxt(n->nfile.fname);
7341 }
7342 break;
7343 case NHERE:
7344 case NXHERE:
7345 cmdputs("<<...");
7346 break;
7347 default:
7348 cmdputs("???");
7349 break;
7350 }
7351}
Manuel Novoa III c639a352001-08-12 17:32:56 +00007352#endif /* CMDTXT_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00007353
Eric Andersen2870d962001-07-02 17:27:21 +00007354static char *
7355commandtext(const union node *n)
7356{
7357 char *name;
Eric Andersencb57d552001-06-28 07:25:16 +00007358
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007359 cmdnextc = name = xmalloc(MAXCMDTEXT);
Eric Andersen2870d962001-07-02 17:27:21 +00007360 cmdnleft = MAXCMDTEXT - 4;
7361 cmdtxt(n);
7362 *cmdnextc = '\0';
7363 return name;
Eric Andersencb57d552001-06-28 07:25:16 +00007364}
7365
Eric Andersen2870d962001-07-02 17:27:21 +00007366
Eric Andersencb57d552001-06-28 07:25:16 +00007367static void waitonint(int sig) {
7368 intreceived = 1;
7369 return;
7370}
Eric Andersenec074692001-10-31 11:05:49 +00007371
Eric Andersend35c5df2002-01-09 15:37:36 +00007372#ifdef CONFIG_ASH_MAIL
Eric Andersenec074692001-10-31 11:05:49 +00007373
Eric Andersencb57d552001-06-28 07:25:16 +00007374/*
Eric Andersenec074692001-10-31 11:05:49 +00007375 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00007376 */
7377
7378
7379#define MAXMBOXES 10
7380
7381
Eric Andersen2870d962001-07-02 17:27:21 +00007382static int nmboxes; /* number of mailboxes */
7383static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
Eric Andersencb57d552001-06-28 07:25:16 +00007384
7385
7386
7387/*
7388 * Print appropriate message(s) if mail has arrived. If the argument is
7389 * nozero, then the value of MAIL has changed, so we just update the
7390 * values.
7391 */
7392
7393static void
Eric Andersen2870d962001-07-02 17:27:21 +00007394chkmail(int silent)
Eric Andersencb57d552001-06-28 07:25:16 +00007395{
7396 int i;
7397 const char *mpath;
7398 char *p;
7399 char *q;
7400 struct stackmark smark;
7401 struct stat statb;
7402
7403 if (silent)
7404 nmboxes = 10;
7405 if (nmboxes == 0)
7406 return;
7407 setstackmark(&smark);
7408 mpath = mpathset()? mpathval() : mailval();
7409 for (i = 0 ; i < nmboxes ; i++) {
7410 p = padvance(&mpath, nullstr);
7411 if (p == NULL)
7412 break;
7413 if (*p == '\0')
7414 continue;
7415 for (q = p ; *q ; q++);
7416#ifdef DEBUG
7417 if (q[-1] != '/')
7418 abort();
7419#endif
Eric Andersen2870d962001-07-02 17:27:21 +00007420 q[-1] = '\0'; /* delete trailing '/' */
Eric Andersencb57d552001-06-28 07:25:16 +00007421 if (stat(p, &statb) < 0)
7422 statb.st_size = 0;
7423 if (statb.st_size > mailtime[i] && ! silent) {
Eric Andersen3102ac42001-07-06 04:26:23 +00007424 out2fmt(snlfmt,
7425 pathopt? pathopt : "you have mail");
Eric Andersencb57d552001-06-28 07:25:16 +00007426 }
7427 mailtime[i] = statb.st_size;
Eric Andersencb57d552001-06-28 07:25:16 +00007428 }
7429 nmboxes = i;
7430 popstackmark(&smark);
7431}
Eric Andersencb57d552001-06-28 07:25:16 +00007432
Eric Andersend35c5df2002-01-09 15:37:36 +00007433#endif /* CONFIG_ASH_MAIL */
Eric Andersenec074692001-10-31 11:05:49 +00007434
Eric Andersencb57d552001-06-28 07:25:16 +00007435#define PROFILE 0
7436
Eric Andersencb57d552001-06-28 07:25:16 +00007437#if PROFILE
Eric Andersen2870d962001-07-02 17:27:21 +00007438static short profile_buf[16384];
Eric Andersencb57d552001-06-28 07:25:16 +00007439extern int etext();
7440#endif
7441
Eric Andersen2870d962001-07-02 17:27:21 +00007442static void read_profile (const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00007443static void cmdloop (int);
7444static void options (int);
Eric Andersen2870d962001-07-02 17:27:21 +00007445static void setoption (int, int);
7446static void procargs (int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00007447
Eric Andersen2870d962001-07-02 17:27:21 +00007448
Eric Andersencb57d552001-06-28 07:25:16 +00007449/*
7450 * Main routine. We initialize things, parse the arguments, execute
7451 * profiles if we're a login shell, and then call cmdloop to execute
7452 * commands. The setjmp call sets up the location to jump to when an
7453 * exception occurs. When an exception occurs the variable "state"
7454 * is used to figure out how far we had gotten.
7455 */
7456
7457int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007458ash_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007459{
7460 struct jmploc jmploc;
7461 struct stackmark smark;
7462 volatile int state;
Eric Andersen62483552001-07-10 06:09:16 +00007463 const char *shinit;
Eric Andersencb57d552001-06-28 07:25:16 +00007464
Eric Andersencb57d552001-06-28 07:25:16 +00007465 BLTINCMD = find_builtin("builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00007466 EXECCMD = find_builtin("exec");
7467 EVALCMD = find_builtin("eval");
7468
Eric Andersenbdfd0d72001-10-24 05:00:29 +00007469#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
Eric Andersen1c039232001-07-07 00:05:55 +00007470 unsetenv("PS1");
7471 unsetenv("PS2");
7472#endif
7473
Eric Andersencb57d552001-06-28 07:25:16 +00007474#if PROFILE
7475 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7476#endif
7477#if defined(linux) || defined(__GNU__)
7478 signal(SIGCHLD, SIG_DFL);
7479#endif
7480 state = 0;
7481 if (setjmp(jmploc.loc)) {
7482 INTOFF;
7483 /*
7484 * When a shell procedure is executed, we raise the
7485 * exception EXSHELLPROC to clean up before executing
7486 * the shell procedure.
7487 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007488 if (exception == EXSHELLPROC) {
Eric Andersencb57d552001-06-28 07:25:16 +00007489 rootpid = getpid();
7490 rootshell = 1;
7491 minusc = NULL;
7492 state = 3;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007493 } else {
7494 if (exception == EXEXEC) {
7495 exitstatus = exerrno;
7496 } else if (exception == EXERROR) {
7497 exitstatus = 2;
7498 }
Eric Andersencb57d552001-06-28 07:25:16 +00007499 if (state == 0 || iflag == 0 || ! rootshell)
7500 exitshell(exitstatus);
7501 }
7502 reset();
Eric Andersen2870d962001-07-02 17:27:21 +00007503 if (exception == EXINT) {
Eric Andersencb57d552001-06-28 07:25:16 +00007504 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00007505 }
7506 popstackmark(&smark);
Eric Andersen2870d962001-07-02 17:27:21 +00007507 FORCEINTON; /* enable interrupts */
Eric Andersencb57d552001-06-28 07:25:16 +00007508 if (state == 1)
7509 goto state1;
7510 else if (state == 2)
7511 goto state2;
7512 else if (state == 3)
7513 goto state3;
7514 else
7515 goto state4;
7516 }
7517 handler = &jmploc;
7518#ifdef DEBUG
7519 opentrace();
7520 trputs("Shell args: "); trargs(argv);
7521#endif
7522 rootpid = getpid();
7523 rootshell = 1;
7524 init();
7525 setstackmark(&smark);
7526 procargs(argc, argv);
7527 if (argv[0] && argv[0][0] == '-') {
7528 state = 1;
7529 read_profile("/etc/profile");
7530state1:
7531 state = 2;
7532 read_profile(".profile");
7533 }
7534state2:
7535 state = 3;
7536#ifndef linux
7537 if (getuid() == geteuid() && getgid() == getegid()) {
7538#endif
7539 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7540 state = 3;
7541 read_profile(shinit);
7542 }
7543#ifndef linux
7544 }
7545#endif
7546state3:
7547 state = 4;
7548 if (sflag == 0 || minusc) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007549 static const char sigs[] = {
Eric Andersen2870d962001-07-02 17:27:21 +00007550 SIGINT, SIGQUIT, SIGHUP,
Eric Andersencb57d552001-06-28 07:25:16 +00007551#ifdef SIGTSTP
7552 SIGTSTP,
7553#endif
7554 SIGPIPE
7555 };
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007556#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])) - 1) /* trailing nul */
Eric Andersencb57d552001-06-28 07:25:16 +00007557 int i;
7558
7559 for (i = 0; i < SIGSSIZE; i++)
7560 setsignal(sigs[i]);
7561 }
7562
7563 if (minusc)
7564 evalstring(minusc, 0);
7565
7566 if (sflag || minusc == NULL) {
Eric Andersen2870d962001-07-02 17:27:21 +00007567state4: /* XXX ??? - why isn't this before the "if" statement */
Eric Andersencb57d552001-06-28 07:25:16 +00007568 cmdloop(1);
7569 }
7570#if PROFILE
7571 monitor(0);
7572#endif
7573 exitshell(exitstatus);
7574 /* NOTREACHED */
7575}
7576
7577
7578/*
7579 * Read and execute commands. "Top" is nonzero for the top level command
7580 * loop; it turns on prompting if the shell is interactive.
7581 */
7582
7583static void
Eric Andersen2870d962001-07-02 17:27:21 +00007584cmdloop(int top)
Eric Andersencb57d552001-06-28 07:25:16 +00007585{
7586 union node *n;
7587 struct stackmark smark;
7588 int inter;
7589 int numeof = 0;
7590
7591 TRACE(("cmdloop(%d) called\n", top));
7592 setstackmark(&smark);
7593 for (;;) {
7594 if (pendingsigs)
7595 dotrap();
7596 inter = 0;
7597 if (iflag && top) {
7598 inter++;
7599 showjobs(1);
Eric Andersend35c5df2002-01-09 15:37:36 +00007600#ifdef CONFIG_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00007601 chkmail(0);
Eric Andersenec074692001-10-31 11:05:49 +00007602#endif
Eric Andersen3102ac42001-07-06 04:26:23 +00007603 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00007604 }
7605 n = parsecmd(inter);
7606 /* showtree(n); DEBUG */
7607 if (n == NEOF) {
7608 if (!top || numeof >= 50)
7609 break;
7610 if (!stoppedjobs()) {
7611 if (!Iflag)
7612 break;
7613 out2str("\nUse \"exit\" to leave shell.\n");
7614 }
7615 numeof++;
7616 } else if (n != NULL && nflag == 0) {
7617 job_warning = (job_warning == 2) ? 1 : 0;
7618 numeof = 0;
7619 evaltree(n, 0);
7620 }
7621 popstackmark(&smark);
7622 setstackmark(&smark);
7623 if (evalskip == SKIPFILE) {
7624 evalskip = 0;
7625 break;
7626 }
7627 }
7628 popstackmark(&smark);
7629}
7630
7631
7632
7633/*
7634 * Read /etc/profile or .profile. Return on error.
7635 */
7636
7637static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007638read_profile(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00007639{
7640 int fd;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007641 int xflag_save;
7642 int vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007643
7644 INTOFF;
7645 if ((fd = open(name, O_RDONLY)) >= 0)
7646 setinputfd(fd, 1);
7647 INTON;
7648 if (fd < 0)
7649 return;
7650 /* -q turns off -x and -v just when executing init files */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007651 /* Note: Might do a little redundant work, but reduces code size. */
7652 xflag_save = xflag;
7653 vflag_save = vflag;
Eric Andersencb57d552001-06-28 07:25:16 +00007654 if (qflag) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007655 vflag = xflag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007656 }
7657 cmdloop(0);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007658 xflag = xflag_save;
7659 vflag = vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007660 popfile();
7661}
7662
7663
7664
7665/*
7666 * Read a file containing shell functions.
7667 */
7668
7669static void
Eric Andersen2870d962001-07-02 17:27:21 +00007670readcmdfile(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00007671{
7672 int fd;
7673
7674 INTOFF;
7675 if ((fd = open(name, O_RDONLY)) >= 0)
7676 setinputfd(fd, 1);
7677 else
7678 error("Can't open %s", name);
7679 INTON;
7680 cmdloop(0);
7681 popfile();
7682}
7683
7684
7685
7686/*
7687 * Take commands from a file. To be compatable we should do a path
7688 * search for the file, which is necessary to find sub-commands.
7689 */
7690
Eric Andersen62483552001-07-10 06:09:16 +00007691static inline char *
Eric Andersen74400cc2001-10-18 04:11:39 +00007692find_dot_file(char *mybasename)
Eric Andersencb57d552001-06-28 07:25:16 +00007693{
7694 char *fullname;
7695 const char *path = pathval();
7696 struct stat statb;
7697
7698 /* don't try this for absolute or relative paths */
7699 if (strchr(mybasename, '/'))
7700 return mybasename;
7701
7702 while ((fullname = padvance(&path, mybasename)) != NULL) {
7703 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
7704 /*
7705 * Don't bother freeing here, since it will
7706 * be freed by the caller.
7707 */
7708 return fullname;
7709 }
7710 stunalloc(fullname);
7711 }
7712
7713 /* not found in the PATH */
7714 error("%s: not found", mybasename);
7715 /* NOTREACHED */
7716}
7717
7718static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007719dotcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007720{
7721 struct strlist *sp;
Eric Andersen69a20f02001-10-31 10:40:37 +00007722 volatile struct shparam saveparam;
Eric Andersencb57d552001-06-28 07:25:16 +00007723 exitstatus = 0;
7724
7725 for (sp = cmdenviron; sp ; sp = sp->next)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007726 setvareq(xstrdup(sp->text), VSTRFIXED|VTEXTFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +00007727
Eric Andersen2870d962001-07-02 17:27:21 +00007728 if (argc >= 2) { /* That's what SVR2 does */
Eric Andersencb57d552001-06-28 07:25:16 +00007729 char *fullname;
7730 struct stackmark smark;
7731
7732 setstackmark(&smark);
7733 fullname = find_dot_file(argv[1]);
Eric Andersen69a20f02001-10-31 10:40:37 +00007734
7735 if (argc>2) {
7736 saveparam = shellparam;
7737 shellparam.malloc = 0;
7738 shellparam.nparam = argc - 2;
7739 shellparam.p = argv + 2;
7740 };
7741
Eric Andersencb57d552001-06-28 07:25:16 +00007742 setinputfile(fullname, 1);
7743 commandname = fullname;
7744 cmdloop(0);
7745 popfile();
Eric Andersen69a20f02001-10-31 10:40:37 +00007746
7747 if (argc>2) {
7748 freeparam(&shellparam);
7749 shellparam = saveparam;
7750 };
7751
Eric Andersencb57d552001-06-28 07:25:16 +00007752 popstackmark(&smark);
7753 }
7754 return exitstatus;
7755}
7756
7757
7758static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007759exitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007760{
7761 if (stoppedjobs())
7762 return 0;
7763 if (argc > 1)
7764 exitstatus = number(argv[1]);
7765 else
7766 exitstatus = oexitstatus;
7767 exitshell(exitstatus);
7768 /* NOTREACHED */
7769}
Eric Andersen62483552001-07-10 06:09:16 +00007770
Eric Andersen2870d962001-07-02 17:27:21 +00007771static pointer
7772stalloc(int nbytes)
Eric Andersencb57d552001-06-28 07:25:16 +00007773{
7774 char *p;
7775
7776 nbytes = ALIGN(nbytes);
7777 if (nbytes > stacknleft) {
7778 int blocksize;
7779 struct stack_block *sp;
7780
7781 blocksize = nbytes;
7782 if (blocksize < MINSIZE)
7783 blocksize = MINSIZE;
7784 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007785 sp = xmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
Eric Andersencb57d552001-06-28 07:25:16 +00007786 sp->prev = stackp;
7787 stacknxt = sp->space;
7788 stacknleft = blocksize;
7789 stackp = sp;
7790 INTON;
7791 }
7792 p = stacknxt;
7793 stacknxt += nbytes;
7794 stacknleft -= nbytes;
7795 return p;
7796}
7797
7798
7799static void
Eric Andersen2870d962001-07-02 17:27:21 +00007800stunalloc(pointer p)
7801{
Eric Andersencb57d552001-06-28 07:25:16 +00007802#ifdef DEBUG
Eric Andersen2870d962001-07-02 17:27:21 +00007803 if (p == NULL) { /*DEBUG */
Eric Andersencb57d552001-06-28 07:25:16 +00007804 write(2, "stunalloc\n", 10);
7805 abort();
7806 }
7807#endif
7808 if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) {
7809 p = stackp->space;
7810 }
7811 stacknleft += stacknxt - (char *)p;
7812 stacknxt = p;
7813}
7814
7815
Eric Andersencb57d552001-06-28 07:25:16 +00007816static void
Eric Andersen2870d962001-07-02 17:27:21 +00007817setstackmark(struct stackmark *mark)
7818{
Eric Andersencb57d552001-06-28 07:25:16 +00007819 mark->stackp = stackp;
7820 mark->stacknxt = stacknxt;
7821 mark->stacknleft = stacknleft;
7822 mark->marknext = markp;
7823 markp = mark;
7824}
7825
7826
7827static void
Eric Andersen2870d962001-07-02 17:27:21 +00007828popstackmark(struct stackmark *mark)
7829{
Eric Andersencb57d552001-06-28 07:25:16 +00007830 struct stack_block *sp;
7831
7832 INTOFF;
7833 markp = mark->marknext;
7834 while (stackp != mark->stackp) {
7835 sp = stackp;
7836 stackp = sp->prev;
7837 ckfree(sp);
7838 }
7839 stacknxt = mark->stacknxt;
7840 stacknleft = mark->stacknleft;
7841 INTON;
7842}
7843
7844
7845/*
7846 * When the parser reads in a string, it wants to stick the string on the
7847 * stack and only adjust the stack pointer when it knows how big the
7848 * string is. Stackblock (defined in stack.h) returns a pointer to a block
7849 * of space on top of the stack and stackblocklen returns the length of
7850 * this block. Growstackblock will grow this space by at least one byte,
7851 * possibly moving it (like realloc). Grabstackblock actually allocates the
7852 * part of the block that has been used.
7853 */
7854
7855static void
Eric Andersen2870d962001-07-02 17:27:21 +00007856growstackblock(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00007857 char *p;
7858 int newlen = ALIGN(stacknleft * 2 + 100);
7859 char *oldspace = stacknxt;
7860 int oldlen = stacknleft;
7861 struct stack_block *sp;
7862 struct stack_block *oldstackp;
7863
7864 if (stacknxt == stackp->space && stackp != &stackbase) {
7865 INTOFF;
7866 oldstackp = stackp;
7867 sp = stackp;
7868 stackp = sp->prev;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007869 sp = xrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
Eric Andersencb57d552001-06-28 07:25:16 +00007870 sp->prev = stackp;
7871 stackp = sp;
7872 stacknxt = sp->space;
7873 stacknleft = newlen;
7874 {
7875 /* Stack marks pointing to the start of the old block
Eric Andersen2870d962001-07-02 17:27:21 +00007876 * must be relocated to point to the new block
Eric Andersencb57d552001-06-28 07:25:16 +00007877 */
7878 struct stackmark *xmark;
7879 xmark = markp;
7880 while (xmark != NULL && xmark->stackp == oldstackp) {
7881 xmark->stackp = stackp;
7882 xmark->stacknxt = stacknxt;
7883 xmark->stacknleft = stacknleft;
7884 xmark = xmark->marknext;
7885 }
7886 }
7887 INTON;
7888 } else {
7889 p = stalloc(newlen);
7890 memcpy(p, oldspace, oldlen);
Eric Andersen2870d962001-07-02 17:27:21 +00007891 stacknxt = p; /* free the space */
7892 stacknleft += newlen; /* we just allocated */
Eric Andersencb57d552001-06-28 07:25:16 +00007893 }
7894}
7895
7896
7897
Eric Andersen2870d962001-07-02 17:27:21 +00007898static inline void
7899grabstackblock(int len)
Eric Andersencb57d552001-06-28 07:25:16 +00007900{
7901 len = ALIGN(len);
7902 stacknxt += len;
7903 stacknleft -= len;
7904}
7905
7906
7907
7908/*
7909 * The following routines are somewhat easier to use that the above.
7910 * The user declares a variable of type STACKSTR, which may be declared
7911 * to be a register. The macro STARTSTACKSTR initializes things. Then
7912 * the user uses the macro STPUTC to add characters to the string. In
7913 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
7914 * grown as necessary. When the user is done, she can just leave the
7915 * string there and refer to it using stackblock(). Or she can allocate
7916 * the space for it using grabstackstr(). If it is necessary to allow
7917 * someone else to use the stack temporarily and then continue to grow
7918 * the string, the user should use grabstack to allocate the space, and
7919 * then call ungrabstr(p) to return to the previous mode of operation.
7920 *
7921 * USTPUTC is like STPUTC except that it doesn't check for overflow.
7922 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
7923 * is space for at least one character.
7924 */
7925
7926
7927static char *
Eric Andersen2870d962001-07-02 17:27:21 +00007928growstackstr(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00007929 int len = stackblocksize();
7930 if (herefd >= 0 && len >= 1024) {
7931 xwrite(herefd, stackblock(), len);
7932 sstrnleft = len - 1;
7933 return stackblock();
7934 }
7935 growstackblock();
7936 sstrnleft = stackblocksize() - len - 1;
7937 return stackblock() + len;
7938}
7939
7940
7941/*
7942 * Called from CHECKSTRSPACE.
7943 */
7944
7945static char *
7946makestrspace(size_t newlen) {
7947 int len = stackblocksize() - sstrnleft;
7948 do {
7949 growstackblock();
7950 sstrnleft = stackblocksize() - len;
7951 } while (sstrnleft < newlen);
7952 return stackblock() + len;
7953}
7954
7955
7956
7957static void
Eric Andersen2870d962001-07-02 17:27:21 +00007958ungrabstackstr(char *s, char *p)
7959{
Eric Andersencb57d552001-06-28 07:25:16 +00007960 stacknleft += stacknxt - s;
7961 stacknxt = s;
7962 sstrnleft = stacknleft - (p - s);
7963}
Eric Andersencb57d552001-06-28 07:25:16 +00007964/*
7965 * Miscelaneous builtins.
7966 */
7967
7968
7969#undef rflag
7970
Eric Andersencb57d552001-06-28 07:25:16 +00007971#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersen62483552001-07-10 06:09:16 +00007972typedef long rlim_t;
Eric Andersencb57d552001-06-28 07:25:16 +00007973#endif
7974
7975
7976
7977/*
7978 * The read builtin. The -e option causes backslashes to escape the
7979 * following character.
7980 *
7981 * This uses unbuffered input, which may be avoidable in some cases.
7982 */
7983
7984static int
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007985readcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007986{
7987 char **ap;
7988 int backslash;
7989 char c;
7990 int rflag;
7991 char *prompt;
7992 const char *ifs;
7993 char *p;
7994 int startword;
7995 int status;
7996 int i;
7997
7998 rflag = 0;
7999 prompt = NULL;
8000 while ((i = nextopt("p:r")) != '\0') {
8001 if (i == 'p')
8002 prompt = optionarg;
8003 else
8004 rflag = 1;
8005 }
8006 if (prompt && isatty(0)) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008007 out2str(prompt); /* read without cmdedit */
Eric Andersencb57d552001-06-28 07:25:16 +00008008 flushall();
8009 }
8010 if (*(ap = argptr) == NULL)
8011 error("arg count");
8012 if ((ifs = bltinlookup("IFS")) == NULL)
8013 ifs = defifs;
8014 status = 0;
8015 startword = 1;
8016 backslash = 0;
8017 STARTSTACKSTR(p);
8018 for (;;) {
8019 if (read(0, &c, 1) != 1) {
8020 status = 1;
8021 break;
8022 }
8023 if (c == '\0')
8024 continue;
8025 if (backslash) {
8026 backslash = 0;
8027 if (c != '\n')
8028 STPUTC(c, p);
8029 continue;
8030 }
8031 if (!rflag && c == '\\') {
8032 backslash++;
8033 continue;
8034 }
8035 if (c == '\n')
8036 break;
8037 if (startword && *ifs == ' ' && strchr(ifs, c)) {
8038 continue;
8039 }
8040 startword = 0;
8041 if (backslash && c == '\\') {
8042 if (read(0, &c, 1) != 1) {
8043 status = 1;
8044 break;
8045 }
8046 STPUTC(c, p);
8047 } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
8048 STACKSTRNUL(p);
8049 setvar(*ap, stackblock(), 0);
8050 ap++;
8051 startword = 1;
8052 STARTSTACKSTR(p);
8053 } else {
8054 STPUTC(c, p);
8055 }
8056 }
8057 STACKSTRNUL(p);
8058 /* Remove trailing blanks */
8059 while (stackblock() <= --p && strchr(ifs, *p) != NULL)
8060 *p = '\0';
8061 setvar(*ap, stackblock(), 0);
8062 while (*++ap != NULL)
8063 setvar(*ap, nullstr, 0);
8064 return status;
8065}
8066
8067
8068
8069static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008070umaskcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008071{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008072 static const char permuser[3] = "ugo";
8073 static const char permmode[3] = "rwx";
8074 static const short int permmask[] = {
8075 S_IRUSR, S_IWUSR, S_IXUSR,
8076 S_IRGRP, S_IWGRP, S_IXGRP,
8077 S_IROTH, S_IWOTH, S_IXOTH
8078 };
8079
Eric Andersencb57d552001-06-28 07:25:16 +00008080 char *ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008081 mode_t mask;
Eric Andersencb57d552001-06-28 07:25:16 +00008082 int i;
8083 int symbolic_mode = 0;
8084
Eric Andersen62483552001-07-10 06:09:16 +00008085 while (nextopt("S") != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00008086 symbolic_mode = 1;
8087 }
8088
8089 INTOFF;
8090 mask = umask(0);
8091 umask(mask);
8092 INTON;
8093
8094 if ((ap = *argptr) == NULL) {
8095 if (symbolic_mode) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008096 char buf[18];
8097 char *p = buf;
8098 for (i=0 ; i<3 ; i++) {
8099 int j;
8100 *p++ = permuser[i];
8101 *p++ = '=';
8102 for (j=0 ; j<3 ; j++) {
8103 if ((mask & permmask[3*i+j]) == 0) {
8104 *p++ = permmode[j];
8105 }
8106 }
8107 *p++ = ',';
8108 }
8109 *--p = 0;
8110 puts(buf);
Eric Andersencb57d552001-06-28 07:25:16 +00008111 } else {
Eric Andersen62483552001-07-10 06:09:16 +00008112 printf("%.4o\n", mask);
Eric Andersencb57d552001-06-28 07:25:16 +00008113 }
8114 } else {
Eric Andersen62483552001-07-10 06:09:16 +00008115 if (is_digit((unsigned char)*ap)) {
Eric Andersencb57d552001-06-28 07:25:16 +00008116 mask = 0;
8117 do {
8118 if (*ap >= '8' || *ap < '0')
8119 error("Illegal number: %s", argv[1]);
8120 mask = (mask << 3) + (*ap - '0');
8121 } while (*++ap != '\0');
8122 umask(mask);
8123 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008124 mask = ~mask & 0777;
Matt Kraai1f0c4362001-12-20 23:13:26 +00008125 if (! parse_mode(ap, &mask)) {
Eric Andersencb57d552001-06-28 07:25:16 +00008126 error("Illegal mode: %s", ap);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008127 }
Eric Andersencb57d552001-06-28 07:25:16 +00008128 umask(~mask & 0777);
8129 }
8130 }
8131 return 0;
8132}
8133
8134/*
8135 * ulimit builtin
8136 *
8137 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
8138 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
8139 * ash by J.T. Conklin.
8140 *
8141 * Public domain.
8142 */
8143
8144struct limits {
8145 const char *name;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008146 short cmd;
8147 short factor; /* multiply by to get rlim_{cur,max} values */
Eric Andersencb57d552001-06-28 07:25:16 +00008148};
8149
8150static const struct limits limits[] = {
8151#ifdef RLIMIT_CPU
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008152 { "time(seconds)", RLIMIT_CPU, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008153#endif
8154#ifdef RLIMIT_FSIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008155 { "file(blocks)", RLIMIT_FSIZE, 512 },
Eric Andersencb57d552001-06-28 07:25:16 +00008156#endif
8157#ifdef RLIMIT_DATA
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008158 { "data(kbytes)", RLIMIT_DATA, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008159#endif
8160#ifdef RLIMIT_STACK
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008161 { "stack(kbytes)", RLIMIT_STACK, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008162#endif
8163#ifdef RLIMIT_CORE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008164 { "coredump(blocks)", RLIMIT_CORE, 512 },
Eric Andersencb57d552001-06-28 07:25:16 +00008165#endif
8166#ifdef RLIMIT_RSS
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008167 { "memory(kbytes)", RLIMIT_RSS, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008168#endif
8169#ifdef RLIMIT_MEMLOCK
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008170 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008171#endif
8172#ifdef RLIMIT_NPROC
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008173 { "process(processes)", RLIMIT_NPROC, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008174#endif
8175#ifdef RLIMIT_NOFILE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008176 { "nofiles(descriptors)", RLIMIT_NOFILE, 1 },
Eric Andersencb57d552001-06-28 07:25:16 +00008177#endif
8178#ifdef RLIMIT_VMEM
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008179 { "vmemory(kbytes)", RLIMIT_VMEM, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008180#endif
8181#ifdef RLIMIT_SWAP
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008182 { "swap(kbytes)", RLIMIT_SWAP, 1024 },
Eric Andersencb57d552001-06-28 07:25:16 +00008183#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008184 { NULL, 0, 0 }
Eric Andersencb57d552001-06-28 07:25:16 +00008185};
8186
8187static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008188ulimitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008189{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008190 static const char unlimited_string[] = "unlimited";
Eric Andersen2870d962001-07-02 17:27:21 +00008191 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008192 rlim_t val = 0;
8193 enum { SOFT = 0x1, HARD = 0x2 }
8194 how = SOFT | HARD;
Eric Andersen2870d962001-07-02 17:27:21 +00008195 const struct limits *l;
8196 int set, all = 0;
8197 int optc, what;
8198 struct rlimit limit;
Eric Andersencb57d552001-06-28 07:25:16 +00008199
8200 what = 'f';
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008201
8202 while ((optc = nextopt("HSa"
8203#ifdef RLIMIT_CPU
8204 "t"
8205#endif
8206#ifdef RLIMIT_FSIZE
8207 "f"
8208#endif
8209#ifdef RLIMIT_DATA
8210 "d"
8211#endif
8212#ifdef RLIMIT_STACK
8213 "s"
8214#endif
8215#ifdef RLIMIT_CORE
8216 "c"
8217#endif
8218#ifdef RLIMIT_RSS
8219 "m"
8220#endif
8221#ifdef RLIMIT_MEMLOCK
8222 "l"
8223#endif
8224#ifdef RLIMIT_NPROC
8225 "p"
8226#endif
8227#ifdef RLIMIT_NOFILE
8228 "n"
8229#endif
8230#ifdef RLIMIT_VMEM
8231 "v"
8232#endif
8233#ifdef RLIMIT_SWAP
8234 "w"
8235#endif
8236 )) != '\0') {
8237 if (optc == 'H') {
Eric Andersencb57d552001-06-28 07:25:16 +00008238 how = HARD;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008239 } else if (optc == 'S') {
Eric Andersencb57d552001-06-28 07:25:16 +00008240 how = SOFT;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008241 } else if (optc == 'a') {
Eric Andersencb57d552001-06-28 07:25:16 +00008242 all = 1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008243 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00008244 what = optc;
8245 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008246 }
Eric Andersencb57d552001-06-28 07:25:16 +00008247
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008248 for (l = limits; l->name; l++) {
8249 if(l->name[0] == what)
8250 break;
8251 if(l->name[1]=='w' && what=='w')
8252 break;
8253 }
Eric Andersencb57d552001-06-28 07:25:16 +00008254
8255 set = *argptr ? 1 : 0;
8256 if (set) {
8257 char *p = *argptr;
8258
8259 if (all || argptr[1])
8260 error("too many arguments");
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008261 if (strcmp(p, unlimited_string) == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00008262 val = RLIM_INFINITY;
8263 else {
8264 val = (rlim_t) 0;
8265
8266 while ((c = *p++) >= '0' && c <= '9')
8267 {
8268 val = (val * 10) + (long)(c - '0');
8269 if (val < (rlim_t) 0)
8270 break;
8271 }
8272 if (c)
8273 error("bad number");
8274 val *= l->factor;
8275 }
8276 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008277
Eric Andersencb57d552001-06-28 07:25:16 +00008278 if (all) {
8279 for (l = limits; l->name; l++) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008280 printf("%-20s ", l->name);
Eric Andersencb57d552001-06-28 07:25:16 +00008281 getrlimit(l->cmd, &limit);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008282 OUTPUT_LIMIT:
Eric Andersencb57d552001-06-28 07:25:16 +00008283 if (how & SOFT)
8284 val = limit.rlim_cur;
8285 else if (how & HARD)
8286 val = limit.rlim_max;
8287
Eric Andersencb57d552001-06-28 07:25:16 +00008288 if (val == RLIM_INFINITY)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008289 puts(unlimited_string);
Eric Andersencb57d552001-06-28 07:25:16 +00008290 else
8291 {
8292 val /= l->factor;
Eric Andersen62483552001-07-10 06:09:16 +00008293 printf("%lld\n", (long long) val);
Eric Andersencb57d552001-06-28 07:25:16 +00008294 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008295 if (!all) {
8296 break;
8297 }
Eric Andersencb57d552001-06-28 07:25:16 +00008298 }
8299 return 0;
8300 }
8301
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008302 if (!set) {
8303 goto OUTPUT_LIMIT;
Eric Andersencb57d552001-06-28 07:25:16 +00008304 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008305
8306 getrlimit(l->cmd, &limit);
8307 if (how & HARD)
8308 limit.rlim_max = val;
8309 if (how & SOFT)
8310 limit.rlim_cur = val;
8311 if (setrlimit(l->cmd, &limit) < 0)
8312 error("error setting limit (%m)");
Eric Andersencb57d552001-06-28 07:25:16 +00008313 return 0;
8314}
Eric Andersencb57d552001-06-28 07:25:16 +00008315/*
8316 * prefix -- see if pfx is a prefix of string.
8317 */
8318
8319static int
Eric Andersen62483552001-07-10 06:09:16 +00008320prefix(char const *pfx, char const *string)
8321{
Eric Andersencb57d552001-06-28 07:25:16 +00008322 while (*pfx) {
8323 if (*pfx++ != *string++)
8324 return 0;
8325 }
8326 return 1;
8327}
8328
Eric Andersen2870d962001-07-02 17:27:21 +00008329/*
8330 * Return true if s is a string of digits, and save munber in intptr
8331 * nagative is bad
8332 */
8333
8334static int
8335is_number(const char *p, int *intptr)
8336{
8337 int ret = 0;
8338
8339 do {
8340 if (! is_digit(*p))
8341 return 0;
8342 ret *= 10;
8343 ret += digit_val(*p);
8344 p++;
8345 } while (*p != '\0');
8346
8347 *intptr = ret;
8348 return 1;
8349}
Eric Andersencb57d552001-06-28 07:25:16 +00008350
8351/*
8352 * Convert a string of digits to an integer, printing an error message on
8353 * failure.
8354 */
8355
8356static int
Eric Andersen2870d962001-07-02 17:27:21 +00008357number(const char *s)
8358{
8359 int i;
8360 if (! is_number(s, &i))
Eric Andersencb57d552001-06-28 07:25:16 +00008361 error("Illegal number: %s", s);
Eric Andersen2870d962001-07-02 17:27:21 +00008362 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00008363}
8364
Eric Andersencb57d552001-06-28 07:25:16 +00008365/*
8366 * Produce a possibly single quoted string suitable as input to the shell.
8367 * The return string is allocated on the stack.
8368 */
8369
8370static char *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008371single_quote(const char *s)
8372{
Eric Andersencb57d552001-06-28 07:25:16 +00008373 char *p;
8374
8375 STARTSTACKSTR(p);
8376
8377 do {
8378 char *q = p;
8379 size_t len1, len1p, len2, len2p;
8380
8381 len1 = strcspn(s, "'");
8382 len2 = strspn(s + len1, "'");
8383
8384 len1p = len1 ? len1 + 2 : len1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008385 len2p = len2 + ((len2 < 2) ? len2 : 2);
Eric Andersencb57d552001-06-28 07:25:16 +00008386
8387 CHECKSTRSPACE(len1p + len2p + 1, p);
8388
8389 if (len1) {
8390 *p = '\'';
Eric Andersencb57d552001-06-28 07:25:16 +00008391 q = p + 1 + len1;
8392 memcpy(p + 1, s, len1);
Eric Andersencb57d552001-06-28 07:25:16 +00008393 *q++ = '\'';
8394 s += len1;
8395 }
8396
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008397 if (len2 > 1) {
Eric Andersencb57d552001-06-28 07:25:16 +00008398 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008399 q += 1 + len2;
8400 memcpy(q + 1, s, len2);
8401 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008402 s += len2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008403 } else if (len2 == 1) {
8404 *q++ = '\\';
8405 *q = '\'';
8406 s++;
Eric Andersencb57d552001-06-28 07:25:16 +00008407 }
8408
8409 STADJUST(len1p + len2p, p);
8410 } while (*s);
8411
8412 USTPUTC(0, p);
8413
8414 return grabstackstr(p);
8415}
8416
8417/*
8418 * Like strdup but works with the ash stack.
8419 */
8420
8421static char *
8422sstrdup(const char *p)
8423{
8424 size_t len = strlen(p) + 1;
8425 return memcpy(stalloc(len), p, len);
8426}
8427
Eric Andersencb57d552001-06-28 07:25:16 +00008428
8429/*
Eric Andersencb57d552001-06-28 07:25:16 +00008430 * Routine for dealing with parsed shell commands.
8431 */
8432
8433
Eric Andersen62483552001-07-10 06:09:16 +00008434static void sizenodelist (const struct nodelist *);
8435static struct nodelist *copynodelist (const struct nodelist *);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008436static char *nodexstrdup (const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00008437
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00008438#define CALCSIZE_TABLE
8439#define COPYNODE_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008440#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE)
8441/*
8442 * To collect a lot of redundant code in case statements for copynode()
8443 * and calcsize(), we implement a mini language here. Each type of node
8444 * struct has an associated instruction sequence that operates on its
8445 * members via their offsets. The instruction are pack in unsigned chars
8446 * with format IIDDDDDE where the bits are
8447 * I : part of the instruction opcode, which are
8448 * 00 : member is a pointer to another node
8449 * 40 : member is an integer
8450 * 80 : member is a pointer to a nodelist
8451 * CC : member is a pointer to a char string
8452 * D : data - the actual offset of the member to operate on in the struct
8453 * (since we assume bit 0 is set, it is not shifted)
8454 * E : flag signaling end of instruction sequence
8455 *
8456 * WARNING: In order to handle larger offsets for 64bit archs, this code
8457 * assumes that no offset can be an odd number and stores the
8458 * end-of-instructions flag in bit 0.
8459 */
8460
8461#define NODE_INTEGER 0x40
8462#define NODE_NODELIST 0x80
8463#define NODE_CHARPTR 0xC0
8464#define NODE_NOMORE 0x01 /* Note: no offset should be odd (aligned)*/
8465#define NODE_MBRMASK 0xC0
8466#define NODE_OFFSETMASK 0x3E
8467
8468static const unsigned char copynode_ops[35] = {
8469#define COPYNODE_OPS0 0
8470 offsetof(union node, nbinary.ch2),
8471 offsetof(union node, nbinary.ch1)|NODE_NOMORE,
8472#define COPYNODE_OPS1 (COPYNODE_OPS0 + 2)
8473 offsetof(union node, ncmd.redirect),
8474 offsetof(union node, ncmd.args),
8475 offsetof(union node, ncmd.assign),
8476 offsetof(union node, ncmd.backgnd)|NODE_INTEGER|NODE_NOMORE,
8477#define COPYNODE_OPS2 (COPYNODE_OPS1 + 4)
8478 offsetof(union node, npipe.cmdlist)|NODE_NODELIST,
8479 offsetof(union node, npipe.backgnd)|NODE_INTEGER|NODE_NOMORE,
8480#define COPYNODE_OPS3 (COPYNODE_OPS2 + 2)
8481 offsetof(union node, nredir.redirect),
8482 offsetof(union node, nredir.n)|NODE_NOMORE,
8483#define COPYNODE_OPS4 (COPYNODE_OPS3 + 2)
8484 offsetof(union node, nif.elsepart),
8485 offsetof(union node, nif.ifpart),
8486 offsetof(union node, nif.test)|NODE_NOMORE,
8487#define COPYNODE_OPS5 (COPYNODE_OPS4 + 3)
8488 offsetof(union node, nfor.var)|NODE_CHARPTR,
8489 offsetof(union node, nfor.body),
8490 offsetof(union node, nfor.args)|NODE_NOMORE,
8491#define COPYNODE_OPS6 (COPYNODE_OPS5 + 3)
8492 offsetof(union node, ncase.cases),
8493 offsetof(union node, ncase.expr)|NODE_NOMORE,
8494#define COPYNODE_OPS7 (COPYNODE_OPS6 + 2)
8495 offsetof(union node, nclist.body),
8496 offsetof(union node, nclist.pattern),
8497 offsetof(union node, nclist.next)|NODE_NOMORE,
8498#define COPYNODE_OPS8 (COPYNODE_OPS7 + 3)
8499 offsetof(union node, narg.backquote)|NODE_NODELIST,
8500 offsetof(union node, narg.text)|NODE_CHARPTR,
8501 offsetof(union node, narg.next)|NODE_NOMORE,
8502#define COPYNODE_OPS9 (COPYNODE_OPS8 + 3)
8503 offsetof(union node, nfile.fname),
8504 offsetof(union node, nfile.fd)|NODE_INTEGER,
8505 offsetof(union node, nfile.next)|NODE_NOMORE,
8506#define COPYNODE_OPS10 (COPYNODE_OPS9 + 3)
8507 offsetof(union node, ndup.vname),
8508 offsetof(union node, ndup.dupfd)|NODE_INTEGER,
8509 offsetof(union node, ndup.fd)|NODE_INTEGER,
8510 offsetof(union node, ndup.next)|NODE_NOMORE,
8511#define COPYNODE_OPS11 (COPYNODE_OPS10 + 4)
8512 offsetof(union node, nhere.doc),
8513 offsetof(union node, nhere.fd)|NODE_INTEGER,
8514 offsetof(union node, nhere.next)|NODE_NOMORE,
8515#define COPYNODE_OPS12 (COPYNODE_OPS11 + 3)
8516 offsetof(union node, nnot.com)|NODE_NOMORE,
8517};
8518
8519#if COPYNODE_OPS12 != 34
8520#error COPYNODE_OPS12 is incorrect
8521#endif
8522
8523static const unsigned char copynode_ops_index[26] = {
8524 COPYNODE_OPS0, /* NSEMI */
8525 COPYNODE_OPS1, /* NCMD */
8526 COPYNODE_OPS2, /* NPIPE */
8527 COPYNODE_OPS3, /* NREDIR */
8528 COPYNODE_OPS3, /* NBACKGND */
8529 COPYNODE_OPS3, /* NSUBSHELL */
8530 COPYNODE_OPS0, /* NAND */
8531 COPYNODE_OPS0, /* NOR */
8532 COPYNODE_OPS4, /* NIF */
8533 COPYNODE_OPS0, /* NWHILE */
8534 COPYNODE_OPS0, /* NUNTIL */
8535 COPYNODE_OPS5, /* NFOR */
8536 COPYNODE_OPS6, /* NCASE */
8537 COPYNODE_OPS7, /* NCLIST */
8538 COPYNODE_OPS8, /* NDEFUN */
8539 COPYNODE_OPS8, /* NARG */
8540 COPYNODE_OPS9, /* NTO */
8541 COPYNODE_OPS9, /* NFROM */
8542 COPYNODE_OPS9, /* NFROMTO */
8543 COPYNODE_OPS9, /* NAPPEND */
8544 COPYNODE_OPS9, /* NTOOV */
8545 COPYNODE_OPS10, /* NTOFD */
8546 COPYNODE_OPS10, /* NFROMFD */
8547 COPYNODE_OPS11, /* NHERE */
8548 COPYNODE_OPS11, /* NXHERE */
8549 COPYNODE_OPS12, /* NNOT */
8550};
8551
8552#if NODE_CHARPTR != NODE_MBRMASK
8553#error NODE_CHARPTR != NODE_MBRMASK!!!
8554#endif
8555#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
8556
8557#ifdef COPYNODE_TABLE
8558static union node *
8559copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008560{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008561 union node *new;
8562 const unsigned char *p;
8563
Manuel Novoa III c639a352001-08-12 17:32:56 +00008564 if (n == NULL) {
8565 return NULL;
8566 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008567 new = funcblock;
8568 new->type = n->type;
8569 funcblock = (char *) funcblock + (int) nodesize[n->type];
8570 p = copynode_ops + (int) copynode_ops_index[n->type];
8571 do {
8572 char *nn = ((char *) new) + ((int)(*p & NODE_OFFSETMASK));
8573 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
8574
8575 if (!(*p & NODE_MBRMASK)) { /* standard node */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008576 *((union node **)nn) = copynode(*((const union node **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008577 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008578 *((const char **)nn) = nodexstrdup(*((const char **)no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008579 } else if (*p & NODE_NODELIST) { /* nodelist */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008580 *((struct nodelist **)nn)
8581 = copynodelist(*((const struct nodelist **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008582 } else { /* integer */
8583 *((int *) nn) = *((int *) no);
8584 }
8585 } while (!(*p++ & NODE_NOMORE));
8586 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008587}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008588#else /* COPYNODE_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00008589static union node *
Eric Andersen62483552001-07-10 06:09:16 +00008590copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008591{
Eric Andersen62483552001-07-10 06:09:16 +00008592 union node *new;
Eric Andersencb57d552001-06-28 07:25:16 +00008593
8594 if (n == NULL)
Manuel Novoa III c639a352001-08-12 17:32:56 +00008595 return NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008596 new = funcblock;
8597 funcblock = (char *) funcblock + nodesize[n->type];
8598 switch (n->type) {
8599 case NSEMI:
8600 case NAND:
8601 case NOR:
8602 case NWHILE:
8603 case NUNTIL:
8604 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8605 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8606 break;
8607 case NCMD:
8608 new->ncmd.redirect = copynode(n->ncmd.redirect);
8609 new->ncmd.args = copynode(n->ncmd.args);
8610 new->ncmd.assign = copynode(n->ncmd.assign);
8611 new->ncmd.backgnd = n->ncmd.backgnd;
8612 break;
8613 case NPIPE:
8614 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8615 new->npipe.backgnd = n->npipe.backgnd;
8616 break;
8617 case NREDIR:
8618 case NBACKGND:
8619 case NSUBSHELL:
8620 new->nredir.redirect = copynode(n->nredir.redirect);
8621 new->nredir.n = copynode(n->nredir.n);
8622 break;
8623 case NIF:
8624 new->nif.elsepart = copynode(n->nif.elsepart);
8625 new->nif.ifpart = copynode(n->nif.ifpart);
8626 new->nif.test = copynode(n->nif.test);
8627 break;
8628 case NFOR:
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008629 new->nfor.var = nodexstrdup(n->nfor.var);
Eric Andersencb57d552001-06-28 07:25:16 +00008630 new->nfor.body = copynode(n->nfor.body);
8631 new->nfor.args = copynode(n->nfor.args);
8632 break;
8633 case NCASE:
8634 new->ncase.cases = copynode(n->ncase.cases);
8635 new->ncase.expr = copynode(n->ncase.expr);
8636 break;
8637 case NCLIST:
8638 new->nclist.body = copynode(n->nclist.body);
8639 new->nclist.pattern = copynode(n->nclist.pattern);
8640 new->nclist.next = copynode(n->nclist.next);
8641 break;
8642 case NDEFUN:
8643 case NARG:
8644 new->narg.backquote = copynodelist(n->narg.backquote);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008645 new->narg.text = nodexstrdup(n->narg.text);
Eric Andersencb57d552001-06-28 07:25:16 +00008646 new->narg.next = copynode(n->narg.next);
8647 break;
8648 case NTO:
8649 case NFROM:
8650 case NFROMTO:
8651 case NAPPEND:
8652 case NTOOV:
8653 new->nfile.fname = copynode(n->nfile.fname);
8654 new->nfile.fd = n->nfile.fd;
8655 new->nfile.next = copynode(n->nfile.next);
8656 break;
8657 case NTOFD:
8658 case NFROMFD:
8659 new->ndup.vname = copynode(n->ndup.vname);
8660 new->ndup.dupfd = n->ndup.dupfd;
8661 new->ndup.fd = n->ndup.fd;
8662 new->ndup.next = copynode(n->ndup.next);
8663 break;
8664 case NHERE:
8665 case NXHERE:
8666 new->nhere.doc = copynode(n->nhere.doc);
8667 new->nhere.fd = n->nhere.fd;
8668 new->nhere.next = copynode(n->nhere.next);
8669 break;
8670 case NNOT:
8671 new->nnot.com = copynode(n->nnot.com);
8672 break;
8673 };
8674 new->type = n->type;
Eric Andersen62483552001-07-10 06:09:16 +00008675 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008676}
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008677#endif /* COPYNODE_TABLE */
8678
8679#ifdef CALCSIZE_TABLE
8680static void
8681calcsize(const union node *n)
8682{
8683 const unsigned char *p;
8684
8685 if (n == NULL)
8686 return;
8687 funcblocksize += (int) nodesize[n->type];
8688
8689 p = copynode_ops + (int) copynode_ops_index[n->type];
8690 do {
8691 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
8692
8693 if (!(*p & NODE_MBRMASK)) { /* standard node */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008694 calcsize(*((const union node **) no));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008695 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008696 funcstringsize += strlen(*((const char **)no)) + 1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008697 } else if (*p & NODE_NODELIST) { /* nodelist */
Manuel Novoa III c639a352001-08-12 17:32:56 +00008698 sizenodelist(*((const struct nodelist **) no));
8699 } /* else integer -- ignore */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008700 } while (!(*p++ & NODE_NOMORE));
8701}
8702#else /* CALCSIZE_TABLE */
8703static void
8704calcsize(const union node *n)
8705{
8706 if (n == NULL)
8707 return;
8708 funcblocksize += nodesize[n->type];
8709 switch (n->type) {
8710 case NSEMI:
8711 case NAND:
8712 case NOR:
8713 case NWHILE:
8714 case NUNTIL:
8715 calcsize(n->nbinary.ch2);
8716 calcsize(n->nbinary.ch1);
8717 break;
8718 case NCMD:
8719 calcsize(n->ncmd.redirect);
8720 calcsize(n->ncmd.args);
8721 calcsize(n->ncmd.assign);
8722 break;
8723 case NPIPE:
8724 sizenodelist(n->npipe.cmdlist);
8725 break;
8726 case NREDIR:
8727 case NBACKGND:
8728 case NSUBSHELL:
8729 calcsize(n->nredir.redirect);
8730 calcsize(n->nredir.n);
8731 break;
8732 case NIF:
8733 calcsize(n->nif.elsepart);
8734 calcsize(n->nif.ifpart);
8735 calcsize(n->nif.test);
8736 break;
8737 case NFOR:
8738 funcstringsize += strlen(n->nfor.var) + 1;
8739 calcsize(n->nfor.body);
8740 calcsize(n->nfor.args);
8741 break;
8742 case NCASE:
8743 calcsize(n->ncase.cases);
8744 calcsize(n->ncase.expr);
8745 break;
8746 case NCLIST:
8747 calcsize(n->nclist.body);
8748 calcsize(n->nclist.pattern);
8749 calcsize(n->nclist.next);
8750 break;
8751 case NDEFUN:
8752 case NARG:
8753 sizenodelist(n->narg.backquote);
8754 funcstringsize += strlen(n->narg.text) + 1;
8755 calcsize(n->narg.next);
8756 break;
8757 case NTO:
8758 case NFROM:
8759 case NFROMTO:
8760 case NAPPEND:
8761 case NTOOV:
8762 calcsize(n->nfile.fname);
8763 calcsize(n->nfile.next);
8764 break;
8765 case NTOFD:
8766 case NFROMFD:
8767 calcsize(n->ndup.vname);
8768 calcsize(n->ndup.next);
8769 break;
8770 case NHERE:
8771 case NXHERE:
8772 calcsize(n->nhere.doc);
8773 calcsize(n->nhere.next);
8774 break;
8775 case NNOT:
8776 calcsize(n->nnot.com);
8777 break;
8778 };
8779}
8780#endif /* CALCSIZE_TABLE */
8781
8782static void
8783sizenodelist(const struct nodelist *lp)
8784{
8785 while (lp) {
8786 funcblocksize += ALIGN(sizeof(struct nodelist));
8787 calcsize(lp->n);
8788 lp = lp->next;
8789 }
8790}
Eric Andersencb57d552001-06-28 07:25:16 +00008791
8792
8793static struct nodelist *
Eric Andersen62483552001-07-10 06:09:16 +00008794copynodelist(const struct nodelist *lp)
Eric Andersencb57d552001-06-28 07:25:16 +00008795{
8796 struct nodelist *start;
8797 struct nodelist **lpp;
8798
8799 lpp = &start;
8800 while (lp) {
8801 *lpp = funcblock;
8802 funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist));
8803 (*lpp)->n = copynode(lp->n);
8804 lp = lp->next;
8805 lpp = &(*lpp)->next;
8806 }
8807 *lpp = NULL;
8808 return start;
8809}
8810
8811
Eric Andersencb57d552001-06-28 07:25:16 +00008812static char *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008813nodexstrdup(const char *s)
Eric Andersencb57d552001-06-28 07:25:16 +00008814{
Eric Andersen62483552001-07-10 06:09:16 +00008815 const char *p = s;
8816 char *q = funcstring;
Eric Andersencb57d552001-06-28 07:25:16 +00008817 char *rtn = funcstring;
8818
8819 while ((*q++ = *p++) != '\0')
8820 continue;
8821 funcstring = q;
8822 return rtn;
Eric Andersencb57d552001-06-28 07:25:16 +00008823}
8824
Eric Andersend35c5df2002-01-09 15:37:36 +00008825#ifdef CONFIG_ASH_GETOPTS
Eric Andersen2870d962001-07-02 17:27:21 +00008826static int getopts (char *, char *, char **, int *, int *);
Eric Andersencb57d552001-06-28 07:25:16 +00008827#endif
8828
8829
8830/*
8831 * Process the shell command line arguments.
8832 */
8833
8834static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008835procargs(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008836{
8837 int i;
8838
8839 argptr = argv;
8840 if (argc > 0)
8841 argptr++;
8842 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008843 optent_val(i) = 2;
Eric Andersencb57d552001-06-28 07:25:16 +00008844 options(1);
8845 if (*argptr == NULL && minusc == NULL)
8846 sflag = 1;
8847 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8848 iflag = 1;
8849 if (mflag == 2)
8850 mflag = iflag;
8851 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008852 if (optent_val(i) == 2)
8853 optent_val(i) = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008854 arg0 = argv[0];
8855 if (sflag == 0 && minusc == NULL) {
8856 commandname = argv[0];
8857 arg0 = *argptr++;
8858 setinputfile(arg0, 0);
8859 commandname = arg0;
8860 }
8861 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8862 if (argptr && minusc && *argptr)
Eric Andersen2870d962001-07-02 17:27:21 +00008863 arg0 = *argptr++;
Eric Andersencb57d552001-06-28 07:25:16 +00008864
8865 shellparam.p = argptr;
8866 shellparam.optind = 1;
8867 shellparam.optoff = -1;
8868 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8869 while (*argptr) {
8870 shellparam.nparam++;
8871 argptr++;
8872 }
8873 optschanged();
8874}
8875
8876
Eric Andersencb57d552001-06-28 07:25:16 +00008877
8878/*
8879 * Process shell options. The global variable argptr contains a pointer
8880 * to the argument list; we advance it past the options.
8881 */
8882
Eric Andersen62483552001-07-10 06:09:16 +00008883static inline void
8884minus_o(const char *name, int val)
8885{
8886 int i;
8887
8888 if (name == NULL) {
8889 out1str("Current option settings\n");
8890 for (i = 0; i < NOPTS; i++)
8891 printf("%-16s%s\n", optent_name(optlist[i]),
8892 optent_val(i) ? "on" : "off");
8893 } else {
8894 for (i = 0; i < NOPTS; i++)
8895 if (equal(name, optent_name(optlist[i]))) {
8896 setoption(optent_letter(optlist[i]), val);
8897 return;
8898 }
8899 error("Illegal option -o %s", name);
8900 }
8901}
8902
8903
Eric Andersencb57d552001-06-28 07:25:16 +00008904static void
Eric Andersen62483552001-07-10 06:09:16 +00008905options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00008906{
8907 char *p;
8908 int val;
8909 int c;
8910
8911 if (cmdline)
8912 minusc = NULL;
8913 while ((p = *argptr) != NULL) {
8914 argptr++;
8915 if ((c = *p++) == '-') {
8916 val = 1;
Eric Andersen2870d962001-07-02 17:27:21 +00008917 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8918 if (!cmdline) {
8919 /* "-" means turn off -x and -v */
8920 if (p[0] == '\0')
8921 xflag = vflag = 0;
8922 /* "--" means reset params */
8923 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00008924 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00008925 }
8926 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00008927 }
8928 } else if (c == '+') {
8929 val = 0;
8930 } else {
8931 argptr--;
8932 break;
8933 }
8934 while ((c = *p++) != '\0') {
8935 if (c == 'c' && cmdline) {
8936 char *q;
Eric Andersen2870d962001-07-02 17:27:21 +00008937#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */
Eric Andersencb57d552001-06-28 07:25:16 +00008938 if (*p == '\0')
8939#endif
8940 q = *argptr++;
8941 if (q == NULL || minusc != NULL)
8942 error("Bad -c option");
8943 minusc = q;
8944#ifdef NOHACK
8945 break;
8946#endif
8947 } else if (c == 'o') {
8948 minus_o(*argptr, val);
8949 if (*argptr)
8950 argptr++;
8951 } else {
8952 setoption(c, val);
8953 }
8954 }
8955 }
8956}
8957
Eric Andersencb57d552001-06-28 07:25:16 +00008958
8959static void
Eric Andersen2870d962001-07-02 17:27:21 +00008960setoption(int flag, int val)
8961{
Eric Andersencb57d552001-06-28 07:25:16 +00008962 int i;
8963
8964 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008965 if (optent_letter(optlist[i]) == flag) {
8966 optent_val(i) = val;
Eric Andersencb57d552001-06-28 07:25:16 +00008967 if (val) {
8968 /* #%$ hack for ksh semantics */
8969 if (flag == 'V')
8970 Eflag = 0;
8971 else if (flag == 'E')
8972 Vflag = 0;
8973 }
8974 return;
8975 }
8976 error("Illegal option -%c", flag);
8977 /* NOTREACHED */
8978}
8979
8980
8981
Eric Andersencb57d552001-06-28 07:25:16 +00008982/*
8983 * Set the shell parameters.
8984 */
8985
8986static void
Eric Andersen2870d962001-07-02 17:27:21 +00008987setparam(char **argv)
8988{
Eric Andersencb57d552001-06-28 07:25:16 +00008989 char **newparam;
8990 char **ap;
8991 int nparam;
8992
8993 for (nparam = 0 ; argv[nparam] ; nparam++);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008994 ap = newparam = xmalloc((nparam + 1) * sizeof *ap);
Eric Andersencb57d552001-06-28 07:25:16 +00008995 while (*argv) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008996 *ap++ = xstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008997 }
8998 *ap = NULL;
8999 freeparam(&shellparam);
9000 shellparam.malloc = 1;
9001 shellparam.nparam = nparam;
9002 shellparam.p = newparam;
9003 shellparam.optind = 1;
9004 shellparam.optoff = -1;
9005}
9006
9007
9008/*
9009 * Free the list of positional parameters.
9010 */
9011
9012static void
Eric Andersen2870d962001-07-02 17:27:21 +00009013freeparam(volatile struct shparam *param)
9014{
Eric Andersencb57d552001-06-28 07:25:16 +00009015 char **ap;
9016
9017 if (param->malloc) {
9018 for (ap = param->p ; *ap ; ap++)
9019 ckfree(*ap);
9020 ckfree(param->p);
9021 }
9022}
9023
9024
9025
9026/*
9027 * The shift builtin command.
9028 */
9029
9030static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009031shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009032{
9033 int n;
9034 char **ap1, **ap2;
9035
9036 n = 1;
9037 if (argc > 1)
9038 n = number(argv[1]);
9039 if (n > shellparam.nparam)
9040 error("can't shift that many");
9041 INTOFF;
9042 shellparam.nparam -= n;
9043 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
9044 if (shellparam.malloc)
9045 ckfree(*ap1);
9046 }
9047 ap2 = shellparam.p;
9048 while ((*ap2++ = *ap1++) != NULL);
9049 shellparam.optind = 1;
9050 shellparam.optoff = -1;
9051 INTON;
9052 return 0;
9053}
9054
9055
9056
9057/*
9058 * The set command builtin.
9059 */
9060
9061static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009062setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009063{
9064 if (argc == 1)
9065 return showvarscmd(argc, argv);
9066 INTOFF;
9067 options(0);
9068 optschanged();
9069 if (*argptr != NULL) {
9070 setparam(argptr);
9071 }
9072 INTON;
9073 return 0;
9074}
9075
9076
9077static void
Eric Andersen2870d962001-07-02 17:27:21 +00009078getoptsreset(const char *value)
Eric Andersencb57d552001-06-28 07:25:16 +00009079{
9080 shellparam.optind = number(value);
9081 shellparam.optoff = -1;
9082}
9083
Eric Andersenbdfd0d72001-10-24 05:00:29 +00009084#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00009085static void change_lc_all(const char *value)
9086{
9087 if(value != 0 && *value != 0)
9088 setlocale(LC_ALL, value);
9089}
9090
9091static void change_lc_ctype(const char *value)
9092{
9093 if(value != 0 && *value != 0)
9094 setlocale(LC_CTYPE, value);
9095}
9096
9097#endif
9098
Eric Andersend35c5df2002-01-09 15:37:36 +00009099#ifdef CONFIG_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009100/*
9101 * The getopts builtin. Shellparam.optnext points to the next argument
9102 * to be processed. Shellparam.optptr points to the next character to
9103 * be processed in the current argument. If shellparam.optnext is NULL,
9104 * then it's the first time getopts has been called.
9105 */
9106
9107static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009108getoptscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009109{
9110 char **optbase;
9111
9112 if (argc < 3)
9113 error("Usage: getopts optstring var [arg]");
9114 else if (argc == 3) {
9115 optbase = shellparam.p;
9116 if (shellparam.optind > shellparam.nparam + 1) {
9117 shellparam.optind = 1;
9118 shellparam.optoff = -1;
9119 }
9120 }
9121 else {
9122 optbase = &argv[3];
9123 if (shellparam.optind > argc - 2) {
9124 shellparam.optind = 1;
9125 shellparam.optoff = -1;
9126 }
9127 }
9128
9129 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
9130 &shellparam.optoff);
9131}
9132
9133/*
9134 * Safe version of setvar, returns 1 on success 0 on failure.
9135 */
9136
9137static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009138setvarsafe(const char *name, const char *val, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009139{
9140 struct jmploc jmploc;
9141 struct jmploc *volatile savehandler = handler;
9142 int err = 0;
9143#ifdef __GNUC__
9144 (void) &err;
9145#endif
9146
9147 if (setjmp(jmploc.loc))
9148 err = 1;
9149 else {
9150 handler = &jmploc;
9151 setvar(name, val, flags);
9152 }
9153 handler = savehandler;
9154 return err;
9155}
9156
9157static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009158getopts(char *optstr, char *optvar, char **optfirst, int *myoptind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009159{
9160 char *p, *q;
9161 char c = '?';
9162 int done = 0;
9163 int err = 0;
9164 char s[10];
9165 char **optnext = optfirst + *myoptind - 1;
9166
9167 if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
9168 strlen(*(optnext - 1)) < *optoff)
9169 p = NULL;
9170 else
9171 p = *(optnext - 1) + *optoff;
9172 if (p == NULL || *p == '\0') {
9173 /* Current word is done, advance */
9174 if (optnext == NULL)
9175 return 1;
9176 p = *optnext;
9177 if (p == NULL || *p != '-' || *++p == '\0') {
9178atend:
9179 *myoptind = optnext - optfirst + 1;
9180 p = NULL;
9181 done = 1;
9182 goto out;
9183 }
9184 optnext++;
Eric Andersen2870d962001-07-02 17:27:21 +00009185 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009186 goto atend;
9187 }
9188
9189 c = *p++;
9190 for (q = optstr; *q != c; ) {
9191 if (*q == '\0') {
9192 if (optstr[0] == ':') {
9193 s[0] = c;
9194 s[1] = '\0';
9195 err |= setvarsafe("OPTARG", s, 0);
9196 }
9197 else {
Eric Andersen3102ac42001-07-06 04:26:23 +00009198 out2fmt("Illegal option -%c\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00009199 (void) unsetvar("OPTARG");
9200 }
9201 c = '?';
9202 goto bad;
9203 }
9204 if (*++q == ':')
9205 q++;
9206 }
9207
9208 if (*++q == ':') {
9209 if (*p == '\0' && (p = *optnext) == NULL) {
9210 if (optstr[0] == ':') {
9211 s[0] = c;
9212 s[1] = '\0';
9213 err |= setvarsafe("OPTARG", s, 0);
9214 c = ':';
9215 }
9216 else {
Eric Andersen3102ac42001-07-06 04:26:23 +00009217 out2fmt("No arg for -%c option\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00009218 (void) unsetvar("OPTARG");
9219 c = '?';
9220 }
9221 goto bad;
9222 }
9223
9224 if (p == *optnext)
9225 optnext++;
9226 setvarsafe("OPTARG", p, 0);
9227 p = NULL;
9228 }
9229 else
9230 setvarsafe("OPTARG", "", 0);
9231 *myoptind = optnext - optfirst + 1;
9232 goto out;
9233
9234bad:
9235 *myoptind = 1;
9236 p = NULL;
9237out:
9238 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersen3102ac42001-07-06 04:26:23 +00009239 snprintf(s, sizeof(s), "%d", *myoptind);
Eric Andersencb57d552001-06-28 07:25:16 +00009240 err |= setvarsafe("OPTIND", s, VNOFUNC);
9241 s[0] = c;
9242 s[1] = '\0';
9243 err |= setvarsafe(optvar, s, 0);
9244 if (err) {
9245 *myoptind = 1;
9246 *optoff = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00009247 exraise(EXERROR);
9248 }
9249 return done;
9250}
Eric Andersen2870d962001-07-02 17:27:21 +00009251#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009252
9253/*
9254 * XXX - should get rid of. have all builtins use getopt(3). the
9255 * library getopt must have the BSD extension static variable "optreset"
9256 * otherwise it can't be used within the shell safely.
9257 *
9258 * Standard option processing (a la getopt) for builtin routines. The
9259 * only argument that is passed to nextopt is the option string; the
9260 * other arguments are unnecessary. It return the character, or '\0' on
9261 * end of input.
9262 */
9263
9264static int
Eric Andersen62483552001-07-10 06:09:16 +00009265nextopt(const char *optstring)
9266{
Eric Andersencb57d552001-06-28 07:25:16 +00009267 char *p;
9268 const char *q;
9269 char c;
9270
9271 if ((p = optptr) == NULL || *p == '\0') {
9272 p = *argptr;
9273 if (p == NULL || *p != '-' || *++p == '\0')
9274 return '\0';
9275 argptr++;
Eric Andersen2870d962001-07-02 17:27:21 +00009276 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009277 return '\0';
9278 }
9279 c = *p++;
9280 for (q = optstring ; *q != c ; ) {
9281 if (*q == '\0')
9282 error("Illegal option -%c", c);
9283 if (*++q == ':')
9284 q++;
9285 }
9286 if (*++q == ':') {
9287 if (*p == '\0' && (p = *argptr++) == NULL)
9288 error("No arg for -%c option", c);
9289 optionarg = p;
9290 p = NULL;
9291 }
9292 optptr = p;
9293 return c;
9294}
9295
Eric Andersencb57d552001-06-28 07:25:16 +00009296static void
9297flushall() {
Eric Andersencb57d552001-06-28 07:25:16 +00009298 INTOFF;
Eric Andersen3102ac42001-07-06 04:26:23 +00009299 fflush(stdout);
Eric Andersencb57d552001-06-28 07:25:16 +00009300 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00009301}
9302
9303
9304static void
Eric Andersen3102ac42001-07-06 04:26:23 +00009305out2fmt(const char *fmt, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00009306{
9307 va_list ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009308 va_start(ap, fmt);
Eric Andersen3102ac42001-07-06 04:26:23 +00009309 vfprintf(stderr, fmt, ap);
Eric Andersencb57d552001-06-28 07:25:16 +00009310 va_end(ap);
9311}
9312
Eric Andersencb57d552001-06-28 07:25:16 +00009313/*
9314 * Version of write which resumes after a signal is caught.
9315 */
9316
9317static int
Eric Andersen2870d962001-07-02 17:27:21 +00009318xwrite(int fd, const char *buf, int nbytes)
9319{
Eric Andersencb57d552001-06-28 07:25:16 +00009320 int ntry;
9321 int i;
9322 int n;
9323
9324 n = nbytes;
9325 ntry = 0;
9326 for (;;) {
9327 i = write(fd, buf, n);
9328 if (i > 0) {
9329 if ((n -= i) <= 0)
9330 return nbytes;
9331 buf += i;
9332 ntry = 0;
9333 } else if (i == 0) {
9334 if (++ntry > 10)
9335 return nbytes - n;
9336 } else if (errno != EINTR) {
9337 return -1;
9338 }
9339 }
9340}
9341
9342
Eric Andersencb57d552001-06-28 07:25:16 +00009343/*
9344 * Shell command parser.
9345 */
9346
9347#define EOFMARKLEN 79
9348
9349
9350
9351struct heredoc {
Eric Andersen2870d962001-07-02 17:27:21 +00009352 struct heredoc *next; /* next here document in list */
9353 union node *here; /* redirection node */
9354 char *eofmark; /* string indicating end of input */
9355 int striptabs; /* if set, strip leading tabs */
Eric Andersencb57d552001-06-28 07:25:16 +00009356};
9357
Eric Andersen2870d962001-07-02 17:27:21 +00009358static struct heredoc *heredoclist; /* list of here documents to read */
9359static int parsebackquote; /* nonzero if we are inside backquotes */
9360static int doprompt; /* if set, prompt the user */
9361static int needprompt; /* true if interactive and at start of line */
9362static int lasttoken; /* last token read */
Eric Andersencb57d552001-06-28 07:25:16 +00009363
Eric Andersen2870d962001-07-02 17:27:21 +00009364static char *wordtext; /* text of last word returned by readtoken */
Eric Andersencb57d552001-06-28 07:25:16 +00009365
Eric Andersen2870d962001-07-02 17:27:21 +00009366static struct nodelist *backquotelist;
9367static union node *redirnode;
Eric Andersen044228d2001-07-17 01:12:36 +00009368static struct heredoc *heredoc;
Eric Andersen2870d962001-07-02 17:27:21 +00009369static int quoteflag; /* set if (part of) last token was quoted */
9370static int startlinno; /* line # where last token started */
Eric Andersencb57d552001-06-28 07:25:16 +00009371
9372
Eric Andersen2870d962001-07-02 17:27:21 +00009373static union node *list (int);
9374static union node *andor (void);
9375static union node *pipeline (void);
9376static union node *command (void);
Eric Andersena3483db2001-10-24 08:01:06 +00009377static union node *simplecmd(union node **rpp, union node *redir);
Eric Andersen2870d962001-07-02 17:27:21 +00009378static void parsefname (void);
9379static void parseheredoc (void);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009380static char peektoken (void);
Eric Andersen2870d962001-07-02 17:27:21 +00009381static int readtoken (void);
9382static int xxreadtoken (void);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009383static int readtoken1 (int, int, const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00009384static int noexpand (char *);
9385static void synexpect (int) __attribute__((noreturn));
9386static void synerror (const char *) __attribute__((noreturn));
9387static void setprompt (int);
Eric Andersencb57d552001-06-28 07:25:16 +00009388
9389
9390/*
9391 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9392 * valid parse tree indicating a blank line.)
9393 */
9394
Eric Andersen2870d962001-07-02 17:27:21 +00009395static union node *
Eric Andersencb57d552001-06-28 07:25:16 +00009396parsecmd(int interact)
9397{
9398 int t;
9399
9400 tokpushback = 0;
9401 doprompt = interact;
9402 if (doprompt)
9403 setprompt(1);
9404 else
9405 setprompt(0);
9406 needprompt = 0;
9407 t = readtoken();
9408 if (t == TEOF)
9409 return NEOF;
9410 if (t == TNL)
9411 return NULL;
9412 tokpushback++;
9413 return list(1);
9414}
9415
9416
9417static union node *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00009418list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009419{
9420 union node *n1, *n2, *n3;
9421 int tok;
9422
9423 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009424 if (nlflag == 0 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009425 return NULL;
9426 n1 = NULL;
9427 for (;;) {
9428 n2 = andor();
9429 tok = readtoken();
9430 if (tok == TBACKGND) {
9431 if (n2->type == NCMD || n2->type == NPIPE) {
9432 n2->ncmd.backgnd = 1;
9433 } else if (n2->type == NREDIR) {
9434 n2->type = NBACKGND;
9435 } else {
9436 n3 = (union node *)stalloc(sizeof (struct nredir));
9437 n3->type = NBACKGND;
9438 n3->nredir.n = n2;
9439 n3->nredir.redirect = NULL;
9440 n2 = n3;
9441 }
9442 }
9443 if (n1 == NULL) {
9444 n1 = n2;
9445 }
9446 else {
9447 n3 = (union node *)stalloc(sizeof (struct nbinary));
9448 n3->type = NSEMI;
9449 n3->nbinary.ch1 = n1;
9450 n3->nbinary.ch2 = n2;
9451 n1 = n3;
9452 }
9453 switch (tok) {
9454 case TBACKGND:
9455 case TSEMI:
9456 tok = readtoken();
9457 /* fall through */
9458 case TNL:
9459 if (tok == TNL) {
9460 parseheredoc();
9461 if (nlflag)
9462 return n1;
9463 } else {
9464 tokpushback++;
9465 }
9466 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009467 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009468 return n1;
9469 break;
9470 case TEOF:
9471 if (heredoclist)
9472 parseheredoc();
9473 else
Eric Andersen2870d962001-07-02 17:27:21 +00009474 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009475 return n1;
9476 default:
9477 if (nlflag)
9478 synexpect(-1);
9479 tokpushback++;
9480 return n1;
9481 }
9482 }
9483}
9484
9485
9486
9487static union node *
9488andor() {
9489 union node *n1, *n2, *n3;
9490 int t;
9491
9492 checkkwd = 1;
9493 n1 = pipeline();
9494 for (;;) {
9495 if ((t = readtoken()) == TAND) {
9496 t = NAND;
9497 } else if (t == TOR) {
9498 t = NOR;
9499 } else {
9500 tokpushback++;
9501 return n1;
9502 }
9503 checkkwd = 2;
9504 n2 = pipeline();
9505 n3 = (union node *)stalloc(sizeof (struct nbinary));
9506 n3->type = t;
9507 n3->nbinary.ch1 = n1;
9508 n3->nbinary.ch2 = n2;
9509 n1 = n3;
9510 }
9511}
9512
9513
9514
9515static union node *
9516pipeline() {
9517 union node *n1, *n2, *pipenode;
9518 struct nodelist *lp, *prev;
9519 int negate;
9520
9521 negate = 0;
9522 TRACE(("pipeline: entered\n"));
9523 if (readtoken() == TNOT) {
9524 negate = !negate;
9525 checkkwd = 1;
9526 } else
9527 tokpushback++;
9528 n1 = command();
9529 if (readtoken() == TPIPE) {
9530 pipenode = (union node *)stalloc(sizeof (struct npipe));
9531 pipenode->type = NPIPE;
9532 pipenode->npipe.backgnd = 0;
9533 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9534 pipenode->npipe.cmdlist = lp;
9535 lp->n = n1;
9536 do {
9537 prev = lp;
9538 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9539 checkkwd = 2;
9540 lp->n = command();
9541 prev->next = lp;
9542 } while (readtoken() == TPIPE);
9543 lp->next = NULL;
9544 n1 = pipenode;
9545 }
9546 tokpushback++;
9547 if (negate) {
9548 n2 = (union node *)stalloc(sizeof (struct nnot));
9549 n2->type = NNOT;
9550 n2->nnot.com = n1;
9551 return n2;
9552 } else
9553 return n1;
9554}
9555
9556
9557
9558static union node *
Eric Andersena3483db2001-10-24 08:01:06 +00009559command(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00009560 union node *n1, *n2;
9561 union node *ap, **app;
9562 union node *cp, **cpp;
9563 union node *redir, **rpp;
9564 int t;
9565
9566 redir = NULL;
9567 n1 = NULL;
9568 rpp = &redir;
9569
Eric Andersen88cec252001-09-06 17:35:20 +00009570 /* Check for redirection which may precede command */
9571 while (readtoken() == TREDIR) {
9572 *rpp = n2 = redirnode;
9573 rpp = &n2->nfile.next;
9574 parsefname();
9575 }
9576 tokpushback++;
9577
Eric Andersencb57d552001-06-28 07:25:16 +00009578 switch (readtoken()) {
9579 case TIF:
9580 n1 = (union node *)stalloc(sizeof (struct nif));
9581 n1->type = NIF;
9582 n1->nif.test = list(0);
9583 if (readtoken() != TTHEN)
9584 synexpect(TTHEN);
9585 n1->nif.ifpart = list(0);
9586 n2 = n1;
9587 while (readtoken() == TELIF) {
9588 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9589 n2 = n2->nif.elsepart;
9590 n2->type = NIF;
9591 n2->nif.test = list(0);
9592 if (readtoken() != TTHEN)
9593 synexpect(TTHEN);
9594 n2->nif.ifpart = list(0);
9595 }
9596 if (lasttoken == TELSE)
9597 n2->nif.elsepart = list(0);
9598 else {
9599 n2->nif.elsepart = NULL;
9600 tokpushback++;
9601 }
9602 if (readtoken() != TFI)
9603 synexpect(TFI);
9604 checkkwd = 1;
9605 break;
9606 case TWHILE:
9607 case TUNTIL: {
9608 int got;
9609 n1 = (union node *)stalloc(sizeof (struct nbinary));
9610 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9611 n1->nbinary.ch1 = list(0);
9612 if ((got=readtoken()) != TDO) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009613TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00009614 synexpect(TDO);
9615 }
9616 n1->nbinary.ch2 = list(0);
9617 if (readtoken() != TDONE)
9618 synexpect(TDONE);
9619 checkkwd = 1;
9620 break;
9621 }
9622 case TFOR:
9623 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9624 synerror("Bad for loop variable");
9625 n1 = (union node *)stalloc(sizeof (struct nfor));
9626 n1->type = NFOR;
9627 n1->nfor.var = wordtext;
9628 checkkwd = 1;
9629 if (readtoken() == TIN) {
9630 app = &ap;
9631 while (readtoken() == TWORD) {
9632 n2 = (union node *)stalloc(sizeof (struct narg));
9633 n2->type = NARG;
9634 n2->narg.text = wordtext;
9635 n2->narg.backquote = backquotelist;
9636 *app = n2;
9637 app = &n2->narg.next;
9638 }
9639 *app = NULL;
9640 n1->nfor.args = ap;
9641 if (lasttoken != TNL && lasttoken != TSEMI)
9642 synexpect(-1);
9643 } else {
9644 static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
9645 '@', '=', '\0'};
9646 n2 = (union node *)stalloc(sizeof (struct narg));
9647 n2->type = NARG;
9648 n2->narg.text = argvars;
9649 n2->narg.backquote = NULL;
9650 n2->narg.next = NULL;
9651 n1->nfor.args = n2;
9652 /*
9653 * Newline or semicolon here is optional (but note
9654 * that the original Bourne shell only allowed NL).
9655 */
9656 if (lasttoken != TNL && lasttoken != TSEMI)
9657 tokpushback++;
9658 }
9659 checkkwd = 2;
9660 if (readtoken() != TDO)
9661 synexpect(TDO);
9662 n1->nfor.body = list(0);
9663 if (readtoken() != TDONE)
9664 synexpect(TDONE);
9665 checkkwd = 1;
9666 break;
9667 case TCASE:
9668 n1 = (union node *)stalloc(sizeof (struct ncase));
9669 n1->type = NCASE;
9670 if (readtoken() != TWORD)
9671 synexpect(TWORD);
9672 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9673 n2->type = NARG;
9674 n2->narg.text = wordtext;
9675 n2->narg.backquote = backquotelist;
9676 n2->narg.next = NULL;
9677 do {
9678 checkkwd = 1;
9679 } while (readtoken() == TNL);
9680 if (lasttoken != TIN)
9681 synerror("expecting \"in\"");
9682 cpp = &n1->ncase.cases;
9683 checkkwd = 2, readtoken();
9684 do {
9685 if (lasttoken == TLP)
9686 readtoken();
9687 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9688 cp->type = NCLIST;
9689 app = &cp->nclist.pattern;
9690 for (;;) {
9691 *app = ap = (union node *)stalloc(sizeof (struct narg));
9692 ap->type = NARG;
9693 ap->narg.text = wordtext;
9694 ap->narg.backquote = backquotelist;
9695 if (checkkwd = 2, readtoken() != TPIPE)
9696 break;
9697 app = &ap->narg.next;
9698 readtoken();
9699 }
9700 ap->narg.next = NULL;
9701 if (lasttoken != TRP)
9702 synexpect(TRP);
9703 cp->nclist.body = list(0);
9704
9705 checkkwd = 2;
9706 if ((t = readtoken()) != TESAC) {
9707 if (t != TENDCASE)
9708 synexpect(TENDCASE);
9709 else
9710 checkkwd = 2, readtoken();
9711 }
9712 cpp = &cp->nclist.next;
9713 } while(lasttoken != TESAC);
9714 *cpp = NULL;
9715 checkkwd = 1;
9716 break;
9717 case TLP:
9718 n1 = (union node *)stalloc(sizeof (struct nredir));
9719 n1->type = NSUBSHELL;
9720 n1->nredir.n = list(0);
9721 n1->nredir.redirect = NULL;
9722 if (readtoken() != TRP)
9723 synexpect(TRP);
9724 checkkwd = 1;
9725 break;
9726 case TBEGIN:
9727 n1 = list(0);
9728 if (readtoken() != TEND)
9729 synexpect(TEND);
9730 checkkwd = 1;
9731 break;
9732 /* Handle an empty command like other simple commands. */
9733 case TSEMI:
9734 case TAND:
9735 case TOR:
9736 case TNL:
9737 case TEOF:
9738 case TRP:
9739 case TBACKGND:
9740 /*
9741 * An empty command before a ; doesn't make much sense, and
9742 * should certainly be disallowed in the case of `if ;'.
9743 */
9744 if (!redir)
9745 synexpect(-1);
9746 case TWORD:
Eric Andersencb57d552001-06-28 07:25:16 +00009747 tokpushback++;
Eric Andersena3483db2001-10-24 08:01:06 +00009748 n1 = simplecmd(rpp, redir);
Eric Andersencb57d552001-06-28 07:25:16 +00009749 return n1;
9750 default:
9751 synexpect(-1);
9752 /* NOTREACHED */
9753 }
9754
9755 /* Now check for redirection which may follow command */
9756 while (readtoken() == TREDIR) {
9757 *rpp = n2 = redirnode;
9758 rpp = &n2->nfile.next;
9759 parsefname();
9760 }
9761 tokpushback++;
9762 *rpp = NULL;
9763 if (redir) {
9764 if (n1->type != NSUBSHELL) {
9765 n2 = (union node *)stalloc(sizeof (struct nredir));
9766 n2->type = NREDIR;
9767 n2->nredir.n = n1;
9768 n1 = n2;
9769 }
9770 n1->nredir.redirect = redir;
9771 }
9772
9773 return n1;
9774}
9775
9776
9777static union node *
Eric Andersena3483db2001-10-24 08:01:06 +00009778simplecmd(union node **rpp, union node *redir) {
Eric Andersencb57d552001-06-28 07:25:16 +00009779 union node *args, **app;
9780 union node *n = NULL;
9781 union node *vars, **vpp;
Eric Andersena3483db2001-10-24 08:01:06 +00009782 union node **orig_rpp;
Eric Andersencb57d552001-06-28 07:25:16 +00009783
9784 args = NULL;
9785 app = &args;
9786 vars = NULL;
9787 vpp = &vars;
Eric Andersena3483db2001-10-24 08:01:06 +00009788
9789 /* If we don't have any redirections already, then we must reset
9790 rpp to be the address of the local redir variable. */
9791 if (redir == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009792 rpp = &redir;
Eric Andersena3483db2001-10-24 08:01:06 +00009793 /* We save the incoming value, because we need this for shell
9794 functions. There can not be a redirect or an argument between
9795 the function name and the open parenthesis. */
9796 orig_rpp = rpp;
Eric Andersencb57d552001-06-28 07:25:16 +00009797
9798 checkalias = 2;
9799 for (;;) {
9800 switch (readtoken()) {
9801 case TWORD:
9802 case TASSIGN:
9803 n = (union node *)stalloc(sizeof (struct narg));
9804 n->type = NARG;
9805 n->narg.text = wordtext;
9806 n->narg.backquote = backquotelist;
9807 if (lasttoken == TWORD) {
9808 *app = n;
9809 app = &n->narg.next;
9810 } else {
9811 *vpp = n;
9812 vpp = &n->narg.next;
9813 }
9814 break;
9815 case TREDIR:
9816 *rpp = n = redirnode;
9817 rpp = &n->nfile.next;
Eric Andersen2870d962001-07-02 17:27:21 +00009818 parsefname(); /* read name of redirection file */
Eric Andersencb57d552001-06-28 07:25:16 +00009819 break;
9820 case TLP:
9821 if (
9822 args && app == &args->narg.next &&
Eric Andersena3483db2001-10-24 08:01:06 +00009823 !vars && rpp == orig_rpp
Eric Andersencb57d552001-06-28 07:25:16 +00009824 ) {
9825 /* We have a function */
9826 if (readtoken() != TRP)
9827 synexpect(TRP);
Eric Andersencb57d552001-06-28 07:25:16 +00009828 n->type = NDEFUN;
9829 checkkwd = 2;
9830 n->narg.next = command();
9831 return n;
9832 }
9833 /* fall through */
9834 default:
9835 tokpushback++;
9836 goto out;
9837 }
9838 }
9839out:
9840 *app = NULL;
9841 *vpp = NULL;
9842 *rpp = NULL;
9843 n = (union node *)stalloc(sizeof (struct ncmd));
9844 n->type = NCMD;
9845 n->ncmd.backgnd = 0;
9846 n->ncmd.args = args;
9847 n->ncmd.assign = vars;
9848 n->ncmd.redirect = redir;
9849 return n;
9850}
9851
9852static union node *
Eric Andersen2870d962001-07-02 17:27:21 +00009853makename(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00009854 union node *n;
9855
9856 n = (union node *)stalloc(sizeof (struct narg));
9857 n->type = NARG;
9858 n->narg.next = NULL;
9859 n->narg.text = wordtext;
9860 n->narg.backquote = backquotelist;
9861 return n;
9862}
9863
9864static void fixredir(union node *n, const char *text, int err)
Eric Andersen2870d962001-07-02 17:27:21 +00009865{
Eric Andersencb57d552001-06-28 07:25:16 +00009866 TRACE(("Fix redir %s %d\n", text, err));
9867 if (!err)
9868 n->ndup.vname = NULL;
9869
9870 if (is_digit(text[0]) && text[1] == '\0')
9871 n->ndup.dupfd = digit_val(text[0]);
9872 else if (text[0] == '-' && text[1] == '\0')
9873 n->ndup.dupfd = -1;
9874 else {
9875
9876 if (err)
9877 synerror("Bad fd number");
9878 else
9879 n->ndup.vname = makename();
9880 }
9881}
9882
9883
9884static void
Eric Andersen2870d962001-07-02 17:27:21 +00009885parsefname(void) {
Eric Andersencb57d552001-06-28 07:25:16 +00009886 union node *n = redirnode;
9887
9888 if (readtoken() != TWORD)
9889 synexpect(-1);
9890 if (n->type == NHERE) {
9891 struct heredoc *here = heredoc;
9892 struct heredoc *p;
9893 int i;
9894
9895 if (quoteflag == 0)
9896 n->type = NXHERE;
9897 TRACE(("Here document %d\n", n->type));
9898 if (here->striptabs) {
9899 while (*wordtext == '\t')
9900 wordtext++;
9901 }
9902 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9903 synerror("Illegal eof marker for << redirection");
9904 rmescapes(wordtext);
9905 here->eofmark = wordtext;
9906 here->next = NULL;
9907 if (heredoclist == NULL)
9908 heredoclist = here;
9909 else {
9910 for (p = heredoclist ; p->next ; p = p->next);
9911 p->next = here;
9912 }
9913 } else if (n->type == NTOFD || n->type == NFROMFD) {
9914 fixredir(n, wordtext, 0);
9915 } else {
9916 n->nfile.fname = makename();
9917 }
9918}
9919
9920
9921/*
9922 * Input any here documents.
9923 */
9924
9925static void
9926parseheredoc() {
9927 struct heredoc *here;
9928 union node *n;
9929
9930 while (heredoclist) {
9931 here = heredoclist;
9932 heredoclist = here->next;
9933 if (needprompt) {
9934 setprompt(2);
9935 needprompt = 0;
9936 }
9937 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
9938 here->eofmark, here->striptabs);
9939 n = (union node *)stalloc(sizeof (struct narg));
9940 n->narg.type = NARG;
9941 n->narg.next = NULL;
9942 n->narg.text = wordtext;
9943 n->narg.backquote = backquotelist;
9944 here->here->nhere.doc = n;
9945 }
9946}
9947
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009948static char
Eric Andersencb57d552001-06-28 07:25:16 +00009949peektoken() {
9950 int t;
9951
9952 t = readtoken();
9953 tokpushback++;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009954 return tokname_array[t][0];
Eric Andersencb57d552001-06-28 07:25:16 +00009955}
9956
9957static int
9958readtoken() {
9959 int t;
Eric Andersen7467c8d2001-07-12 20:26:32 +00009960
Eric Andersend35c5df2002-01-09 15:37:36 +00009961#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00009962 int savecheckalias = checkalias;
Eric Andersen7467c8d2001-07-12 20:26:32 +00009963 int savecheckkwd = checkkwd;
Eric Andersencb57d552001-06-28 07:25:16 +00009964 struct alias *ap;
Eric Andersen2870d962001-07-02 17:27:21 +00009965#endif
9966
Eric Andersencb57d552001-06-28 07:25:16 +00009967#ifdef DEBUG
9968 int alreadyseen = tokpushback;
9969#endif
9970
Eric Andersend35c5df2002-01-09 15:37:36 +00009971#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00009972top:
Eric Andersen2870d962001-07-02 17:27:21 +00009973#endif
9974
Eric Andersencb57d552001-06-28 07:25:16 +00009975 t = xxreadtoken();
Eric Andersen2870d962001-07-02 17:27:21 +00009976
Eric Andersend35c5df2002-01-09 15:37:36 +00009977#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00009978 checkalias = savecheckalias;
Eric Andersen2870d962001-07-02 17:27:21 +00009979#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009980
9981 if (checkkwd) {
9982 /*
9983 * eat newlines
9984 */
9985 if (checkkwd == 2) {
9986 checkkwd = 0;
9987 while (t == TNL) {
9988 parseheredoc();
9989 t = xxreadtoken();
9990 }
9991 }
9992 checkkwd = 0;
9993 /*
9994 * check for keywords
9995 */
9996 if (t == TWORD && !quoteflag)
9997 {
9998 const char *const *pp;
9999
10000 if ((pp = findkwd(wordtext))) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010001 lasttoken = t = pp - tokname_array;
10002 TRACE(("keyword %s recognized\n", tokname(t)));
Eric Andersencb57d552001-06-28 07:25:16 +000010003 goto out;
10004 }
10005 }
10006 }
10007
Eric Andersen7467c8d2001-07-12 20:26:32 +000010008
Eric Andersencb57d552001-06-28 07:25:16 +000010009 if (t != TWORD) {
10010 if (t != TREDIR) {
10011 checkalias = 0;
10012 }
10013 } else if (checkalias == 2 && isassignment(wordtext)) {
10014 lasttoken = t = TASSIGN;
Eric Andersend35c5df2002-01-09 15:37:36 +000010015#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010016 } else if (checkalias) {
Eric Andersenec074692001-10-31 11:05:49 +000010017 if (!quoteflag && (ap = *__lookupalias(wordtext)) != NULL && !(ap->flag & ALIASINUSE)) {
Eric Andersencb57d552001-06-28 07:25:16 +000010018 if (*ap->val) {
10019 pushstring(ap->val, strlen(ap->val), ap);
10020 }
10021 checkkwd = savecheckkwd;
10022 goto top;
10023 }
10024 checkalias = 0;
Eric Andersen2870d962001-07-02 17:27:21 +000010025#endif
Eric Andersen7467c8d2001-07-12 20:26:32 +000010026 }
Eric Andersencb57d552001-06-28 07:25:16 +000010027out:
10028#ifdef DEBUG
10029 if (!alreadyseen)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010030 TRACE(("token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +000010031 else
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010032 TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +000010033#endif
10034 return (t);
10035}
10036
10037
10038/*
10039 * Read the next input token.
10040 * If the token is a word, we set backquotelist to the list of cmds in
Eric Andersen2870d962001-07-02 17:27:21 +000010041 * backquotes. We set quoteflag to true if any part of the word was
10042 * quoted.
Eric Andersencb57d552001-06-28 07:25:16 +000010043 * If the token is TREDIR, then we set redirnode to a structure containing
Eric Andersen2870d962001-07-02 17:27:21 +000010044 * the redirection.
Eric Andersencb57d552001-06-28 07:25:16 +000010045 * In all cases, the variable startlinno is set to the number of the line
Eric Andersen2870d962001-07-02 17:27:21 +000010046 * on which the token starts.
Eric Andersencb57d552001-06-28 07:25:16 +000010047 *
10048 * [Change comment: here documents and internal procedures]
10049 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10050 * word parsing code into a separate routine. In this case, readtoken
10051 * doesn't need to have any internal procedures, but parseword does.
10052 * We could also make parseoperator in essence the main routine, and
10053 * have parseword (readtoken1?) handle both words and redirection.]
10054 */
10055
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000010056#define NEW_xxreadtoken
10057#ifdef NEW_xxreadtoken
10058
10059static const char xxreadtoken_chars[] = "\n()&|;"; /* singles must be first! */
10060static const char xxreadtoken_tokens[] = {
10061 TNL, TLP, TRP, /* only single occurrence allowed */
10062 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10063 TEOF, /* corresponds to trailing nul */
10064 TAND, TOR, TENDCASE, /* if double occurrence */
10065};
10066
10067#define xxreadtoken_doubles \
10068 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10069#define xxreadtoken_singles \
10070 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10071
10072static int
10073xxreadtoken() {
10074 int c;
10075
10076 if (tokpushback) {
10077 tokpushback = 0;
10078 return lasttoken;
10079 }
10080 if (needprompt) {
10081 setprompt(2);
10082 needprompt = 0;
10083 }
10084 startlinno = plinno;
10085 for (;;) { /* until token or start of word found */
10086 c = pgetc_macro();
10087
10088 if ((c!=' ') && (c!='\t')
Eric Andersend35c5df2002-01-09 15:37:36 +000010089#ifdef CONFIG_ASH_ALIAS
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000010090 && (c!=PEOA)
10091#endif
10092 ) {
10093 if (c=='#') {
10094 while ((c = pgetc()) != '\n' && c != PEOF);
10095 pungetc();
10096 } else if (c=='\\') {
10097 if (pgetc() != '\n') {
10098 pungetc();
10099 goto READTOKEN1;
10100 }
10101 startlinno = ++plinno;
10102 setprompt(doprompt ? 2 : 0);
10103 } else {
10104 const char *p
10105 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10106
10107 if (c!=PEOF) {
10108 if (c=='\n') {
10109 plinno++;
10110 needprompt = doprompt;
10111 }
10112
10113 p = strchr(xxreadtoken_chars, c);
10114 if (p == NULL) {
10115 READTOKEN1:
10116 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10117 }
10118
10119 if (p-xxreadtoken_chars >= xxreadtoken_singles) {
10120 if (pgetc() == *p) { /* double occurrence? */
10121 p += xxreadtoken_doubles + 1;
10122 } else {
10123 pungetc();
10124 }
10125 }
10126 }
10127
10128 return lasttoken = xxreadtoken_tokens[p-xxreadtoken_chars];
10129 }
10130 }
10131 }
10132}
10133
10134
10135#else
Eric Andersen2870d962001-07-02 17:27:21 +000010136#define RETURN(token) return lasttoken = token
Eric Andersencb57d552001-06-28 07:25:16 +000010137
10138static int
10139xxreadtoken() {
10140 int c;
10141
10142 if (tokpushback) {
10143 tokpushback = 0;
10144 return lasttoken;
10145 }
10146 if (needprompt) {
10147 setprompt(2);
10148 needprompt = 0;
10149 }
10150 startlinno = plinno;
Eric Andersen2870d962001-07-02 17:27:21 +000010151 for (;;) { /* until token or start of word found */
Eric Andersencb57d552001-06-28 07:25:16 +000010152 c = pgetc_macro();
10153 switch (c) {
10154 case ' ': case '\t':
Eric Andersend35c5df2002-01-09 15:37:36 +000010155#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010156 case PEOA:
Eric Andersen3102ac42001-07-06 04:26:23 +000010157#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010158 continue;
10159 case '#':
10160 while ((c = pgetc()) != '\n' && c != PEOF);
10161 pungetc();
10162 continue;
10163 case '\\':
10164 if (pgetc() == '\n') {
10165 startlinno = ++plinno;
10166 if (doprompt)
10167 setprompt(2);
10168 else
10169 setprompt(0);
10170 continue;
10171 }
10172 pungetc();
10173 goto breakloop;
10174 case '\n':
10175 plinno++;
10176 needprompt = doprompt;
10177 RETURN(TNL);
10178 case PEOF:
10179 RETURN(TEOF);
10180 case '&':
10181 if (pgetc() == '&')
10182 RETURN(TAND);
10183 pungetc();
10184 RETURN(TBACKGND);
10185 case '|':
10186 if (pgetc() == '|')
10187 RETURN(TOR);
10188 pungetc();
10189 RETURN(TPIPE);
10190 case ';':
10191 if (pgetc() == ';')
10192 RETURN(TENDCASE);
10193 pungetc();
10194 RETURN(TSEMI);
10195 case '(':
10196 RETURN(TLP);
10197 case ')':
10198 RETURN(TRP);
10199 default:
10200 goto breakloop;
10201 }
10202 }
10203breakloop:
10204 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10205#undef RETURN
10206}
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +000010207#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010208
Eric Andersencb57d552001-06-28 07:25:16 +000010209/*
10210 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10211 * is not NULL, read a here document. In the latter case, eofmark is the
10212 * word which marks the end of the document and striptabs is true if
10213 * leading tabs should be stripped from the document. The argument firstc
10214 * is the first character of the input token or document.
10215 *
10216 * Because C does not have internal subroutines, I have simulated them
10217 * using goto's to implement the subroutine linkage. The following macros
10218 * will run code that appears at the end of readtoken1.
10219 */
10220
Eric Andersen2870d962001-07-02 17:27:21 +000010221#define CHECKEND() {goto checkend; checkend_return:;}
10222#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10223#define PARSESUB() {goto parsesub; parsesub_return:;}
10224#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10225#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10226#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010227
10228static int
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010229readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
10230{
Eric Andersencb57d552001-06-28 07:25:16 +000010231 int c = firstc;
10232 char *out;
10233 int len;
10234 char line[EOFMARKLEN + 1];
10235 struct nodelist *bqlist;
10236 int quotef;
10237 int dblquote;
Eric Andersen2870d962001-07-02 17:27:21 +000010238 int varnest; /* levels of variables expansion */
10239 int arinest; /* levels of arithmetic expansion */
10240 int parenlevel; /* levels of parens in arithmetic */
10241 int dqvarnest; /* levels of variables expansion within double quotes */
Eric Andersencb57d552001-06-28 07:25:16 +000010242 int oldstyle;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010243 int prevsyntax; /* syntax before arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010244#if __GNUC__
10245 /* Avoid longjmp clobbering */
10246 (void) &out;
10247 (void) &quotef;
10248 (void) &dblquote;
10249 (void) &varnest;
10250 (void) &arinest;
10251 (void) &parenlevel;
10252 (void) &dqvarnest;
10253 (void) &oldstyle;
10254 (void) &prevsyntax;
10255 (void) &syntax;
10256#endif
10257
10258 startlinno = plinno;
10259 dblquote = 0;
10260 if (syntax == DQSYNTAX)
10261 dblquote = 1;
10262 quotef = 0;
10263 bqlist = NULL;
10264 varnest = 0;
10265 arinest = 0;
10266 parenlevel = 0;
10267 dqvarnest = 0;
10268
10269 STARTSTACKSTR(out);
Eric Andersen2870d962001-07-02 17:27:21 +000010270 loop: { /* for each line, until end of word */
10271 CHECKEND(); /* set c to PEOF if at end of here document */
10272 for (;;) { /* until end of line or end of word */
10273 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010274 switch(SIT(c,syntax)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010275 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010276 if (syntax == BASESYNTAX)
Eric Andersen2870d962001-07-02 17:27:21 +000010277 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010278 USTPUTC(c, out);
10279 plinno++;
10280 if (doprompt)
10281 setprompt(2);
10282 else
10283 setprompt(0);
10284 c = pgetc();
Eric Andersen2870d962001-07-02 17:27:21 +000010285 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010286 case CWORD:
10287 USTPUTC(c, out);
10288 break;
10289 case CCTL:
10290 if ((eofmark == NULL || dblquote) &&
10291 dqvarnest == 0)
10292 USTPUTC(CTLESC, out);
10293 USTPUTC(c, out);
10294 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010295 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010296 c = pgetc2();
10297 if (c == PEOF) {
10298 USTPUTC('\\', out);
10299 pungetc();
10300 } else if (c == '\n') {
10301 if (doprompt)
10302 setprompt(2);
10303 else
10304 setprompt(0);
10305 } else {
10306 if (dblquote && c != '\\' && c != '`' && c != '$'
10307 && (c != '"' || eofmark != NULL))
10308 USTPUTC('\\', out);
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010309 if (SIT(c,SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010310 USTPUTC(CTLESC, out);
10311 else if (eofmark == NULL)
10312 USTPUTC(CTLQUOTEMARK, out);
10313 USTPUTC(c, out);
10314 quotef++;
10315 }
10316 break;
10317 case CSQUOTE:
10318 if (eofmark == NULL)
10319 USTPUTC(CTLQUOTEMARK, out);
10320 syntax = SQSYNTAX;
10321 break;
10322 case CDQUOTE:
10323 if (eofmark == NULL)
10324 USTPUTC(CTLQUOTEMARK, out);
10325 syntax = DQSYNTAX;
10326 dblquote = 1;
10327 break;
10328 case CENDQUOTE:
10329 if (eofmark != NULL && arinest == 0 &&
10330 varnest == 0) {
10331 USTPUTC(c, out);
10332 } else {
10333 if (arinest) {
10334 syntax = ARISYNTAX;
10335 dblquote = 0;
10336 } else if (eofmark == NULL &&
10337 dqvarnest == 0) {
10338 syntax = BASESYNTAX;
10339 dblquote = 0;
10340 }
10341 quotef++;
10342 }
10343 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010344 case CVAR: /* '$' */
10345 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010346 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010347 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010348 if (varnest > 0) {
10349 varnest--;
10350 if (dqvarnest > 0) {
10351 dqvarnest--;
10352 }
10353 USTPUTC(CTLENDVAR, out);
10354 } else {
10355 USTPUTC(c, out);
10356 }
10357 break;
Eric Andersend35c5df2002-01-09 15:37:36 +000010358#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +000010359 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010360 parenlevel++;
10361 USTPUTC(c, out);
10362 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010363 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010364 if (parenlevel > 0) {
10365 USTPUTC(c, out);
10366 --parenlevel;
10367 } else {
10368 if (pgetc() == ')') {
10369 if (--arinest == 0) {
10370 USTPUTC(CTLENDARI, out);
10371 syntax = prevsyntax;
10372 if (syntax == DQSYNTAX)
10373 dblquote = 1;
10374 else
10375 dblquote = 0;
10376 } else
10377 USTPUTC(')', out);
10378 } else {
10379 /*
10380 * unbalanced parens
10381 * (don't 2nd guess - no error)
10382 */
10383 pungetc();
10384 USTPUTC(')', out);
10385 }
10386 }
10387 break;
10388#endif
Eric Andersen2870d962001-07-02 17:27:21 +000010389 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010390 PARSEBACKQOLD();
10391 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010392 case CENDFILE:
10393 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010394 case CIGN:
10395 break;
10396 default:
10397 if (varnest == 0)
Eric Andersen2870d962001-07-02 17:27:21 +000010398 goto endword; /* exit outer loop */
Eric Andersend35c5df2002-01-09 15:37:36 +000010399#ifdef CONFIG_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010400 if (c != PEOA)
10401#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010402 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010403
Eric Andersencb57d552001-06-28 07:25:16 +000010404 }
10405 c = pgetc_macro();
10406 }
10407 }
10408endword:
10409 if (syntax == ARISYNTAX)
10410 synerror("Missing '))'");
10411 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10412 synerror("Unterminated quoted string");
10413 if (varnest != 0) {
10414 startlinno = plinno;
10415 synerror("Missing '}'");
10416 }
10417 USTPUTC('\0', out);
10418 len = out - stackblock();
10419 out = stackblock();
10420 if (eofmark == NULL) {
10421 if ((c == '>' || c == '<')
10422 && quotef == 0
10423 && len <= 2
10424 && (*out == '\0' || is_digit(*out))) {
10425 PARSEREDIR();
10426 return lasttoken = TREDIR;
10427 } else {
10428 pungetc();
10429 }
10430 }
10431 quoteflag = quotef;
10432 backquotelist = bqlist;
10433 grabstackblock(len);
10434 wordtext = out;
10435 return lasttoken = TWORD;
10436/* end of readtoken routine */
10437
10438
10439
10440/*
10441 * Check to see whether we are at the end of the here document. When this
10442 * is called, c is set to the first character of the next input line. If
10443 * we are at the end of the here document, this routine sets the c to PEOF.
10444 */
10445
10446checkend: {
10447 if (eofmark) {
Eric Andersend35c5df2002-01-09 15:37:36 +000010448#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010449 if (c == PEOA) {
10450 c = pgetc2();
10451 }
Eric Andersen2870d962001-07-02 17:27:21 +000010452#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010453 if (striptabs) {
10454 while (c == '\t') {
10455 c = pgetc2();
10456 }
10457 }
10458 if (c == *eofmark) {
10459 if (pfgets(line, sizeof line) != NULL) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010460 const char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010461
10462 p = line;
10463 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10464 if (*p == '\n' && *q == '\0') {
10465 c = PEOF;
10466 plinno++;
10467 needprompt = doprompt;
10468 } else {
10469 pushstring(line, strlen(line), NULL);
10470 }
10471 }
10472 }
10473 }
10474 goto checkend_return;
10475}
10476
10477
10478/*
10479 * Parse a redirection operator. The variable "out" points to a string
10480 * specifying the fd to be redirected. The variable "c" contains the
10481 * first character of the redirection operator.
10482 */
10483
10484parseredir: {
10485 char fd = *out;
10486 union node *np;
10487
10488 np = (union node *)stalloc(sizeof (struct nfile));
10489 if (c == '>') {
10490 np->nfile.fd = 1;
10491 c = pgetc();
10492 if (c == '>')
10493 np->type = NAPPEND;
10494 else if (c == '&')
10495 np->type = NTOFD;
10496 else if (c == '|')
10497 np->type = NTOOV;
10498 else {
10499 np->type = NTO;
10500 pungetc();
10501 }
Eric Andersen2870d962001-07-02 17:27:21 +000010502 } else { /* c == '<' */
Eric Andersencb57d552001-06-28 07:25:16 +000010503 np->nfile.fd = 0;
10504 switch (c = pgetc()) {
10505 case '<':
10506 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10507 np = (union node *)stalloc(sizeof (struct nhere));
10508 np->nfile.fd = 0;
10509 }
10510 np->type = NHERE;
10511 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10512 heredoc->here = np;
10513 if ((c = pgetc()) == '-') {
10514 heredoc->striptabs = 1;
10515 } else {
10516 heredoc->striptabs = 0;
10517 pungetc();
10518 }
10519 break;
10520
10521 case '&':
10522 np->type = NFROMFD;
10523 break;
10524
10525 case '>':
10526 np->type = NFROMTO;
10527 break;
10528
10529 default:
10530 np->type = NFROM;
10531 pungetc();
10532 break;
10533 }
10534 }
10535 if (fd != '\0')
10536 np->nfile.fd = digit_val(fd);
10537 redirnode = np;
10538 goto parseredir_return;
10539}
10540
10541
10542/*
10543 * Parse a substitution. At this point, we have read the dollar sign
10544 * and nothing else.
10545 */
10546
10547parsesub: {
10548 int subtype;
10549 int typeloc;
10550 int flags;
10551 char *p;
10552 static const char types[] = "}-+?=";
10553
10554 c = pgetc();
10555 if (
10556 c <= PEOA ||
10557 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10558 ) {
10559 USTPUTC('$', out);
10560 pungetc();
Eric Andersen2870d962001-07-02 17:27:21 +000010561 } else if (c == '(') { /* $(command) or $((arith)) */
Eric Andersencb57d552001-06-28 07:25:16 +000010562 if (pgetc() == '(') {
10563 PARSEARITH();
10564 } else {
10565 pungetc();
10566 PARSEBACKQNEW();
10567 }
10568 } else {
10569 USTPUTC(CTLVAR, out);
10570 typeloc = out - stackblock();
10571 USTPUTC(VSNORMAL, out);
10572 subtype = VSNORMAL;
10573 if (c == '{') {
10574 c = pgetc();
10575 if (c == '#') {
10576 if ((c = pgetc()) == '}')
10577 c = '#';
10578 else
10579 subtype = VSLENGTH;
10580 }
10581 else
10582 subtype = 0;
10583 }
10584 if (c > PEOA && is_name(c)) {
10585 do {
10586 STPUTC(c, out);
10587 c = pgetc();
10588 } while (c > PEOA && is_in_name(c));
10589 } else if (is_digit(c)) {
10590 do {
10591 USTPUTC(c, out);
10592 c = pgetc();
10593 } while (is_digit(c));
10594 }
10595 else if (is_special(c)) {
10596 USTPUTC(c, out);
10597 c = pgetc();
10598 }
10599 else
Eric Andersen2870d962001-07-02 17:27:21 +000010600badsub: synerror("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010601
10602 STPUTC('=', out);
10603 flags = 0;
10604 if (subtype == 0) {
10605 switch (c) {
10606 case ':':
10607 flags = VSNUL;
10608 c = pgetc();
10609 /*FALLTHROUGH*/
10610 default:
10611 p = strchr(types, c);
10612 if (p == NULL)
10613 goto badsub;
10614 subtype = p - types + VSNORMAL;
10615 break;
10616 case '%':
10617 case '#':
10618 {
10619 int cc = c;
10620 subtype = c == '#' ? VSTRIMLEFT :
10621 VSTRIMRIGHT;
10622 c = pgetc();
10623 if (c == cc)
10624 subtype++;
10625 else
10626 pungetc();
10627 break;
10628 }
10629 }
10630 } else {
10631 pungetc();
10632 }
10633 if (dblquote || arinest)
10634 flags |= VSQUOTE;
10635 *(stackblock() + typeloc) = subtype | flags;
10636 if (subtype != VSNORMAL) {
10637 varnest++;
10638 if (dblquote) {
10639 dqvarnest++;
10640 }
10641 }
10642 }
10643 goto parsesub_return;
10644}
10645
10646
10647/*
10648 * Called to parse command substitutions. Newstyle is set if the command
10649 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10650 * list of commands (passed by reference), and savelen is the number of
10651 * characters on the top of the stack which must be preserved.
10652 */
10653
10654parsebackq: {
10655 struct nodelist **nlpp;
10656 int savepbq;
10657 union node *n;
10658 char *volatile str;
10659 struct jmploc jmploc;
10660 struct jmploc *volatile savehandler;
10661 int savelen;
10662 int saveprompt;
10663#ifdef __GNUC__
10664 (void) &saveprompt;
10665#endif
10666
10667 savepbq = parsebackquote;
10668 if (setjmp(jmploc.loc)) {
10669 if (str)
10670 ckfree(str);
10671 parsebackquote = 0;
10672 handler = savehandler;
10673 longjmp(handler->loc, 1);
10674 }
10675 INTOFF;
10676 str = NULL;
10677 savelen = out - stackblock();
10678 if (savelen > 0) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000010679 str = xmalloc(savelen);
Eric Andersencb57d552001-06-28 07:25:16 +000010680 memcpy(str, stackblock(), savelen);
10681 }
10682 savehandler = handler;
10683 handler = &jmploc;
10684 INTON;
Eric Andersen2870d962001-07-02 17:27:21 +000010685 if (oldstyle) {
10686 /* We must read until the closing backquote, giving special
10687 treatment to some slashes, and then push the string and
10688 reread it as input, interpreting it normally. */
10689 char *pout;
10690 int pc;
10691 int psavelen;
10692 char *pstr;
Eric Andersencb57d552001-06-28 07:25:16 +000010693
10694
Eric Andersen2870d962001-07-02 17:27:21 +000010695 STARTSTACKSTR(pout);
Eric Andersencb57d552001-06-28 07:25:16 +000010696 for (;;) {
10697 if (needprompt) {
10698 setprompt(2);
10699 needprompt = 0;
10700 }
10701 switch (pc = pgetc()) {
10702 case '`':
10703 goto done;
10704
10705 case '\\':
Eric Andersen2870d962001-07-02 17:27:21 +000010706 if ((pc = pgetc()) == '\n') {
Eric Andersencb57d552001-06-28 07:25:16 +000010707 plinno++;
10708 if (doprompt)
10709 setprompt(2);
10710 else
10711 setprompt(0);
10712 /*
10713 * If eating a newline, avoid putting
10714 * the newline into the new character
10715 * stream (via the STPUTC after the
10716 * switch).
10717 */
10718 continue;
10719 }
Eric Andersen2870d962001-07-02 17:27:21 +000010720 if (pc != '\\' && pc != '`' && pc != '$'
10721 && (!dblquote || pc != '"'))
10722 STPUTC('\\', pout);
Eric Andersencb57d552001-06-28 07:25:16 +000010723 if (pc > PEOA) {
10724 break;
10725 }
10726 /* fall through */
10727
10728 case PEOF:
Eric Andersend35c5df2002-01-09 15:37:36 +000010729#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +000010730 case PEOA:
Eric Andersen2870d962001-07-02 17:27:21 +000010731#endif
10732 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010733 synerror("EOF in backquote substitution");
10734
10735 case '\n':
10736 plinno++;
10737 needprompt = doprompt;
10738 break;
10739
10740 default:
10741 break;
10742 }
10743 STPUTC(pc, pout);
Eric Andersen2870d962001-07-02 17:27:21 +000010744 }
Eric Andersencb57d552001-06-28 07:25:16 +000010745done:
Eric Andersen2870d962001-07-02 17:27:21 +000010746 STPUTC('\0', pout);
10747 psavelen = pout - stackblock();
10748 if (psavelen > 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010749 pstr = grabstackstr(pout);
10750 setinputstring(pstr);
Eric Andersen2870d962001-07-02 17:27:21 +000010751 }
10752 }
Eric Andersencb57d552001-06-28 07:25:16 +000010753 nlpp = &bqlist;
10754 while (*nlpp)
10755 nlpp = &(*nlpp)->next;
10756 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
10757 (*nlpp)->next = NULL;
10758 parsebackquote = oldstyle;
10759
10760 if (oldstyle) {
10761 saveprompt = doprompt;
10762 doprompt = 0;
10763 }
10764
10765 n = list(0);
10766
10767 if (oldstyle)
10768 doprompt = saveprompt;
10769 else {
10770 if (readtoken() != TRP)
10771 synexpect(TRP);
10772 }
10773
10774 (*nlpp)->n = n;
Eric Andersen2870d962001-07-02 17:27:21 +000010775 if (oldstyle) {
Eric Andersencb57d552001-06-28 07:25:16 +000010776 /*
10777 * Start reading from old file again, ignoring any pushed back
10778 * tokens left from the backquote parsing
10779 */
Eric Andersen2870d962001-07-02 17:27:21 +000010780 popfile();
Eric Andersencb57d552001-06-28 07:25:16 +000010781 tokpushback = 0;
10782 }
10783 while (stackblocksize() <= savelen)
10784 growstackblock();
10785 STARTSTACKSTR(out);
10786 if (str) {
10787 memcpy(out, str, savelen);
10788 STADJUST(savelen, out);
10789 INTOFF;
10790 ckfree(str);
10791 str = NULL;
10792 INTON;
10793 }
10794 parsebackquote = savepbq;
10795 handler = savehandler;
10796 if (arinest || dblquote)
10797 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10798 else
10799 USTPUTC(CTLBACKQ, out);
10800 if (oldstyle)
10801 goto parsebackq_oldreturn;
10802 else
10803 goto parsebackq_newreturn;
10804}
10805
10806/*
10807 * Parse an arithmetic expansion (indicate start of one and set state)
10808 */
10809parsearith: {
10810
10811 if (++arinest == 1) {
10812 prevsyntax = syntax;
10813 syntax = ARISYNTAX;
10814 USTPUTC(CTLARI, out);
10815 if (dblquote)
10816 USTPUTC('"',out);
10817 else
10818 USTPUTC(' ',out);
10819 } else {
10820 /*
10821 * we collapse embedded arithmetic expansion to
10822 * parenthesis, which should be equivalent
10823 */
10824 USTPUTC('(', out);
10825 }
10826 goto parsearith_return;
10827}
10828
10829} /* end of readtoken */
10830
10831
Eric Andersencb57d552001-06-28 07:25:16 +000010832/*
10833 * Returns true if the text contains nothing to expand (no dollar signs
10834 * or backquotes).
10835 */
10836
10837static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000010838noexpand(char *text)
10839{
Eric Andersencb57d552001-06-28 07:25:16 +000010840 char *p;
10841 char c;
10842
10843 p = text;
10844 while ((c = *p++) != '\0') {
10845 if (c == CTLQUOTEMARK)
10846 continue;
10847 if (c == CTLESC)
10848 p++;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010849 else if (SIT(c,BASESYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010850 return 0;
10851 }
10852 return 1;
10853}
10854
10855
10856/*
10857 * Return true if the argument is a legal variable name (a letter or
10858 * underscore followed by zero or more letters, underscores, and digits).
10859 */
10860
10861static int
Eric Andersen2870d962001-07-02 17:27:21 +000010862goodname(const char *name)
10863{
10864 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +000010865
10866 p = name;
10867 if (! is_name(*p))
10868 return 0;
10869 while (*++p) {
10870 if (! is_in_name(*p))
10871 return 0;
10872 }
10873 return 1;
10874}
10875
10876
10877/*
10878 * Called when an unexpected token is read during the parse. The argument
10879 * is the token that is expected, or -1 if more than one type of token can
10880 * occur at this point.
10881 */
10882
10883static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000010884synexpect(int token)
Eric Andersencb57d552001-06-28 07:25:16 +000010885{
10886 char msg[64];
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010887 int l;
Eric Andersencb57d552001-06-28 07:25:16 +000010888
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010889 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10890 if (token >= 0)
10891 sprintf(msg+l, " (expecting %s)", tokname(token));
Eric Andersencb57d552001-06-28 07:25:16 +000010892 synerror(msg);
10893 /* NOTREACHED */
10894}
10895
10896
10897static void
Eric Andersen2870d962001-07-02 17:27:21 +000010898synerror(const char *msg)
10899{
Eric Andersencb57d552001-06-28 07:25:16 +000010900 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +000010901 out2fmt("%s: %d: ", commandname, startlinno);
10902 out2fmt("Syntax error: %s\n", msg);
Eric Andersencb57d552001-06-28 07:25:16 +000010903 error((char *)NULL);
10904 /* NOTREACHED */
10905}
10906
Eric Andersencb57d552001-06-28 07:25:16 +000010907
10908/*
10909 * called by editline -- any expansions to the prompt
10910 * should be added here.
10911 */
Eric Andersen2870d962001-07-02 17:27:21 +000010912static void
Eric Andersen62483552001-07-10 06:09:16 +000010913setprompt(int whichprompt)
Eric Andersen2870d962001-07-02 17:27:21 +000010914{
Eric Andersen62483552001-07-10 06:09:16 +000010915 char *prompt;
10916 switch (whichprompt) {
10917 case 1:
10918 prompt = ps1val();
10919 break;
10920 case 2:
10921 prompt = ps2val();
10922 break;
10923 default: /* 0 */
10924 prompt = "";
10925 }
10926 putprompt(prompt);
Eric Andersencb57d552001-06-28 07:25:16 +000010927}
10928
Eric Andersencb57d552001-06-28 07:25:16 +000010929
Eric Andersencb57d552001-06-28 07:25:16 +000010930/*
10931 * Code for dealing with input/output redirection.
10932 */
10933
Eric Andersen2870d962001-07-02 17:27:21 +000010934#define EMPTY -2 /* marks an unused slot in redirtab */
Eric Andersencb57d552001-06-28 07:25:16 +000010935#ifndef PIPE_BUF
Eric Andersen2870d962001-07-02 17:27:21 +000010936# define PIPESIZE 4096 /* amount of buffering in a pipe */
Eric Andersencb57d552001-06-28 07:25:16 +000010937#else
10938# define PIPESIZE PIPE_BUF
10939#endif
10940
10941
Eric Andersen62483552001-07-10 06:09:16 +000010942/*
10943 * Open a file in noclobber mode.
10944 * The code was copied from bash.
10945 */
10946static inline int
10947noclobberopen(const char *fname)
10948{
10949 int r, fd;
10950 struct stat finfo, finfo2;
10951
10952 /*
10953 * If the file exists and is a regular file, return an error
10954 * immediately.
10955 */
10956 r = stat(fname, &finfo);
10957 if (r == 0 && S_ISREG(finfo.st_mode)) {
10958 errno = EEXIST;
10959 return -1;
10960 }
10961
10962 /*
10963 * If the file was not present (r != 0), make sure we open it
10964 * exclusively so that if it is created before we open it, our open
10965 * will fail. Make sure that we do not truncate an existing file.
10966 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10967 * file was not a regular file, we leave O_EXCL off.
10968 */
10969 if (r != 0)
10970 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10971 fd = open(fname, O_WRONLY|O_CREAT, 0666);
10972
10973 /* If the open failed, return the file descriptor right away. */
10974 if (fd < 0)
10975 return fd;
10976
10977 /*
10978 * OK, the open succeeded, but the file may have been changed from a
10979 * non-regular file to a regular file between the stat and the open.
10980 * We are assuming that the O_EXCL open handles the case where FILENAME
10981 * did not exist and is symlinked to an existing file between the stat
10982 * and open.
10983 */
10984
10985 /*
10986 * If we can open it and fstat the file descriptor, and neither check
10987 * revealed that it was a regular file, and the file has not been
10988 * replaced, return the file descriptor.
10989 */
10990 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
10991 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
10992 return fd;
10993
10994 /* The file has been replaced. badness. */
10995 close(fd);
10996 errno = EEXIST;
10997 return -1;
10998}
Eric Andersencb57d552001-06-28 07:25:16 +000010999
11000/*
Eric Andersen62483552001-07-10 06:09:16 +000011001 * Handle here documents. Normally we fork off a process to write the
11002 * data to a pipe. If the document is short, we can stuff the data in
11003 * the pipe without forking.
Eric Andersencb57d552001-06-28 07:25:16 +000011004 */
11005
Eric Andersen62483552001-07-10 06:09:16 +000011006static inline int
11007openhere(const union node *redir)
11008{
11009 int pip[2];
11010 int len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011011
Eric Andersen62483552001-07-10 06:09:16 +000011012 if (pipe(pip) < 0)
11013 error("Pipe call failed");
11014 if (redir->type == NHERE) {
11015 len = strlen(redir->nhere.doc->narg.text);
11016 if (len <= PIPESIZE) {
11017 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11018 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011019 }
Eric Andersencb57d552001-06-28 07:25:16 +000011020 }
Eric Andersen62483552001-07-10 06:09:16 +000011021 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
11022 close(pip[0]);
11023 signal(SIGINT, SIG_IGN);
11024 signal(SIGQUIT, SIG_IGN);
11025 signal(SIGHUP, SIG_IGN);
11026#ifdef SIGTSTP
11027 signal(SIGTSTP, SIG_IGN);
11028#endif
11029 signal(SIGPIPE, SIG_DFL);
11030 if (redir->type == NHERE)
11031 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11032 else
11033 expandhere(redir->nhere.doc, pip[1]);
11034 _exit(0);
11035 }
11036out:
11037 close(pip[1]);
11038 return pip[0];
Eric Andersencb57d552001-06-28 07:25:16 +000011039}
11040
11041
Eric Andersen62483552001-07-10 06:09:16 +000011042static inline int
11043openredirect(const union node *redir)
11044{
Eric Andersencb57d552001-06-28 07:25:16 +000011045 char *fname;
11046 int f;
11047
11048 switch (redir->nfile.type) {
11049 case NFROM:
11050 fname = redir->nfile.expfname;
11051 if ((f = open(fname, O_RDONLY)) < 0)
11052 goto eopen;
11053 break;
11054 case NFROMTO:
11055 fname = redir->nfile.expfname;
11056 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
11057 goto ecreate;
11058 break;
11059 case NTO:
11060 /* Take care of noclobber mode. */
11061 if (Cflag) {
11062 fname = redir->nfile.expfname;
11063 if ((f = noclobberopen(fname)) < 0)
11064 goto ecreate;
11065 break;
11066 }
11067 case NTOOV:
11068 fname = redir->nfile.expfname;
11069#ifdef O_CREAT
11070 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
11071 goto ecreate;
11072#else
11073 if ((f = creat(fname, 0666)) < 0)
11074 goto ecreate;
11075#endif
11076 break;
11077 case NAPPEND:
11078 fname = redir->nfile.expfname;
11079#ifdef O_APPEND
11080 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
11081 goto ecreate;
11082#else
11083 if ((f = open(fname, O_WRONLY)) < 0
11084 && (f = creat(fname, 0666)) < 0)
11085 goto ecreate;
11086 lseek(f, (off_t)0, 2);
11087#endif
11088 break;
11089 default:
11090#ifdef DEBUG
11091 abort();
11092#endif
11093 /* Fall through to eliminate warning. */
11094 case NTOFD:
11095 case NFROMFD:
11096 f = -1;
11097 break;
11098 case NHERE:
11099 case NXHERE:
11100 f = openhere(redir);
11101 break;
11102 }
11103
11104 return f;
11105ecreate:
11106 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11107eopen:
11108 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11109}
11110
11111
Eric Andersen62483552001-07-10 06:09:16 +000011112/*
11113 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11114 * old file descriptors are stashed away so that the redirection can be
11115 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11116 * standard output, and the standard error if it becomes a duplicate of
11117 * stdout.
11118 */
11119
Eric Andersencb57d552001-06-28 07:25:16 +000011120static void
Eric Andersen62483552001-07-10 06:09:16 +000011121redirect(union node *redir, int flags)
11122{
11123 union node *n;
11124 struct redirtab *sv = NULL;
11125 int i;
11126 int fd;
11127 int newfd;
11128 int try;
11129 int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
11130
11131 if (flags & REDIR_PUSH) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011132 sv = xmalloc(sizeof (struct redirtab));
Eric Andersen62483552001-07-10 06:09:16 +000011133 for (i = 0 ; i < 10 ; i++)
11134 sv->renamed[i] = EMPTY;
11135 sv->next = redirlist;
11136 redirlist = sv;
11137 }
11138 for (n = redir ; n ; n = n->nfile.next) {
11139 fd = n->nfile.fd;
11140 try = 0;
11141 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
11142 n->ndup.dupfd == fd)
11143 continue; /* redirect from/to same file descriptor */
11144
11145 INTOFF;
11146 newfd = openredirect(n);
11147 if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
11148 if (newfd == fd) {
11149 try++;
11150 } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
11151 switch (errno) {
11152 case EBADF:
11153 if (!try) {
11154 dupredirect(n, newfd, fd1dup);
11155 try++;
11156 break;
11157 }
11158 /* FALLTHROUGH*/
11159 default:
11160 if (newfd >= 0) {
11161 close(newfd);
11162 }
11163 INTON;
11164 error("%d: %m", fd);
11165 /* NOTREACHED */
11166 }
11167 }
11168 if (!try) {
11169 close(fd);
11170 if (flags & REDIR_PUSH) {
11171 sv->renamed[fd] = i;
11172 }
11173 }
11174 } else if (fd != newfd) {
11175 close(fd);
11176 }
11177 if (fd == 0)
11178 fd0_redirected++;
11179 if (!try)
11180 dupredirect(n, newfd, fd1dup);
11181 INTON;
11182 }
11183}
11184
11185
11186static void
11187dupredirect(const union node *redir, int f, int fd1dup)
Eric Andersen2870d962001-07-02 17:27:21 +000011188{
Eric Andersencb57d552001-06-28 07:25:16 +000011189 int fd = redir->nfile.fd;
11190
Eric Andersen62483552001-07-10 06:09:16 +000011191 if(fd==1)
11192 fd1dup = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011193 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Eric Andersen2870d962001-07-02 17:27:21 +000011194 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
Eric Andersen62483552001-07-10 06:09:16 +000011195 if (redir->ndup.dupfd!=1 || fd1dup!=1)
Eric Andersencb57d552001-06-28 07:25:16 +000011196 dup_as_newfd(redir->ndup.dupfd, fd);
11197 }
11198 return;
11199 }
11200
11201 if (f != fd) {
11202 dup_as_newfd(f, fd);
11203 close(f);
11204 }
11205 return;
11206}
11207
11208
Eric Andersencb57d552001-06-28 07:25:16 +000011209
Eric Andersencb57d552001-06-28 07:25:16 +000011210/*
11211 * Undo the effects of the last redirection.
11212 */
11213
11214static void
Eric Andersen2870d962001-07-02 17:27:21 +000011215popredir(void)
11216{
Eric Andersencb57d552001-06-28 07:25:16 +000011217 struct redirtab *rp = redirlist;
11218 int i;
11219
11220 INTOFF;
11221 for (i = 0 ; i < 10 ; i++) {
11222 if (rp->renamed[i] != EMPTY) {
Eric Andersen2870d962001-07-02 17:27:21 +000011223 if (i == 0)
11224 fd0_redirected--;
Eric Andersencb57d552001-06-28 07:25:16 +000011225 close(i);
11226 if (rp->renamed[i] >= 0) {
11227 dup_as_newfd(rp->renamed[i], i);
11228 close(rp->renamed[i]);
11229 }
Eric Andersencb57d552001-06-28 07:25:16 +000011230 }
11231 }
11232 redirlist = rp->next;
11233 ckfree(rp);
11234 INTON;
11235}
11236
11237/*
Eric Andersencb57d552001-06-28 07:25:16 +000011238 * Discard all saved file descriptors.
11239 */
11240
11241static void
Eric Andersen2870d962001-07-02 17:27:21 +000011242clearredir(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000011243 struct redirtab *rp;
11244 int i;
11245
11246 for (rp = redirlist ; rp ; rp = rp->next) {
11247 for (i = 0 ; i < 10 ; i++) {
11248 if (rp->renamed[i] >= 0) {
11249 close(rp->renamed[i]);
Eric Andersencb57d552001-06-28 07:25:16 +000011250 }
11251 rp->renamed[i] = EMPTY;
11252 }
11253 }
Eric Andersencb57d552001-06-28 07:25:16 +000011254}
11255
11256
Eric Andersencb57d552001-06-28 07:25:16 +000011257/*
11258 * Copy a file descriptor to be >= to. Returns -1
11259 * if the source file descriptor is closed, EMPTY if there are no unused
11260 * file descriptors left.
11261 */
11262
11263static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011264dup_as_newfd(int from, int to)
Eric Andersencb57d552001-06-28 07:25:16 +000011265{
11266 int newfd;
11267
11268 newfd = fcntl(from, F_DUPFD, to);
11269 if (newfd < 0) {
11270 if (errno == EMFILE)
11271 return EMPTY;
11272 else
Eric Andersen2870d962001-07-02 17:27:21 +000011273 error("%d: %m", from);
Eric Andersencb57d552001-06-28 07:25:16 +000011274 }
11275 return newfd;
11276}
11277
Eric Andersencb57d552001-06-28 07:25:16 +000011278#ifdef DEBUG
Eric Andersenec074692001-10-31 11:05:49 +000011279/*
11280 * Debugging stuff.
11281 */
Eric Andersen2870d962001-07-02 17:27:21 +000011282static void shtree (union node *, int, char *, FILE*);
11283static void shcmd (union node *, FILE *);
11284static void sharg (union node *, FILE *);
11285static void indent (int, char *, FILE *);
11286static void trstring (char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011287
11288
11289static void
11290showtree(n)
Eric Andersen69a20f02001-10-31 10:40:37 +000011291 unode *n;
Eric Andersencb57d552001-06-28 07:25:16 +000011292{
11293 trputs("showtree called\n");
11294 shtree(n, 1, NULL, stdout);
11295}
11296
11297
11298static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011299shtree(union node *n, int ind, char *pfx, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011300{
11301 struct nodelist *lp;
11302 const char *s;
11303
11304 if (n == NULL)
11305 return;
11306
11307 indent(ind, pfx, fp);
11308 switch(n->type) {
11309 case NSEMI:
11310 s = "; ";
11311 goto binop;
11312 case NAND:
11313 s = " && ";
11314 goto binop;
11315 case NOR:
11316 s = " || ";
11317binop:
11318 shtree(n->nbinary.ch1, ind, NULL, fp);
11319 /* if (ind < 0) */
11320 fputs(s, fp);
11321 shtree(n->nbinary.ch2, ind, NULL, fp);
11322 break;
11323 case NCMD:
11324 shcmd(n, fp);
11325 if (ind >= 0)
11326 putc('\n', fp);
11327 break;
11328 case NPIPE:
11329 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11330 shcmd(lp->n, fp);
11331 if (lp->next)
11332 fputs(" | ", fp);
11333 }
11334 if (n->npipe.backgnd)
11335 fputs(" &", fp);
11336 if (ind >= 0)
11337 putc('\n', fp);
11338 break;
11339 default:
11340 fprintf(fp, "<node type %d>", n->type);
11341 if (ind >= 0)
11342 putc('\n', fp);
11343 break;
11344 }
11345}
11346
11347
11348
11349static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011350shcmd(union node *cmd, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011351{
11352 union node *np;
11353 int first;
11354 const char *s;
11355 int dftfd;
11356
11357 first = 1;
11358 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11359 if (! first)
11360 putchar(' ');
11361 sharg(np, fp);
11362 first = 0;
11363 }
11364 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11365 if (! first)
11366 putchar(' ');
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011367#if 1
11368 s = "*error*";
11369 dftfd = 0;
11370 if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) {
11371 s = redir_strings[np->nfile.type - NTO];
11372 if (*s == '>') {
11373 dftfd = 1;
11374 }
11375 }
11376#else
Eric Andersencb57d552001-06-28 07:25:16 +000011377 switch (np->nfile.type) {
Eric Andersen2870d962001-07-02 17:27:21 +000011378 case NTO: s = ">"; dftfd = 1; break;
11379 case NAPPEND: s = ">>"; dftfd = 1; break;
11380 case NTOFD: s = ">&"; dftfd = 1; break;
11381 case NTOOV: s = ">|"; dftfd = 1; break;
11382 case NFROM: s = "<"; dftfd = 0; break;
11383 case NFROMFD: s = "<&"; dftfd = 0; break;
11384 case NFROMTO: s = "<>"; dftfd = 0; break;
11385 default: s = "*error*"; dftfd = 0; break;
Eric Andersencb57d552001-06-28 07:25:16 +000011386 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011387#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011388 if (np->nfile.fd != dftfd)
11389 fprintf(fp, "%d", np->nfile.fd);
11390 fputs(s, fp);
11391 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11392 fprintf(fp, "%d", np->ndup.dupfd);
11393 } else {
11394 sharg(np->nfile.fname, fp);
11395 }
11396 first = 0;
11397 }
11398}
11399
Eric Andersencb57d552001-06-28 07:25:16 +000011400static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011401sharg(union node *arg, FILE *fp)
11402{
Eric Andersencb57d552001-06-28 07:25:16 +000011403 char *p;
11404 struct nodelist *bqlist;
11405 int subtype;
11406
11407 if (arg->type != NARG) {
11408 printf("<node type %d>\n", arg->type);
11409 fflush(stdout);
11410 abort();
11411 }
11412 bqlist = arg->narg.backquote;
11413 for (p = arg->narg.text ; *p ; p++) {
11414 switch (*p) {
11415 case CTLESC:
11416 putc(*++p, fp);
11417 break;
11418 case CTLVAR:
11419 putc('$', fp);
11420 putc('{', fp);
11421 subtype = *++p;
11422 if (subtype == VSLENGTH)
11423 putc('#', fp);
11424
11425 while (*p != '=')
11426 putc(*p++, fp);
11427
11428 if (subtype & VSNUL)
11429 putc(':', fp);
11430
11431 switch (subtype & VSTYPE) {
11432 case VSNORMAL:
11433 putc('}', fp);
11434 break;
11435 case VSMINUS:
11436 putc('-', fp);
11437 break;
11438 case VSPLUS:
11439 putc('+', fp);
11440 break;
11441 case VSQUESTION:
11442 putc('?', fp);
11443 break;
11444 case VSASSIGN:
11445 putc('=', fp);
11446 break;
11447 case VSTRIMLEFT:
11448 putc('#', fp);
11449 break;
11450 case VSTRIMLEFTMAX:
11451 putc('#', fp);
11452 putc('#', fp);
11453 break;
11454 case VSTRIMRIGHT:
11455 putc('%', fp);
11456 break;
11457 case VSTRIMRIGHTMAX:
11458 putc('%', fp);
11459 putc('%', fp);
11460 break;
11461 case VSLENGTH:
11462 break;
11463 default:
11464 printf("<subtype %d>", subtype);
11465 }
11466 break;
11467 case CTLENDVAR:
11468 putc('}', fp);
11469 break;
11470 case CTLBACKQ:
11471 case CTLBACKQ|CTLQUOTE:
11472 putc('$', fp);
11473 putc('(', fp);
11474 shtree(bqlist->n, -1, NULL, fp);
11475 putc(')', fp);
11476 break;
11477 default:
11478 putc(*p, fp);
11479 break;
11480 }
11481 }
11482}
11483
11484
11485static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011486indent(int amount, char *pfx, FILE *fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011487{
11488 int i;
11489
11490 for (i = 0 ; i < amount ; i++) {
11491 if (pfx && i == amount - 1)
11492 fputs(pfx, fp);
11493 putc('\t', fp);
11494 }
11495}
Eric Andersencb57d552001-06-28 07:25:16 +000011496
11497
Eric Andersencb57d552001-06-28 07:25:16 +000011498FILE *tracefile;
11499
11500#if DEBUG == 2
11501static int debug = 1;
11502#else
11503static int debug = 0;
11504#endif
11505
11506
11507static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011508trputc(int c)
Eric Andersencb57d552001-06-28 07:25:16 +000011509{
11510 if (tracefile == NULL)
11511 return;
11512 putc(c, tracefile);
11513 if (c == '\n')
11514 fflush(tracefile);
11515}
11516
11517static void
11518trace(const char *fmt, ...)
11519{
11520 va_list va;
Eric Andersencb57d552001-06-28 07:25:16 +000011521 va_start(va, fmt);
Eric Andersencb57d552001-06-28 07:25:16 +000011522 if (tracefile != NULL) {
11523 (void) vfprintf(tracefile, fmt, va);
11524 if (strchr(fmt, '\n'))
11525 (void) fflush(tracefile);
11526 }
11527 va_end(va);
11528}
11529
11530
11531static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011532trputs(const char *s)
Eric Andersencb57d552001-06-28 07:25:16 +000011533{
11534 if (tracefile == NULL)
11535 return;
11536 fputs(s, tracefile);
11537 if (strchr(s, '\n'))
11538 fflush(tracefile);
11539}
11540
11541
11542static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011543trstring(char *s)
Eric Andersencb57d552001-06-28 07:25:16 +000011544{
11545 char *p;
11546 char c;
11547
11548 if (tracefile == NULL)
11549 return;
11550 putc('"', tracefile);
11551 for (p = s ; *p ; p++) {
11552 switch (*p) {
11553 case '\n': c = 'n'; goto backslash;
11554 case '\t': c = 't'; goto backslash;
11555 case '\r': c = 'r'; goto backslash;
11556 case '"': c = '"'; goto backslash;
11557 case '\\': c = '\\'; goto backslash;
11558 case CTLESC: c = 'e'; goto backslash;
11559 case CTLVAR: c = 'v'; goto backslash;
11560 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11561 case CTLBACKQ: c = 'q'; goto backslash;
11562 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Eric Andersen2870d962001-07-02 17:27:21 +000011563backslash: putc('\\', tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011564 putc(c, tracefile);
11565 break;
11566 default:
11567 if (*p >= ' ' && *p <= '~')
11568 putc(*p, tracefile);
11569 else {
11570 putc('\\', tracefile);
11571 putc(*p >> 6 & 03, tracefile);
11572 putc(*p >> 3 & 07, tracefile);
11573 putc(*p & 07, tracefile);
11574 }
11575 break;
11576 }
11577 }
11578 putc('"', tracefile);
11579}
11580
11581
11582static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011583trargs(char **ap)
Eric Andersencb57d552001-06-28 07:25:16 +000011584{
11585 if (tracefile == NULL)
11586 return;
11587 while (*ap) {
11588 trstring(*ap++);
11589 if (*ap)
11590 putc(' ', tracefile);
11591 else
11592 putc('\n', tracefile);
11593 }
11594 fflush(tracefile);
11595}
11596
11597
11598static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011599opentrace()
11600{
Eric Andersencb57d552001-06-28 07:25:16 +000011601 char s[100];
11602#ifdef O_APPEND
11603 int flags;
11604#endif
11605
11606 if (!debug)
11607 return;
11608#ifdef not_this_way
11609 {
11610 char *p;
11611 if ((p = getenv("HOME")) == NULL) {
11612 if (geteuid() == 0)
11613 p = "/";
11614 else
11615 p = "/tmp";
11616 }
Eric Andersen2870d962001-07-02 17:27:21 +000011617 strcpy(s, p);
Eric Andersencb57d552001-06-28 07:25:16 +000011618 strcat(s, "/trace");
11619 }
11620#else
Eric Andersen2870d962001-07-02 17:27:21 +000011621 strcpy(s, "./trace");
Eric Andersencb57d552001-06-28 07:25:16 +000011622#endif /* not_this_way */
Matt Kraaia5f09c62001-11-12 16:44:55 +000011623 if ((tracefile = wfopen(s, "a")) == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011624 return;
Eric Andersencb57d552001-06-28 07:25:16 +000011625#ifdef O_APPEND
11626 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11627 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11628#endif
11629 fputs("\nTracing started.\n", tracefile);
11630 fflush(tracefile);
11631}
11632#endif /* DEBUG */
11633
11634
11635/*
Eric Andersencb57d552001-06-28 07:25:16 +000011636 * The trap builtin.
11637 */
11638
11639static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011640trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011641{
11642 char *action;
11643 char **ap;
11644 int signo;
11645
11646 if (argc <= 1) {
11647 for (signo = 0 ; signo < NSIG ; signo++) {
11648 if (trap[signo] != NULL) {
11649 char *p;
Eric Andersen34506362001-08-02 05:02:46 +000011650 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011651
11652 p = single_quote(trap[signo]);
Eric Andersen34506362001-08-02 05:02:46 +000011653 sn = sys_siglist[signo];
11654 if(sn==NULL)
11655 sn = u_signal_names(0, &signo, 0);
11656 if(sn==NULL)
11657 sn = "???";
11658 printf("trap -- %s %s\n", p, sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011659 stunalloc(p);
11660 }
11661 }
11662 return 0;
11663 }
11664 ap = argv + 1;
11665 if (argc == 2)
11666 action = NULL;
11667 else
11668 action = *ap++;
11669 while (*ap) {
11670 if ((signo = decode_signal(*ap, 0)) < 0)
11671 error("%s: bad trap", *ap);
11672 INTOFF;
11673 if (action) {
11674 if (action[0] == '-' && action[1] == '\0')
11675 action = NULL;
11676 else
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011677 action = xstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011678 }
11679 if (trap[signo])
11680 ckfree(trap[signo]);
11681 trap[signo] = action;
11682 if (signo != 0)
11683 setsignal(signo);
11684 INTON;
11685 ap++;
11686 }
11687 return 0;
11688}
11689
11690
11691
Eric Andersencb57d552001-06-28 07:25:16 +000011692
11693
11694
11695/*
11696 * Set the signal handler for the specified signal. The routine figures
11697 * out what it should be set to.
11698 */
11699
11700static void
Eric Andersen2870d962001-07-02 17:27:21 +000011701setsignal(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011702{
11703 int action;
11704 char *t;
11705 struct sigaction act;
11706
11707 if ((t = trap[signo]) == NULL)
11708 action = S_DFL;
11709 else if (*t != '\0')
11710 action = S_CATCH;
11711 else
11712 action = S_IGN;
11713 if (rootshell && action == S_DFL) {
11714 switch (signo) {
11715 case SIGINT:
11716 if (iflag || minusc || sflag == 0)
11717 action = S_CATCH;
11718 break;
11719 case SIGQUIT:
11720#ifdef DEBUG
11721 {
Eric Andersencb57d552001-06-28 07:25:16 +000011722
11723 if (debug)
11724 break;
11725 }
11726#endif
11727 /* FALLTHROUGH */
11728 case SIGTERM:
11729 if (iflag)
11730 action = S_IGN;
11731 break;
Eric Andersend35c5df2002-01-09 15:37:36 +000011732#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000011733 case SIGTSTP:
11734 case SIGTTOU:
11735 if (mflag)
11736 action = S_IGN;
11737 break;
11738#endif
11739 }
11740 }
11741
11742 t = &sigmode[signo - 1];
11743 if (*t == 0) {
11744 /*
11745 * current setting unknown
11746 */
11747 if (sigaction(signo, 0, &act) == -1) {
11748 /*
11749 * Pretend it worked; maybe we should give a warning
11750 * here, but other shells don't. We don't alter
11751 * sigmode, so that we retry every time.
11752 */
11753 return;
11754 }
11755 if (act.sa_handler == SIG_IGN) {
11756 if (mflag && (signo == SIGTSTP ||
11757 signo == SIGTTIN || signo == SIGTTOU)) {
Eric Andersen2870d962001-07-02 17:27:21 +000011758 *t = S_IGN; /* don't hard ignore these */
Eric Andersencb57d552001-06-28 07:25:16 +000011759 } else
11760 *t = S_HARD_IGN;
11761 } else {
Eric Andersen2870d962001-07-02 17:27:21 +000011762 *t = S_RESET; /* force to be set */
Eric Andersencb57d552001-06-28 07:25:16 +000011763 }
11764 }
11765 if (*t == S_HARD_IGN || *t == action)
11766 return;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011767 act.sa_handler = ((action == S_CATCH) ? onsig
11768 : ((action == S_IGN) ? SIG_IGN : SIG_DFL));
Eric Andersencb57d552001-06-28 07:25:16 +000011769 *t = action;
11770 act.sa_flags = 0;
11771 sigemptyset(&act.sa_mask);
11772 sigaction(signo, &act, 0);
11773}
11774
11775/*
11776 * Ignore a signal.
11777 */
11778
11779static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011780ignoresig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011781{
11782 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11783 signal(signo, SIG_IGN);
11784 }
11785 sigmode[signo - 1] = S_HARD_IGN;
11786}
11787
11788
Eric Andersencb57d552001-06-28 07:25:16 +000011789/*
11790 * Signal handler.
11791 */
11792
11793static void
Eric Andersen2870d962001-07-02 17:27:21 +000011794onsig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011795{
11796 if (signo == SIGINT && trap[SIGINT] == NULL) {
11797 onint();
11798 return;
11799 }
11800 gotsig[signo - 1] = 1;
11801 pendingsigs++;
11802}
11803
11804
Eric Andersencb57d552001-06-28 07:25:16 +000011805/*
11806 * Called to execute a trap. Perhaps we should avoid entering new trap
11807 * handlers while we are executing a trap handler.
11808 */
11809
11810static void
Eric Andersen2870d962001-07-02 17:27:21 +000011811dotrap(void)
11812{
Eric Andersencb57d552001-06-28 07:25:16 +000011813 int i;
11814 int savestatus;
11815
11816 for (;;) {
11817 for (i = 1 ; ; i++) {
11818 if (gotsig[i - 1])
11819 break;
11820 if (i >= NSIG - 1)
11821 goto done;
11822 }
11823 gotsig[i - 1] = 0;
11824 savestatus=exitstatus;
11825 evalstring(trap[i], 0);
11826 exitstatus=savestatus;
11827 }
11828done:
11829 pendingsigs = 0;
11830}
11831
Eric Andersencb57d552001-06-28 07:25:16 +000011832/*
11833 * Called to exit the shell.
11834 */
11835
11836static void
Eric Andersen2870d962001-07-02 17:27:21 +000011837exitshell(int status)
Eric Andersencb57d552001-06-28 07:25:16 +000011838{
11839 struct jmploc loc1, loc2;
11840 char *p;
11841
11842 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
11843 if (setjmp(loc1.loc)) {
11844 goto l1;
11845 }
11846 if (setjmp(loc2.loc)) {
11847 goto l2;
11848 }
11849 handler = &loc1;
11850 if ((p = trap[0]) != NULL && *p != '\0') {
11851 trap[0] = NULL;
11852 evalstring(p, 0);
11853 }
Eric Andersen2870d962001-07-02 17:27:21 +000011854l1: handler = &loc2; /* probably unnecessary */
Eric Andersencb57d552001-06-28 07:25:16 +000011855 flushall();
Eric Andersend35c5df2002-01-09 15:37:36 +000011856#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000011857 setjobctl(0);
11858#endif
11859l2: _exit(status);
11860 /* NOTREACHED */
11861}
11862
11863static int decode_signal(const char *string, int minsig)
11864{
11865 int signo;
Eric Andersen34506362001-08-02 05:02:46 +000011866 const char *name = u_signal_names(string, &signo, minsig);
Eric Andersencb57d552001-06-28 07:25:16 +000011867
Eric Andersen34506362001-08-02 05:02:46 +000011868 return name ? signo : -1;
Eric Andersencb57d552001-06-28 07:25:16 +000011869}
Eric Andersen34506362001-08-02 05:02:46 +000011870
Eric Andersen2870d962001-07-02 17:27:21 +000011871static struct var **hashvar (const char *);
11872static void showvars (const char *, int, int);
11873static struct var **findvar (struct var **, const char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011874
11875/*
11876 * Initialize the varable symbol tables and import the environment
11877 */
11878
Eric Andersencb57d552001-06-28 07:25:16 +000011879/*
11880 * This routine initializes the builtin variables. It is called when the
11881 * shell is initialized and again when a shell procedure is spawned.
11882 */
11883
11884static void
11885initvar() {
11886 const struct varinit *ip;
11887 struct var *vp;
11888 struct var **vpp;
11889
11890 for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
11891 if ((vp->flags & VEXPORT) == 0) {
11892 vpp = hashvar(ip->text);
11893 vp->next = *vpp;
11894 *vpp = vp;
Matt Kraaic8227632001-11-12 16:57:27 +000011895 vp->text = xstrdup(ip->text);
Eric Andersencb57d552001-06-28 07:25:16 +000011896 vp->flags = ip->flags;
11897 vp->func = ip->func;
11898 }
11899 }
Tim Riker497a8852002-04-13 05:37:10 +000011900#if !defined(CONFIG_FEATURE_COMMAND_EDITING) || !defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
Eric Andersencb57d552001-06-28 07:25:16 +000011901 /*
11902 * PS1 depends on uid
11903 */
11904 if ((vps1.flags & VEXPORT) == 0) {
Aaron Lehmann9a218bf2001-12-31 06:13:38 +000011905 vpp = hashvar("PS1=$");
Eric Andersencb57d552001-06-28 07:25:16 +000011906 vps1.next = *vpp;
11907 *vpp = &vps1;
Matt Kraaic8227632001-11-12 16:57:27 +000011908 vps1.text = xstrdup(geteuid() ? "PS1=$ " : "PS1=# ");
Eric Andersencb57d552001-06-28 07:25:16 +000011909 vps1.flags = VSTRFIXED|VTEXTFIXED;
11910 }
Tim Riker497a8852002-04-13 05:37:10 +000011911#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011912}
11913
11914/*
11915 * Set the value of a variable. The flags argument is ored with the
11916 * flags of the variable. If val is NULL, the variable is unset.
11917 */
11918
11919static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011920setvar(const char *name, const char *val, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +000011921{
11922 const char *p;
11923 int len;
11924 int namelen;
11925 char *nameeq;
11926 int isbad;
11927 int vallen = 0;
11928
11929 isbad = 0;
11930 p = name;
11931 if (! is_name(*p))
11932 isbad = 1;
11933 p++;
11934 for (;;) {
11935 if (! is_in_name(*p)) {
11936 if (*p == '\0' || *p == '=')
11937 break;
11938 isbad = 1;
11939 }
11940 p++;
11941 }
11942 namelen = p - name;
11943 if (isbad)
11944 error("%.*s: bad variable name", namelen, name);
Eric Andersen2870d962001-07-02 17:27:21 +000011945 len = namelen + 2; /* 2 is space for '=' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +000011946 if (val == NULL) {
11947 flags |= VUNSET;
11948 } else {
11949 len += vallen = strlen(val);
11950 }
11951 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011952 nameeq = xmalloc(len);
Eric Andersencb57d552001-06-28 07:25:16 +000011953 memcpy(nameeq, name, namelen);
11954 nameeq[namelen] = '=';
11955 if (val) {
11956 memcpy(nameeq + namelen + 1, val, vallen + 1);
11957 } else {
11958 nameeq[namelen + 1] = '\0';
11959 }
11960 setvareq(nameeq, flags);
11961 INTON;
11962}
11963
11964
11965
11966/*
11967 * Same as setvar except that the variable and value are passed in
11968 * the first argument as name=value. Since the first argument will
11969 * be actually stored in the table, it should not be a string that
11970 * will go away.
11971 */
11972
11973static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011974setvareq(char *s, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +000011975{
11976 struct var *vp, **vpp;
11977
11978 vpp = hashvar(s);
11979 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
11980 if ((vp = *findvar(vpp, s))) {
11981 if (vp->flags & VREADONLY) {
11982 size_t len = strchr(s, '=') - s;
11983 error("%.*s: is read only", len, s);
11984 }
11985 INTOFF;
11986
11987 if (vp->func && (flags & VNOFUNC) == 0)
11988 (*vp->func)(strchr(s, '=') + 1);
11989
11990 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
11991 ckfree(vp->text);
11992
11993 vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
11994 vp->flags |= flags;
11995 vp->text = s;
11996
Eric Andersend35c5df2002-01-09 15:37:36 +000011997#ifdef CONFIG_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011998 /*
11999 * We could roll this to a function, to handle it as
12000 * a regular variable function callback, but why bother?
12001 */
12002 if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset())))
12003 chkmail(1);
Eric Andersenec074692001-10-31 11:05:49 +000012004#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012005 INTON;
12006 return;
12007 }
12008 /* not found */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012009 vp = xmalloc(sizeof (*vp));
Eric Andersencb57d552001-06-28 07:25:16 +000012010 vp->flags = flags;
12011 vp->text = s;
12012 vp->next = *vpp;
12013 vp->func = NULL;
12014 *vpp = vp;
12015}
12016
12017
12018
12019/*
12020 * Process a linked list of variable assignments.
12021 */
12022
12023static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012024listsetvar(struct strlist *mylist)
12025{
Eric Andersencb57d552001-06-28 07:25:16 +000012026 struct strlist *lp;
12027
12028 INTOFF;
12029 for (lp = mylist ; lp ; lp = lp->next) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012030 setvareq(xstrdup(lp->text), 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012031 }
12032 INTON;
12033}
12034
12035
12036
12037/*
12038 * Find the value of a variable. Returns NULL if not set.
12039 */
12040
Eric Andersen62483552001-07-10 06:09:16 +000012041static const char *
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012042lookupvar(const char *name)
Eric Andersenec074692001-10-31 11:05:49 +000012043{
Eric Andersencb57d552001-06-28 07:25:16 +000012044 struct var *v;
12045
12046 if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
12047 return strchr(v->text, '=') + 1;
12048 }
12049 return NULL;
12050}
12051
12052
12053
12054/*
12055 * Search the environment of a builtin command.
12056 */
12057
Eric Andersen62483552001-07-10 06:09:16 +000012058static const char *
12059bltinlookup(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +000012060{
Eric Andersen62483552001-07-10 06:09:16 +000012061 const struct strlist *sp;
Eric Andersencb57d552001-06-28 07:25:16 +000012062
12063 for (sp = cmdenviron ; sp ; sp = sp->next) {
12064 if (varequal(sp->text, name))
12065 return strchr(sp->text, '=') + 1;
12066 }
12067 return lookupvar(name);
12068}
12069
12070
12071
12072/*
12073 * Generate a list of exported variables. This routine is used to construct
12074 * the third argument to execve when executing a program.
12075 */
12076
12077static char **
12078environment() {
12079 int nenv;
12080 struct var **vpp;
12081 struct var *vp;
12082 char **env;
12083 char **ep;
12084
12085 nenv = 0;
12086 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12087 for (vp = *vpp ; vp ; vp = vp->next)
12088 if (vp->flags & VEXPORT)
12089 nenv++;
12090 }
12091 ep = env = stalloc((nenv + 1) * sizeof *env);
12092 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12093 for (vp = *vpp ; vp ; vp = vp->next)
12094 if (vp->flags & VEXPORT)
12095 *ep++ = vp->text;
12096 }
12097 *ep = NULL;
12098 return env;
12099}
12100
12101
12102/*
12103 * Called when a shell procedure is invoked to clear out nonexported
12104 * variables. It is also necessary to reallocate variables of with
12105 * VSTACK set since these are currently allocated on the stack.
12106 */
12107
Eric Andersencb57d552001-06-28 07:25:16 +000012108static void
Eric Andersen2870d962001-07-02 17:27:21 +000012109shprocvar(void) {
Eric Andersencb57d552001-06-28 07:25:16 +000012110 struct var **vpp;
12111 struct var *vp, **prev;
12112
12113 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12114 for (prev = vpp ; (vp = *prev) != NULL ; ) {
12115 if ((vp->flags & VEXPORT) == 0) {
12116 *prev = vp->next;
12117 if ((vp->flags & VTEXTFIXED) == 0)
12118 ckfree(vp->text);
12119 if ((vp->flags & VSTRFIXED) == 0)
12120 ckfree(vp);
12121 } else {
12122 if (vp->flags & VSTACK) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012123 vp->text = xstrdup(vp->text);
Eric Andersencb57d552001-06-28 07:25:16 +000012124 vp->flags &=~ VSTACK;
12125 }
12126 prev = &vp->next;
12127 }
12128 }
12129 }
12130 initvar();
12131}
12132
12133
12134
12135/*
12136 * Command to list all variables which are set. Currently this command
12137 * is invoked from the set command when the set command is called without
12138 * any variables.
12139 */
12140
12141static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012142showvarscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012143{
12144 showvars(nullstr, VUNSET, VUNSET);
12145 return 0;
12146}
12147
12148
12149
12150/*
12151 * The export and readonly commands.
12152 */
12153
12154static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012155exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012156{
12157 struct var *vp;
12158 char *name;
12159 const char *p;
12160 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
12161 int pflag;
12162
12163 listsetvar(cmdenviron);
12164 pflag = (nextopt("p") == 'p');
12165 if (argc > 1 && !pflag) {
12166 while ((name = *argptr++) != NULL) {
12167 if ((p = strchr(name, '=')) != NULL) {
12168 p++;
12169 } else {
12170 if ((vp = *findvar(hashvar(name), name))) {
12171 vp->flags |= flag;
12172 goto found;
12173 }
12174 }
12175 setvar(name, p, flag);
12176found:;
12177 }
12178 } else {
12179 showvars(argv[0], flag, 0);
12180 }
12181 return 0;
12182}
12183
Eric Andersen34506362001-08-02 05:02:46 +000012184
Eric Andersencb57d552001-06-28 07:25:16 +000012185/*
12186 * The "local" command.
12187 */
12188
Eric Andersen2870d962001-07-02 17:27:21 +000012189/* funcnest nonzero if we are currently evaluating a function */
12190
Eric Andersencb57d552001-06-28 07:25:16 +000012191static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012192localcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012193{
12194 char *name;
12195
Eric Andersen2870d962001-07-02 17:27:21 +000012196 if (! funcnest)
Eric Andersencb57d552001-06-28 07:25:16 +000012197 error("Not in a function");
12198 while ((name = *argptr++) != NULL) {
12199 mklocal(name);
12200 }
12201 return 0;
12202}
12203
12204
12205/*
12206 * Make a variable a local variable. When a variable is made local, it's
12207 * value and flags are saved in a localvar structure. The saved values
12208 * will be restored when the shell function returns. We handle the name
12209 * "-" as a special case.
12210 */
12211
12212static void
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012213mklocal(char *name)
12214{
Eric Andersencb57d552001-06-28 07:25:16 +000012215 struct localvar *lvp;
12216 struct var **vpp;
12217 struct var *vp;
12218
12219 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012220 lvp = xmalloc(sizeof (struct localvar));
Eric Andersencb57d552001-06-28 07:25:16 +000012221 if (name[0] == '-' && name[1] == '\0') {
12222 char *p;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012223 p = xmalloc(sizeof optet_vals);
Eric Andersen2870d962001-07-02 17:27:21 +000012224 lvp->text = memcpy(p, optet_vals, sizeof optet_vals);
Eric Andersencb57d552001-06-28 07:25:16 +000012225 vp = NULL;
12226 } else {
12227 vpp = hashvar(name);
12228 vp = *findvar(vpp, name);
12229 if (vp == NULL) {
12230 if (strchr(name, '='))
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012231 setvareq(xstrdup(name), VSTRFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +000012232 else
12233 setvar(name, NULL, VSTRFIXED);
Eric Andersen2870d962001-07-02 17:27:21 +000012234 vp = *vpp; /* the new variable */
Eric Andersencb57d552001-06-28 07:25:16 +000012235 lvp->text = NULL;
12236 lvp->flags = VUNSET;
12237 } else {
12238 lvp->text = vp->text;
12239 lvp->flags = vp->flags;
12240 vp->flags |= VSTRFIXED|VTEXTFIXED;
12241 if (strchr(name, '='))
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012242 setvareq(xstrdup(name), 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012243 }
12244 }
12245 lvp->vp = vp;
12246 lvp->next = localvars;
12247 localvars = lvp;
12248 INTON;
12249}
12250
12251
12252/*
12253 * Called after a function returns.
12254 */
12255
12256static void
12257poplocalvars() {
12258 struct localvar *lvp;
12259 struct var *vp;
12260
12261 while ((lvp = localvars) != NULL) {
12262 localvars = lvp->next;
12263 vp = lvp->vp;
Eric Andersen2870d962001-07-02 17:27:21 +000012264 if (vp == NULL) { /* $- saved */
12265 memcpy(optet_vals, lvp->text, sizeof optet_vals);
Eric Andersencb57d552001-06-28 07:25:16 +000012266 ckfree(lvp->text);
12267 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
12268 (void)unsetvar(vp->text);
12269 } else {
12270 if ((vp->flags & VTEXTFIXED) == 0)
12271 ckfree(vp->text);
12272 vp->flags = lvp->flags;
12273 vp->text = lvp->text;
12274 }
12275 ckfree(lvp);
12276 }
12277}
12278
12279
12280static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012281setvarcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012282{
12283 if (argc <= 2)
12284 return unsetcmd(argc, argv);
12285 else if (argc == 3)
12286 setvar(argv[1], argv[2], 0);
12287 else
12288 error("List assignment not implemented");
12289 return 0;
12290}
12291
12292
12293/*
12294 * The unset builtin command. We unset the function before we unset the
12295 * variable to allow a function to be unset when there is a readonly variable
12296 * with the same name.
12297 */
12298
12299static int
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012300unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012301{
12302 char **ap;
12303 int i;
12304 int flg_func = 0;
12305 int flg_var = 0;
12306 int ret = 0;
12307
12308 while ((i = nextopt("vf")) != '\0') {
12309 if (i == 'f')
12310 flg_func = 1;
12311 else
12312 flg_var = 1;
12313 }
12314 if (flg_func == 0 && flg_var == 0)
12315 flg_var = 1;
12316
12317 for (ap = argptr; *ap ; ap++) {
12318 if (flg_func)
12319 unsetfunc(*ap);
12320 if (flg_var)
12321 ret |= unsetvar(*ap);
12322 }
12323 return ret;
12324}
12325
12326
12327/*
12328 * Unset the specified variable.
12329 */
12330
12331static int
Eric Andersen62483552001-07-10 06:09:16 +000012332unsetvar(const char *s)
12333{
Eric Andersencb57d552001-06-28 07:25:16 +000012334 struct var **vpp;
12335 struct var *vp;
12336
12337 vpp = findvar(hashvar(s), s);
12338 vp = *vpp;
12339 if (vp) {
12340 if (vp->flags & VREADONLY)
12341 return (1);
12342 INTOFF;
12343 if (*(strchr(vp->text, '=') + 1) != '\0')
12344 setvar(s, nullstr, 0);
12345 vp->flags &= ~VEXPORT;
12346 vp->flags |= VUNSET;
12347 if ((vp->flags & VSTRFIXED) == 0) {
12348 if ((vp->flags & VTEXTFIXED) == 0)
12349 ckfree(vp->text);
12350 *vpp = vp->next;
12351 ckfree(vp);
12352 }
12353 INTON;
12354 return (0);
12355 }
12356
12357 return (0);
12358}
12359
12360
12361
12362/*
12363 * Find the appropriate entry in the hash table from the name.
12364 */
12365
12366static struct var **
Eric Andersen62483552001-07-10 06:09:16 +000012367hashvar(const char *p)
12368{
Eric Andersencb57d552001-06-28 07:25:16 +000012369 unsigned int hashval;
12370
12371 hashval = ((unsigned char) *p) << 4;
12372 while (*p && *p != '=')
12373 hashval += (unsigned char) *p++;
12374 return &vartab[hashval % VTABSIZE];
12375}
12376
12377
12378
12379/*
12380 * Returns true if the two strings specify the same varable. The first
12381 * variable name is terminated by '='; the second may be terminated by
12382 * either '=' or '\0'.
12383 */
12384
12385static int
Eric Andersen62483552001-07-10 06:09:16 +000012386varequal(const char *p, const char *q)
12387{
Eric Andersencb57d552001-06-28 07:25:16 +000012388 while (*p == *q++) {
12389 if (*p++ == '=')
12390 return 1;
12391 }
12392 if (*p == '=' && *(q - 1) == '\0')
12393 return 1;
12394 return 0;
12395}
12396
12397static void
12398showvars(const char *myprefix, int mask, int xor)
12399{
12400 struct var **vpp;
12401 struct var *vp;
12402 const char *sep = myprefix == nullstr ? myprefix : spcstr;
12403
12404 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
12405 for (vp = *vpp ; vp ; vp = vp->next) {
12406 if ((vp->flags & mask) ^ xor) {
12407 char *p;
12408 int len;
12409
12410 p = strchr(vp->text, '=') + 1;
12411 len = p - vp->text;
12412 p = single_quote(p);
12413
Eric Andersen62483552001-07-10 06:09:16 +000012414 printf("%s%s%.*s%s\n", myprefix, sep, len,
12415 vp->text, p);
Eric Andersencb57d552001-06-28 07:25:16 +000012416 stunalloc(p);
12417 }
12418 }
12419 }
12420}
12421
12422static struct var **
12423findvar(struct var **vpp, const char *name)
12424{
12425 for (; *vpp; vpp = &(*vpp)->next) {
12426 if (varequal((*vpp)->text, name)) {
12427 break;
12428 }
12429 }
12430 return vpp;
12431}
12432
12433/*
12434 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
12435 * This file contains code for the times builtin.
Tim Riker497a8852002-04-13 05:37:10 +000012436 * $Id: ash.c,v 1.47 2002/04/13 05:37:10 timr Exp $
Eric Andersencb57d552001-06-28 07:25:16 +000012437 */
12438static int timescmd (int argc, char **argv)
12439{
12440 struct tms buf;
12441 long int clk_tck = sysconf(_SC_CLK_TCK);
12442
12443 times(&buf);
12444 printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
12445 (int) (buf.tms_utime / clk_tck / 60),
12446 ((double) buf.tms_utime) / clk_tck,
12447 (int) (buf.tms_stime / clk_tck / 60),
12448 ((double) buf.tms_stime) / clk_tck,
12449 (int) (buf.tms_cutime / clk_tck / 60),
12450 ((double) buf.tms_cutime) / clk_tck,
12451 (int) (buf.tms_cstime / clk_tck / 60),
12452 ((double) buf.tms_cstime) / clk_tck);
12453 return 0;
12454}
12455
Eric Andersend35c5df2002-01-09 15:37:36 +000012456#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012457/* The let builtin. */
12458int letcmd(int argc, char **argv)
Eric Andersen74bcd162001-07-30 21:41:37 +000012459{
Eric Andersen34506362001-08-02 05:02:46 +000012460 int errcode;
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012461 long result=0;
12462 if (argc == 2) {
12463 char *tmp, *expression, p[13];
12464 expression = strchr(argv[1], '=');
12465 if (!expression) {
Eric Andersen34506362001-08-02 05:02:46 +000012466 /* Cannot use 'error()' here, or the return code
12467 * will be incorrect */
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012468 out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]);
12469 return 0;
Eric Andersen74bcd162001-07-30 21:41:37 +000012470 }
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012471 *expression = '\0';
12472 tmp = ++expression;
Eric Andersen34506362001-08-02 05:02:46 +000012473 result = arith(tmp, &errcode);
12474 if (errcode < 0) {
12475 /* Cannot use 'error()' here, or the return code
12476 * will be incorrect */
12477 out2fmt("sh: let: ");
12478 if(errcode == -2)
12479 out2fmt("divide by zero");
12480 else
12481 out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression);
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012482 return 0;
12483 }
12484 snprintf(p, 12, "%ld", result);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012485 setvar(argv[1], xstrdup(p), 0);
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012486 } else if (argc >= 3)
12487 synerror("invalid operand");
12488 return !result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012489}
12490#endif
12491
12492
12493
Eric Andersendf82f612001-06-28 07:46:40 +000012494/*-
12495 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012496 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012497 *
12498 * This code is derived from software contributed to Berkeley by
12499 * Kenneth Almquist.
12500 *
12501 * Redistribution and use in source and binary forms, with or without
12502 * modification, are permitted provided that the following conditions
12503 * are met:
12504 * 1. Redistributions of source code must retain the above copyright
12505 * notice, this list of conditions and the following disclaimer.
12506 * 2. Redistributions in binary form must reproduce the above copyright
12507 * notice, this list of conditions and the following disclaimer in the
12508 * documentation and/or other materials provided with the distribution.
12509 *
Eric Andersen2870d962001-07-02 17:27:21 +000012510 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
12511 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
Eric Andersendf82f612001-06-28 07:46:40 +000012512 *
12513 * 4. Neither the name of the University nor the names of its contributors
12514 * may be used to endorse or promote products derived from this software
12515 * without specific prior written permission.
12516 *
12517 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12518 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12519 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12520 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12521 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12522 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12523 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12524 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12525 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12526 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12527 * SUCH DAMAGE.
12528 */