blob: 696d554a9114c091d32270dc093ff6e611c3f956 [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>
Robert Grieblea1a63a2002-06-04 20:10:23 +000071#include "busybox.h"
Eric Andersen887ca792002-07-03 23:19:26 +000072#include "pwd_.h"
Eric Andersencb57d552001-06-28 07:25:16 +000073
74
75#if !defined(FNMATCH_BROKEN)
76#include <fnmatch.h>
77#endif
78#if !defined(GLOB_BROKEN)
79#include <glob.h>
80#endif
81
Eric Andersend35c5df2002-01-09 15:37:36 +000082#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000083#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000084#endif
85
Eric Andersen2870d962001-07-02 17:27:21 +000086#include "cmdedit.h"
87
Eric Andersenaa1d6cc2002-09-30 20:12:32 +000088#if defined(__uClinux__)
89#error "Do not even bother, ash will not run on uClinux"
90#endif
91
Eric Andersen2870d962001-07-02 17:27:21 +000092/*
93 * This file was generated by the mksyntax program.
94 */
95
96/* Syntax classes */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000097#define CWORD 0 /* character is nothing special */
98#define CNL 1 /* newline character */
99#define CBACK 2 /* a backslash character */
100#define CSQUOTE 3 /* single quote */
101#define CDQUOTE 4 /* double quote */
102#define CENDQUOTE 5 /* a terminating quote */
103#define CBQUOTE 6 /* backwards single quote */
104#define CVAR 7 /* a dollar sign */
105#define CENDVAR 8 /* a '}' character */
106#define CLP 9 /* a left paren in arithmetic */
107#define CRP 10 /* a right paren in arithmetic */
108#define CENDFILE 11 /* end of file */
109#define CCTL 12 /* like CWORD, except it must be escaped */
110#define CSPCL 13 /* these terminate a word */
111#define CIGN 14 /* character should be ignored */
Eric Andersen2870d962001-07-02 17:27:21 +0000112
Eric Andersen2870d962001-07-02 17:27:21 +0000113#define SYNBASE 130
114#define PEOF -130
115
116#define PEOA -129
117
118#define TEOF 0
119#define TNL 1
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000120#define TREDIR 2
121#define TWORD 3
122#define TASSIGN 4
123#define TSEMI 5
124#define TBACKGND 6
125#define TAND 7
126#define TOR 8
127#define TPIPE 9
128#define TLP 10
129#define TRP 11
130#define TENDCASE 12
131#define TENDBQUOTE 13
Eric Andersen2870d962001-07-02 17:27:21 +0000132#define TNOT 14
133#define TCASE 15
134#define TDO 16
135#define TDONE 17
136#define TELIF 18
137#define TELSE 19
138#define TESAC 20
139#define TFI 21
140#define TFOR 22
141#define TIF 23
142#define TIN 24
143#define TTHEN 25
144#define TUNTIL 26
145#define TWHILE 27
146#define TBEGIN 28
147#define TEND 29
148
149
Eric Andersen2870d962001-07-02 17:27:21 +0000150
151/* control characters in argument strings */
152#define CTLESC '\201'
153#define CTLVAR '\202'
154#define CTLENDVAR '\203'
155#define CTLBACKQ '\204'
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000156#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
Eric Andersen2870d962001-07-02 17:27:21 +0000157/* CTLBACKQ | CTLQUOTE == '\205' */
158#define CTLARI '\206'
159#define CTLENDARI '\207'
160#define CTLQUOTEMARK '\210'
161
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000162
Eric Andersen62483552001-07-10 06:09:16 +0000163#define is_digit(c) ((c)>='0' && (c)<='9')
Eric Andersen2870d962001-07-02 17:27:21 +0000164#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
165#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000166
167/*
168 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
169 * (assuming ascii char codes, as the original implementation did)
170 */
171#define is_special(c) \
172 ( (((unsigned int)c) - 33 < 32) \
173 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
174
Eric Andersen2870d962001-07-02 17:27:21 +0000175#define digit_val(c) ((c) - '0')
Eric Andersencb57d552001-06-28 07:25:16 +0000176
177
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000178#define S_DFL 1 /* default signal handling (SIG_DFL) */
179#define S_CATCH 2 /* signal is caught */
180#define S_IGN 3 /* signal is ignored (SIG_IGN) */
181#define S_HARD_IGN 4 /* signal is ignored permenantly */
182#define S_RESET 5 /* temporary - to reset a hard ignored sig */
Eric Andersencb57d552001-06-28 07:25:16 +0000183
184
Eric Andersen2870d962001-07-02 17:27:21 +0000185/* variable substitution byte (follows CTLVAR) */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000186#define VSTYPE 0x0f /* type of variable substitution */
187#define VSNUL 0x10 /* colon--treat the empty string as unset */
188#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
Eric Andersencb57d552001-06-28 07:25:16 +0000189
Eric Andersen2870d962001-07-02 17:27:21 +0000190/* values of VSTYPE field */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000191#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
192#define VSMINUS 0x2 /* ${var-text} */
193#define VSPLUS 0x3 /* ${var+text} */
194#define VSQUESTION 0x4 /* ${var?message} */
195#define VSASSIGN 0x5 /* ${var=text} */
196#define VSTRIMLEFT 0x6 /* ${var#pattern} */
197#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
198#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
199#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
200#define VSLENGTH 0xa /* ${#var} */
Eric Andersencb57d552001-06-28 07:25:16 +0000201
Eric Andersen2870d962001-07-02 17:27:21 +0000202/* flags passed to redirect */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000203#define REDIR_PUSH 01 /* save previous values of file descriptors */
204#define REDIR_BACKQ 02 /* save the command output to pipe */
Eric Andersencb57d552001-06-28 07:25:16 +0000205
Eric Andersen2870d962001-07-02 17:27:21 +0000206/*
207 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
208 * so we use _setjmp instead.
209 */
210
Eric Andersen62483552001-07-10 06:09:16 +0000211#if defined(BSD)
Eric Andersen2870d962001-07-02 17:27:21 +0000212#define setjmp(jmploc) _setjmp(jmploc)
213#define longjmp(jmploc, val) _longjmp(jmploc, val)
214#endif
215
216/*
217 * Most machines require the value returned from malloc to be aligned
218 * in some way. The following macro will get this right on many machines.
219 */
220
221#ifndef ALIGN
222union align {
223 int i;
224 char *cp;
225};
226
227#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
228#endif
229
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000230#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +0000231#include <locale.h>
232static void change_lc_all(const char *value);
233static void change_lc_ctype(const char *value);
234#endif
235
236/*
237 * These macros allow the user to suspend the handling of interrupt signals
238 * over a period of time. This is similar to SIGHOLD to or sigblock, but
239 * much more efficient and portable. (But hacking the kernel is so much
240 * more fun than worrying about efficiency and portability. :-))
241 */
242
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000243static void onint(void);
Eric Andersen2870d962001-07-02 17:27:21 +0000244static volatile int suppressint;
245static volatile int intpending;
246
247#define INTOFF suppressint++
Eric Andersend35c5df2002-01-09 15:37:36 +0000248#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +0000249#define INTON { if (--suppressint == 0 && intpending) onint(); }
Eric Andersen3102ac42001-07-06 04:26:23 +0000250#define FORCEINTON {suppressint = 0; if (intpending) onint();}
Eric Andersen2870d962001-07-02 17:27:21 +0000251#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000252static void __inton(void);
253static void forceinton(void);
254
Eric Andersen2870d962001-07-02 17:27:21 +0000255#define INTON __inton()
Eric Andersen3102ac42001-07-06 04:26:23 +0000256#define FORCEINTON forceinton()
Eric Andersen2870d962001-07-02 17:27:21 +0000257#endif
Eric Andersen3102ac42001-07-06 04:26:23 +0000258
Eric Andersen2870d962001-07-02 17:27:21 +0000259#define CLEAR_PENDING_INT intpending = 0
260#define int_pending() intpending
261
262
263typedef void *pointer;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000264
Eric Andersen2870d962001-07-02 17:27:21 +0000265#ifndef NULL
266#define NULL (void *)0
267#endif
268
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000269static pointer stalloc(int);
270static void stunalloc(pointer);
271static void ungrabstackstr(char *, char *);
272static char *growstackstr(void);
273static char *makestrspace(size_t newlen);
Eric Andersen2870d962001-07-02 17:27:21 +0000274
275/*
276 * Parse trees for commands are allocated in lifo order, so we use a stack
277 * to make this more efficient, and also to avoid all sorts of exception
278 * handling code to handle interrupts in the middle of a parse.
279 *
280 * The size 504 was chosen because the Ultrix malloc handles that size
281 * well.
282 */
283
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000284#define MINSIZE 504 /* minimum size of a block */
Eric Andersen2870d962001-07-02 17:27:21 +0000285
286
287struct stack_block {
288 struct stack_block *prev;
289 char space[MINSIZE];
290};
291
292static struct stack_block stackbase;
293static struct stack_block *stackp = &stackbase;
294static struct stackmark *markp;
295static char *stacknxt = stackbase.space;
296static int stacknleft = MINSIZE;
297
298
299#define equal(s1, s2) (strcmp(s1, s2) == 0)
300
301#define stackblock() stacknxt
302#define stackblocksize() stacknleft
303#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
Eric Andersen3102ac42001-07-06 04:26:23 +0000304
Eric Andersen2870d962001-07-02 17:27:21 +0000305#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
306#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); }
Eric Andersen2870d962001-07-02 17:27:21 +0000307#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
Eric Andersen3102ac42001-07-06 04:26:23 +0000308
309
310#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
Eric Andersen2870d962001-07-02 17:27:21 +0000311#define STUNPUTC(p) (++sstrnleft, --p)
312#define STTOPC(p) p[-1]
313#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
314#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
315
Eric Andersen2870d962001-07-02 17:27:21 +0000316
Eric Andersen2870d962001-07-02 17:27:21 +0000317#ifdef DEBUG
318#define TRACE(param) trace param
Eric Andersen69a20f02001-10-31 10:40:37 +0000319typedef union node unode;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000320static void trace(const char *, ...);
321static void trargs(char **);
322static void showtree(unode *);
323static void trputc(int);
324static void trputs(const char *);
325static void opentrace(void);
Eric Andersen2870d962001-07-02 17:27:21 +0000326#else
327#define TRACE(param)
328#endif
329
330#define NSEMI 0
331#define NCMD 1
332#define NPIPE 2
333#define NREDIR 3
334#define NBACKGND 4
335#define NSUBSHELL 5
336#define NAND 6
337#define NOR 7
338#define NIF 8
339#define NWHILE 9
340#define NUNTIL 10
341#define NFOR 11
342#define NCASE 12
343#define NCLIST 13
344#define NDEFUN 14
345#define NARG 15
346#define NTO 16
347#define NFROM 17
348#define NFROMTO 18
349#define NAPPEND 19
350#define NTOOV 20
351#define NTOFD 21
352#define NFROMFD 22
353#define NHERE 23
354#define NXHERE 24
355#define NNOT 25
356
357/*
358 * expandarg() flags
359 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000360#define EXP_FULL 0x1 /* perform word splitting & file globbing */
361#define EXP_TILDE 0x2 /* do normal tilde expansion */
362#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
363#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
364#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
365#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
Eric Andersen2870d962001-07-02 17:27:21 +0000366
367
368#define NOPTS 16
369
370static char optet_vals[NOPTS];
371
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000372static const char *const optlist[NOPTS] = {
Eric Andersen2870d962001-07-02 17:27:21 +0000373 "e" "errexit",
374 "f" "noglob",
375 "I" "ignoreeof",
376 "i" "interactive",
377 "m" "monitor",
378 "n" "noexec",
379 "s" "stdin",
380 "x" "xtrace",
381 "v" "verbose",
382 "V" "vi",
383 "E" "emacs",
384 "C" "noclobber",
385 "a" "allexport",
386 "b" "notify",
387 "u" "nounset",
388 "q" "quietprofile"
389};
390
391#define optent_name(optent) (optent+1)
392#define optent_letter(optent) optent[0]
393#define optent_val(optent) optet_vals[optent]
394
395#define eflag optent_val(0)
396#define fflag optent_val(1)
397#define Iflag optent_val(2)
398#define iflag optent_val(3)
399#define mflag optent_val(4)
400#define nflag optent_val(5)
401#define sflag optent_val(6)
402#define xflag optent_val(7)
403#define vflag optent_val(8)
404#define Vflag optent_val(9)
405#define Eflag optent_val(10)
406#define Cflag optent_val(11)
407#define aflag optent_val(12)
408#define bflag optent_val(13)
409#define uflag optent_val(14)
410#define qflag optent_val(15)
411
412
413/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
414#define FORK_FG 0
415#define FORK_BG 1
416#define FORK_NOJOB 2
417
418
419struct nbinary {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000420 int type;
421 union node *ch1;
422 union node *ch2;
Eric Andersen2870d962001-07-02 17:27:21 +0000423};
424
425
426struct ncmd {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000427 int type;
428 int backgnd;
429 union node *assign;
430 union node *args;
431 union node *redirect;
Eric Andersen2870d962001-07-02 17:27:21 +0000432};
433
434
435struct npipe {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000436 int type;
437 int backgnd;
438 struct nodelist *cmdlist;
Eric Andersen2870d962001-07-02 17:27:21 +0000439};
440
441
442struct nredir {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000443 int type;
444 union node *n;
445 union node *redirect;
Eric Andersen2870d962001-07-02 17:27:21 +0000446};
447
448
449struct nif {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000450 int type;
451 union node *test;
452 union node *ifpart;
453 union node *elsepart;
Eric Andersen2870d962001-07-02 17:27:21 +0000454};
455
456
457struct nfor {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000458 int type;
459 union node *args;
460 union node *body;
461 char *var;
Eric Andersen2870d962001-07-02 17:27:21 +0000462};
463
464
465struct ncase {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000466 int type;
467 union node *expr;
468 union node *cases;
Eric Andersen2870d962001-07-02 17:27:21 +0000469};
470
471
472struct nclist {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000473 int type;
474 union node *next;
475 union node *pattern;
476 union node *body;
Eric Andersen2870d962001-07-02 17:27:21 +0000477};
478
479
480struct narg {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000481 int type;
482 union node *next;
483 char *text;
484 struct nodelist *backquote;
Eric Andersen2870d962001-07-02 17:27:21 +0000485};
486
487
488struct nfile {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000489 int type;
490 union node *next;
491 int fd;
492 union node *fname;
493 char *expfname;
Eric Andersen2870d962001-07-02 17:27:21 +0000494};
495
496
497struct ndup {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000498 int type;
499 union node *next;
500 int fd;
501 int dupfd;
502 union node *vname;
Eric Andersen2870d962001-07-02 17:27:21 +0000503};
504
505
506struct nhere {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000507 int type;
508 union node *next;
509 int fd;
510 union node *doc;
Eric Andersen2870d962001-07-02 17:27:21 +0000511};
512
513
514struct nnot {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000515 int type;
516 union node *com;
Eric Andersen2870d962001-07-02 17:27:21 +0000517};
518
519
520union node {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000521 int type;
522 struct nbinary nbinary;
523 struct ncmd ncmd;
524 struct npipe npipe;
525 struct nredir nredir;
526 struct nif nif;
527 struct nfor nfor;
528 struct ncase ncase;
529 struct nclist nclist;
530 struct narg narg;
531 struct nfile nfile;
532 struct ndup ndup;
533 struct nhere nhere;
534 struct nnot nnot;
Eric Andersen2870d962001-07-02 17:27:21 +0000535};
536
537
538struct nodelist {
539 struct nodelist *next;
540 union node *n;
541};
542
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000543struct backcmd { /* result of evalbackcmd */
544 int fd; /* file descriptor to read from */
545 char *buf; /* buffer */
546 int nleft; /* number of chars in buffer */
547 struct job *jp; /* job structure for command */
Eric Andersen2870d962001-07-02 17:27:21 +0000548};
549
550struct cmdentry {
551 int cmdtype;
552 union param {
553 int index;
554 union node *func;
555 const struct builtincmd *cmd;
556 } u;
557};
558
559struct strlist {
560 struct strlist *next;
561 char *text;
562};
563
564
565struct arglist {
566 struct strlist *list;
567 struct strlist **lastp;
568};
569
570struct strpush {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000571 struct strpush *prev; /* preceding string on stack */
Eric Andersen2870d962001-07-02 17:27:21 +0000572 char *prevstring;
573 int prevnleft;
Eric Andersend35c5df2002-01-09 15:37:36 +0000574#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000575 struct alias *ap; /* if push was associated with an alias */
Eric Andersen2870d962001-07-02 17:27:21 +0000576#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000577 char *string; /* remember the string since it may change */
Eric Andersen2870d962001-07-02 17:27:21 +0000578};
579
580struct parsefile {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000581 struct parsefile *prev; /* preceding file on stack */
582 int linno; /* current line */
583 int fd; /* file descriptor (or -1 if string) */
584 int nleft; /* number of chars left in this line */
585 int lleft; /* number of chars left in this buffer */
586 char *nextc; /* next char in buffer */
587 char *buf; /* input buffer */
588 struct strpush *strpush; /* for pushing strings at this level */
589 struct strpush basestrpush; /* so pushing one is fast */
Eric Andersen2870d962001-07-02 17:27:21 +0000590};
591
592struct stackmark {
593 struct stack_block *stackp;
594 char *stacknxt;
595 int stacknleft;
596 struct stackmark *marknext;
597};
598
599struct shparam {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000600 int nparam; /* # of positional parameters (without $0) */
601 unsigned char malloc; /* if parameter list dynamically allocated */
602 char **p; /* parameter list */
603 int optind; /* next parameter to be processed by getopts */
604 int optoff; /* used by getopts */
Eric Andersen2870d962001-07-02 17:27:21 +0000605};
606
Eric Andersen62483552001-07-10 06:09:16 +0000607/*
608 * When commands are first encountered, they are entered in a hash table.
609 * This ensures that a full path search will not have to be done for them
610 * on each invocation.
611 *
612 * We should investigate converting to a linear search, even though that
613 * would make the command name "hash" a misnomer.
614 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000615#define CMDTABLESIZE 31 /* should be prime */
616#define ARB 1 /* actual size determined at run time */
Eric Andersen62483552001-07-10 06:09:16 +0000617
618
619
620struct tblentry {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000621 struct tblentry *next; /* next entry in hash chain */
622 union param param; /* definition of builtin function */
623 short cmdtype; /* index identifying command */
624 char rehash; /* if set, cd done since entry created */
625 char cmdname[ARB]; /* name of command */
Eric Andersen62483552001-07-10 06:09:16 +0000626};
627
628
629static struct tblentry *cmdtable[CMDTABLESIZE];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000630static int builtinloc = -1; /* index in path of %builtin, or -1 */
631static int exerrno = 0; /* Last exec error */
Eric Andersen62483552001-07-10 06:09:16 +0000632
633
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000634static void tryexec(char *, char **, char **);
635static void printentry(struct tblentry *, int);
636static void clearcmdentry(int);
637static struct tblentry *cmdlookup(const char *, int);
638static void delete_cmd_entry(void);
639static int path_change(const char *, int *);
Eric Andersen62483552001-07-10 06:09:16 +0000640
641
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000642static void flushall(void);
643static void out2fmt(const char *, ...)
644 __attribute__ ((__format__(__printf__, 1, 2)));
645static int xwrite(int, const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +0000646
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000647static inline void outstr(const char *p, FILE * file)
648{
649 fputs(p, file);
650}
651static void out1str(const char *p)
652{
653 outstr(p, stdout);
654}
655static void out2str(const char *p)
656{
657 outstr(p, stderr);
658}
Eric Andersen2870d962001-07-02 17:27:21 +0000659
Eric Andersend35c5df2002-01-09 15:37:36 +0000660#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen3102ac42001-07-06 04:26:23 +0000661#define out2c(c) putc((c), stderr)
Eric Andersen62483552001-07-10 06:09:16 +0000662#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000663static void out2c(int c)
664{
665 putc(c, stderr);
666}
Eric Andersen62483552001-07-10 06:09:16 +0000667#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000668
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000669
Eric Andersend35c5df2002-01-09 15:37:36 +0000670#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000671#define USE_SIT_FUNCTION
672#endif
673
674/* number syntax index */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000675#define BASESYNTAX 0 /* not in quotes */
676#define DQSYNTAX 1 /* in double quotes */
677#define SQSYNTAX 2 /* in single quotes */
678#define ARISYNTAX 3 /* in arithmetic */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000679
680static const char S_I_T[][4] = {
Glenn L McGrath50812ff2002-08-23 13:14:48 +0000681 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
682 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
683 {CNL, CNL, CNL, CNL}, /* 2, \n */
684 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
685 {CDQUOTE, CENDQUOTE, CWORD, CDQUOTE}, /* 4, '"' */
686 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
687 {CSQUOTE, CWORD, CENDQUOTE, CSQUOTE}, /* 6, "'" */
688 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
689 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
690 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
691 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
692 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000693#ifndef USE_SIT_FUNCTION
Glenn L McGrath50812ff2002-08-23 13:14:48 +0000694 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
695 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
696 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000697#endif
Eric Andersen2870d962001-07-02 17:27:21 +0000698};
699
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000700#ifdef USE_SIT_FUNCTION
701
702#define U_C(c) ((unsigned char)(c))
703
704static int SIT(int c, int syntax)
705{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000706 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
707 static const char syntax_index_table[] = {
708 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
709 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
710 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
711 11, 3
712 }; /* "}~" */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000713 const char *s;
714 int indx;
715
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000716 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000717 return CENDFILE;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000718 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000719 indx = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000720 else if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000721 return CCTL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000722 else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000723 s = strchr(spec_symbls, c);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000724 if (s == 0)
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000725 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000726 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000727 }
728 return S_I_T[indx][syntax];
729}
730
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000731#else /* USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +0000732
733#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
734
735#define CSPCL_CIGN_CIGN_CIGN 0
736#define CSPCL_CWORD_CWORD_CWORD 1
737#define CNL_CNL_CNL_CNL 2
738#define CWORD_CCTL_CCTL_CWORD 3
739#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE 4
740#define CVAR_CVAR_CWORD_CVAR 5
741#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE 6
742#define CSPCL_CWORD_CWORD_CLP 7
743#define CSPCL_CWORD_CWORD_CRP 8
744#define CBACK_CBACK_CCTL_CBACK 9
745#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
746#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
747#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
748#define CWORD_CWORD_CWORD_CWORD 13
749#define CCTL_CCTL_CCTL_CCTL 14
750
751static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000752 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
753 /* 0 -130 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
754 /* 1 -129 PEOA */ CSPCL_CIGN_CIGN_CIGN,
755 /* 2 -128 0xff */ CWORD_CWORD_CWORD_CWORD,
Glenn L McGrath50812ff2002-08-23 13:14:48 +0000756 /* 3 -127 */ CCTL_CCTL_CCTL_CCTL,
757 /* CTLQUOTEMARK */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000758 /* 4 -126 */ CCTL_CCTL_CCTL_CCTL,
759 /* 5 -125 */ CCTL_CCTL_CCTL_CCTL,
760 /* 6 -124 */ CCTL_CCTL_CCTL_CCTL,
761 /* 7 -123 */ CCTL_CCTL_CCTL_CCTL,
762 /* 8 -122 */ CCTL_CCTL_CCTL_CCTL,
763 /* 9 -121 */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath50812ff2002-08-23 13:14:48 +0000764 /* 10 -120 */ CCTL_CCTL_CCTL_CCTL,
765 /* CTLESC */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000766 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
767 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
768 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
769 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
770 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
771 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
772 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
773 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
774 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
775 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
776 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
777 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
778 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
779 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
780 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
781 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
782 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
783 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
784 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
785 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
786 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
787 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
788 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
789 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
790 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
791 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
792 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
793 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
794 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
795 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
796 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
797 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
798 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
799 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
800 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
801 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
802 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
803 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
804 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
805 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
806 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
807 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
808 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
809 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
810 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
811 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
812 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
813 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
814 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
815 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
816 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
817 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
818 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
819 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
820 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
821 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
822 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
823 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
824 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
825 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
826 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
827 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
828 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
829 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
830 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
831 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
832 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
833 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
834 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
835 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
836 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
837 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
838 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
839 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
840 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
841 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
842 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
843 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
844 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
845 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
846 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
847 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
848 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
849 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
850 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
851 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
852 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
853 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
854 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
855 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
856 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
857 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
858 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
859 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
860 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
861 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
862 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
863 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
864 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
865 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
866 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
867 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
868 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
869 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
870 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
871 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
872 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
873 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
874 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
875 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
876 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
877 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
878 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
879 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
880 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
881 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
882 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
883 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
884 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
885 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
886 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
887 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
888 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
889 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
890 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
891 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
892 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
893 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
894 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
895 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
896 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
897 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
898 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
899 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
900 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
901 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
902 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
903 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
904 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
905 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
906 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
907 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
908 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
909 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
910 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
911 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
912 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
913 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
914 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
915 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
916 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
917 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
918 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
919 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CDQUOTE,
920 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
921 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
922 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
923 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
924 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CSQUOTE,
925 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
926 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
927 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
928 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
929 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
930 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
931 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
932 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
933 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
934 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
935 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
936 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
937 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
938 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
939 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
940 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
941 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
942 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
943 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
944 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
945 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
946 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
947 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
948 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
949 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
950 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
951 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
952 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
953 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
954 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
955 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
956 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
957 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
958 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
959 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
960 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
961 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
962 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
963 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
964 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
965 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
966 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
967 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
968 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
969 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
970 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
971 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
972 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
973 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
974 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
975 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
976 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
977 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
978 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
979 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
980 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
981 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
982 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
983 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
984 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
985 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
986 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
987 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
988 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
989 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
990 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
991 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
992 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
993 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
994 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
995 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
996 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
997 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
998 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
999 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
1000 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
1001 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
1002 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
1003 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
1004 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
1005 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
1006 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
1007 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
1008 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
1009 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
1010 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
1011 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
1012 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00001013};
1014
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001015#endif /* USE_SIT_FUNCTION */
Eric Andersen2870d962001-07-02 17:27:21 +00001016
Eric Andersen2870d962001-07-02 17:27:21 +00001017
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001018/* first char is indicating which tokens mark the end of a list */
1019static const char *const tokname_array[] = {
1020 "\1end of file",
1021 "\0newline",
1022 "\0redirection",
1023 "\0word",
1024 "\0assignment",
1025 "\0;",
1026 "\0&",
1027 "\0&&",
1028 "\0||",
1029 "\0|",
1030 "\0(",
1031 "\1)",
1032 "\1;;",
1033 "\1`",
Eric Andersen2870d962001-07-02 17:27:21 +00001034#define KWDOFFSET 14
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001035 /* the following are keywords */
1036 "\0!",
1037 "\0case",
1038 "\1do",
1039 "\1done",
1040 "\1elif",
1041 "\1else",
1042 "\1esac",
1043 "\1fi",
1044 "\0for",
1045 "\0if",
1046 "\0in",
1047 "\1then",
1048 "\0until",
1049 "\0while",
1050 "\0{",
1051 "\1}",
Eric Andersen2870d962001-07-02 17:27:21 +00001052};
1053
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001054static const char *tokname(int tok)
1055{
1056 static char buf[16];
1057
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001058 if (tok >= TSEMI)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001059 buf[0] = '"';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001060 sprintf(buf + (tok >= TSEMI), "%s%c",
1061 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001062 return buf;
1063}
Eric Andersen2870d962001-07-02 17:27:21 +00001064
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001065static int plinno = 1; /* input line number */
Eric Andersen2870d962001-07-02 17:27:21 +00001066
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001067static int parselleft; /* copy of parsefile->lleft */
Eric Andersen2870d962001-07-02 17:27:21 +00001068
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001069static struct parsefile basepf; /* top level input file */
1070static char basebuf[BUFSIZ]; /* buffer for top level input file */
1071static struct parsefile *parsefile = &basepf; /* current input file */
Eric Andersen2870d962001-07-02 17:27:21 +00001072
1073/*
1074 * NEOF is returned by parsecmd when it encounters an end of file. It
1075 * must be distinct from NULL, so we use the address of a variable that
1076 * happens to be handy.
1077 */
1078
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001079static int tokpushback; /* last token pushed back */
1080
Eric Andersen2870d962001-07-02 17:27:21 +00001081#define NEOF ((union node *)&tokpushback)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001082static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
Eric Andersen2870d962001-07-02 17:27:21 +00001083
1084
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001085static void error(const char *, ...) __attribute__ ((__noreturn__));
1086static void exerror(int, const char *, ...) __attribute__ ((__noreturn__));
1087static void shellexec(char **, char **, const char *, int)
1088 __attribute__ ((noreturn));
1089static void exitshell(int) __attribute__ ((noreturn));
Eric Andersen2870d962001-07-02 17:27:21 +00001090
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001091static int goodname(const char *);
1092static void ignoresig(int);
1093static void onsig(int);
1094static void dotrap(void);
1095static int decode_signal(const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001096
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001097static void setparam(char **);
1098static void freeparam(volatile struct shparam *);
Eric Andersen2870d962001-07-02 17:27:21 +00001099
1100/* reasons for skipping commands (see comment on breakcmd routine) */
1101#define SKIPBREAK 1
1102#define SKIPCONT 2
1103#define SKIPFUNC 3
1104#define SKIPFILE 4
1105
1106/* values of cmdtype */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001107#define CMDUNKNOWN -1 /* no entry in table for command */
1108#define CMDNORMAL 0 /* command is an executable program */
1109#define CMDBUILTIN 1 /* command is a shell builtin */
1110#define CMDFUNCTION 2 /* command is a shell function */
Eric Andersen2870d962001-07-02 17:27:21 +00001111
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001112#define DO_ERR 1 /* find_command prints errors */
1113#define DO_ABS 2 /* find_command checks absolute paths */
1114#define DO_NOFUN 4 /* find_command ignores functions */
1115#define DO_BRUTE 8 /* find_command ignores hash table */
Eric Andersen2870d962001-07-02 17:27:21 +00001116
1117/*
1118 * Shell variables.
1119 */
1120
1121/* flags */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001122#define VEXPORT 0x01 /* variable is exported */
1123#define VREADONLY 0x02 /* variable cannot be modified */
1124#define VSTRFIXED 0x04 /* variable struct is staticly allocated */
1125#define VTEXTFIXED 0x08 /* text is staticly allocated */
1126#define VSTACK 0x10 /* text is allocated on the stack */
1127#define VUNSET 0x20 /* the variable is not set */
1128#define VNOFUNC 0x40 /* don't call the callback function */
Eric Andersen2870d962001-07-02 17:27:21 +00001129
1130
1131struct var {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001132 struct var *next; /* next entry in hash list */
1133 int flags; /* flags are defined above */
1134 char *text; /* name=value */
Eric Andersen2870d962001-07-02 17:27:21 +00001135 void (*func) (const char *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001136 /* function to be called when */
1137 /* the variable gets set/unset */
Eric Andersen2870d962001-07-02 17:27:21 +00001138};
1139
1140struct localvar {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001141 struct localvar *next; /* next local variable in list */
1142 struct var *vp; /* the variable that was made local */
1143 int flags; /* saved flags */
1144 char *text; /* saved text */
Eric Andersen2870d962001-07-02 17:27:21 +00001145};
1146
1147
Eric Andersen62483552001-07-10 06:09:16 +00001148#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00001149#define rmescapes(p) _rmescapes((p), 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001150static char *_rmescapes(char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001151#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001152static void rmescapes(char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001153#endif
1154
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001155static int casematch(union node *, const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001156static void clearredir(void);
1157static void popstring(void);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001158static void readcmdfile(const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001159
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001160static int number(const char *);
1161static int is_number(const char *, int *num);
1162static char *single_quote(const char *);
1163static int nextopt(const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001164
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001165static void redirect(union node *, int);
1166static void popredir(void);
1167static int dup_as_newfd(int, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001168
1169static void changepath(const char *newval);
1170static void getoptsreset(const char *value);
1171
1172
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001173static int parsenleft; /* copy of parsefile->nleft */
1174static char *parsenextc; /* copy of parsefile->nextc */
1175static int rootpid; /* pid of main shell */
1176static int rootshell; /* true if we aren't a child of the main shell */
Eric Andersen2870d962001-07-02 17:27:21 +00001177
1178static const char spcstr[] = " ";
1179static const char snlfmt[] = "%s\n";
1180
1181static int sstrnleft;
1182static int herefd = -1;
1183
1184static struct localvar *localvars;
1185
1186static struct var vifs;
1187static struct var vmail;
1188static struct var vmpath;
1189static struct var vpath;
1190static struct var vps1;
1191static struct var vps2;
1192static struct var voptind;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001193
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001194#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00001195static struct var vlc_all;
1196static struct var vlc_ctype;
1197#endif
1198
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001199#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1200static struct var vhistfile;
1201#endif
1202
Eric Andersen2870d962001-07-02 17:27:21 +00001203struct varinit {
1204 struct var *var;
1205 int flags;
1206 const char *text;
1207 void (*func) (const char *);
1208};
1209
1210static const char defpathvar[] =
1211 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
1212#define defpath (defpathvar + 5)
1213
1214#ifdef IFS_BROKEN
1215static const char defifsvar[] = "IFS= \t\n";
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001216
Eric Andersen2870d962001-07-02 17:27:21 +00001217#define defifs (defifsvar + 4)
1218#else
1219static const char defifs[] = " \t\n";
1220#endif
1221
1222static const struct varinit varinit[] = {
1223#ifdef IFS_BROKEN
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001224 {&vifs, VSTRFIXED | VTEXTFIXED, defifsvar,
Eric Andersen2870d962001-07-02 17:27:21 +00001225#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001226 {&vifs, VSTRFIXED | VTEXTFIXED | VUNSET, "IFS=",
Eric Andersen2870d962001-07-02 17:27:21 +00001227#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001228 NULL},
1229 {&vmail, VSTRFIXED | VTEXTFIXED | VUNSET, "MAIL=",
1230 NULL},
1231 {&vmpath, VSTRFIXED | VTEXTFIXED | VUNSET, "MAILPATH=",
1232 NULL},
1233 {&vpath, VSTRFIXED | VTEXTFIXED, defpathvar,
1234 changepath},
Tim Riker497a8852002-04-13 05:37:10 +00001235#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001236 {&vps1, VSTRFIXED | VTEXTFIXED, "PS1=\\w \\$ ",
1237 NULL},
1238#endif /* else vps1 depends on uid */
1239 {&vps2, VSTRFIXED | VTEXTFIXED, "PS2=> ",
1240 NULL},
1241 {&voptind, VSTRFIXED | VTEXTFIXED, "OPTIND=1",
1242 getoptsreset},
Eric Andersenbdfd0d72001-10-24 05:00:29 +00001243#ifdef CONFIG_LOCALE_SUPPORT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001244 {&vlc_all, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=",
1245 change_lc_all},
1246 {&vlc_ctype, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=",
1247 change_lc_ctype},
Eric Andersen2870d962001-07-02 17:27:21 +00001248#endif
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001249#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1250 {&vhistfile, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=",
1251 NULL},
1252#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001253 {NULL, 0, NULL,
1254 NULL}
Eric Andersen2870d962001-07-02 17:27:21 +00001255};
1256
1257#define VTABSIZE 39
1258
1259static struct var *vartab[VTABSIZE];
1260
1261/*
1262 * The following macros access the values of the above variables.
1263 * They have to skip over the name. They return the null string
1264 * for unset variables.
1265 */
1266
1267#define ifsval() (vifs.text + 4)
1268#define ifsset() ((vifs.flags & VUNSET) == 0)
1269#define mailval() (vmail.text + 5)
1270#define mpathval() (vmpath.text + 9)
1271#define pathval() (vpath.text + 5)
1272#define ps1val() (vps1.text + 4)
1273#define ps2val() (vps2.text + 4)
1274#define optindval() (voptind.text + 7)
1275
1276#define mpathset() ((vmpath.flags & VUNSET) == 0)
1277
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001278static void initvar(void);
1279static void setvar(const char *, const char *, int);
1280static void setvareq(char *, int);
1281static void listsetvar(struct strlist *);
1282static const char *lookupvar(const char *);
1283static const char *bltinlookup(const char *);
1284static char **environment(void);
1285static int showvarscmd(int, char **);
1286static void mklocal(char *);
1287static void poplocalvars(void);
1288static int unsetvar(const char *);
1289static int varequal(const char *, const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001290
1291
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001292static char *arg0; /* value of $0 */
1293static struct shparam shellparam; /* current positional parameters */
1294static char **argptr; /* argument list for builtin commands */
1295static char *optionarg; /* set by nextopt (like getopt) */
1296static char *optptr; /* used by nextopt */
1297static char *minusc; /* argument to -c option */
Eric Andersen2870d962001-07-02 17:27:21 +00001298
1299
Eric Andersend35c5df2002-01-09 15:37:36 +00001300#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00001301
1302#define ALIASINUSE 1
1303#define ALIASDEAD 2
1304
Eric Andersen3102ac42001-07-06 04:26:23 +00001305#define ATABSIZE 39
1306
Eric Andersen2870d962001-07-02 17:27:21 +00001307struct alias {
1308 struct alias *next;
1309 char *name;
1310 char *val;
1311 int flag;
1312};
1313
1314static struct alias *atab[ATABSIZE];
1315
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001316static void setalias(char *, char *);
1317static struct alias **hashalias(const char *);
1318static struct alias *freealias(struct alias *);
1319static struct alias **__lookupalias(const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00001320
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001321static void setalias(char *name, char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00001322{
1323 struct alias *ap, **app;
1324
1325 app = __lookupalias(name);
1326 ap = *app;
1327 INTOFF;
1328 if (ap) {
1329 if (!(ap->flag & ALIASINUSE)) {
Aaron Lehmann49c024a2002-08-02 06:39:47 +00001330 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00001331 }
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001332 ap->val = xstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00001333 ap->flag &= ~ALIASDEAD;
1334 } else {
1335 /* not found */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001336 ap = xmalloc(sizeof(struct alias));
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001337 ap->name = xstrdup(name);
1338 ap->val = xstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00001339 ap->flag = 0;
1340 ap->next = 0;
1341 *app = ap;
1342 }
1343 INTON;
1344}
1345
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001346static int unalias(char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00001347{
Eric Andersencb57d552001-06-28 07:25:16 +00001348 struct alias **app;
1349
1350 app = __lookupalias(name);
1351
1352 if (*app) {
1353 INTOFF;
1354 *app = freealias(*app);
1355 INTON;
1356 return (0);
1357 }
1358
1359 return (1);
1360}
1361
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001362static void rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00001363{
Eric Andersencb57d552001-06-28 07:25:16 +00001364 struct alias *ap, **app;
1365 int i;
1366
1367 INTOFF;
1368 for (i = 0; i < ATABSIZE; i++) {
1369 app = &atab[i];
1370 for (ap = *app; ap; ap = *app) {
1371 *app = freealias(*app);
1372 if (ap == *app) {
1373 app = &ap->next;
1374 }
1375 }
1376 }
1377 INTON;
1378}
1379
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001380static void printalias(const struct alias *ap)
1381{
Eric Andersen2870d962001-07-02 17:27:21 +00001382 char *p;
1383
1384 p = single_quote(ap->val);
Eric Andersen62483552001-07-10 06:09:16 +00001385 printf("alias %s=%s\n", ap->name, p);
Eric Andersen2870d962001-07-02 17:27:21 +00001386 stunalloc(p);
1387}
1388
Eric Andersencb57d552001-06-28 07:25:16 +00001389
1390/*
1391 * TODO - sort output
1392 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001393static int aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001394{
1395 char *n, *v;
1396 int ret = 0;
1397 struct alias *ap;
1398
1399 if (argc == 1) {
1400 int i;
1401
1402 for (i = 0; i < ATABSIZE; i++)
1403 for (ap = atab[i]; ap; ap = ap->next) {
1404 printalias(ap);
1405 }
1406 return (0);
1407 }
1408 while ((n = *++argv) != NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001409 if ((v = strchr(n + 1, '=')) == NULL) { /* n+1: funny ksh stuff */
Eric Andersencb57d552001-06-28 07:25:16 +00001410 if ((ap = *__lookupalias(n)) == NULL) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001411 out2fmt("%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00001412 ret = 1;
1413 } else
1414 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001415 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00001416 *v++ = '\0';
1417 setalias(n, v);
1418 }
1419 }
1420
1421 return (ret);
1422}
1423
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001424static int unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001425{
1426 int i;
1427
1428 while ((i = nextopt("a")) != '\0') {
1429 if (i == 'a') {
1430 rmaliases();
1431 return (0);
1432 }
1433 }
1434 for (i = 0; *argptr; argptr++) {
1435 if (unalias(*argptr)) {
Eric Andersen3102ac42001-07-06 04:26:23 +00001436 out2fmt("%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00001437 i = 1;
1438 }
1439 }
1440
1441 return (i);
1442}
1443
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001444static struct alias **hashalias(const char *p)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001445{
Eric Andersencb57d552001-06-28 07:25:16 +00001446 unsigned int hashval;
1447
1448 hashval = *p << 4;
1449 while (*p)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001450 hashval += *p++;
Eric Andersencb57d552001-06-28 07:25:16 +00001451 return &atab[hashval % ATABSIZE];
1452}
1453
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001454static struct alias *freealias(struct alias *ap)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001455{
Eric Andersencb57d552001-06-28 07:25:16 +00001456 struct alias *next;
1457
1458 if (ap->flag & ALIASINUSE) {
1459 ap->flag |= ALIASDEAD;
1460 return ap;
1461 }
1462
1463 next = ap->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00001464 free(ap->name);
1465 free(ap->val);
1466 free(ap);
Eric Andersencb57d552001-06-28 07:25:16 +00001467 return next;
1468}
1469
Eric Andersencb57d552001-06-28 07:25:16 +00001470
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001471static struct alias **__lookupalias(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001472{
Eric Andersencb57d552001-06-28 07:25:16 +00001473 struct alias **app = hashalias(name);
1474
1475 for (; *app; app = &(*app)->next) {
1476 if (equal(name, (*app)->name)) {
1477 break;
1478 }
1479 }
1480
1481 return app;
1482}
Eric Andersen2870d962001-07-02 17:27:21 +00001483#endif
Eric Andersencb57d552001-06-28 07:25:16 +00001484
Eric Andersend35c5df2002-01-09 15:37:36 +00001485#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersen74bcd162001-07-30 21:41:37 +00001486/* The generated file arith.c has been replaced with a custom hand
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001487 * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
1488 * This is now part of libbb, so that it can be used by all the shells
Eric Andersen74bcd162001-07-30 21:41:37 +00001489 * in busybox. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001490static void expari(int);
Eric Andersencb57d552001-06-28 07:25:16 +00001491#endif
1492
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001493static char *trap[NSIG]; /* trap handler commands */
1494static char sigmode[NSIG - 1]; /* current value of signal */
1495static char gotsig[NSIG - 1]; /* indicates specified signal received */
1496static int pendingsigs; /* indicates some signal received */
Eric Andersen2870d962001-07-02 17:27:21 +00001497
Eric Andersencb57d552001-06-28 07:25:16 +00001498/*
1499 * This file was generated by the mkbuiltins program.
1500 */
1501
Eric Andersend35c5df2002-01-09 15:37:36 +00001502#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001503static int bgcmd(int, char **);
1504static int fgcmd(int, char **);
1505static int killcmd(int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001506#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001507static int bltincmd(int, char **);
1508static int cdcmd(int, char **);
1509static int breakcmd(int, char **);
1510
Eric Andersend35c5df2002-01-09 15:37:36 +00001511#ifdef CONFIG_ASH_CMDCMD
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001512static int commandcmd(int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001513#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001514static int dotcmd(int, char **);
1515static int evalcmd(int, char **);
1516static int execcmd(int, char **);
1517static int exitcmd(int, char **);
1518static int exportcmd(int, char **);
1519static int histcmd(int, char **);
1520static int hashcmd(int, char **);
1521static int helpcmd(int, char **);
1522static int jobscmd(int, char **);
1523static int localcmd(int, char **);
1524static int pwdcmd(int, char **);
1525static int readcmd(int, char **);
1526static int returncmd(int, char **);
1527static int setcmd(int, char **);
1528static int setvarcmd(int, char **);
1529static int shiftcmd(int, char **);
1530static int trapcmd(int, char **);
1531static int umaskcmd(int, char **);
1532
Eric Andersend35c5df2002-01-09 15:37:36 +00001533#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001534static int aliascmd(int, char **);
1535static int unaliascmd(int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001536#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001537static int unsetcmd(int, char **);
1538static int waitcmd(int, char **);
1539static int ulimitcmd(int, char **);
1540static int timescmd(int, char **);
1541
Eric Andersend35c5df2002-01-09 15:37:36 +00001542#ifdef CONFIG_ASH_MATH_SUPPORT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001543static int letcmd(int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001544#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001545static int typecmd(int, char **);
1546
Eric Andersend35c5df2002-01-09 15:37:36 +00001547#ifdef CONFIG_ASH_GETOPTS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001548static int getoptscmd(int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00001549#endif
1550
Eric Andersen69a20f02001-10-31 10:40:37 +00001551#ifndef CONFIG_TRUE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001552static int true_main(int, char **);
Eric Andersen69a20f02001-10-31 10:40:37 +00001553#endif
1554#ifndef CONFIG_FALSE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001555static int false_main(int, char **);
Eric Andersen2870d962001-07-02 17:27:21 +00001556#endif
1557
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001558static void setpwd(const char *, int);
Eric Andersen2870d962001-07-02 17:27:21 +00001559
1560
1561#define BUILTIN_NOSPEC "0"
1562#define BUILTIN_SPECIAL "1"
1563#define BUILTIN_REGULAR "2"
1564#define BUILTIN_ASSIGN "4"
1565#define BUILTIN_SPEC_ASSG "5"
1566#define BUILTIN_REG_ASSG "6"
1567
1568#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1569#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1570#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
1571
1572struct builtincmd {
1573 const char *name;
1574 int (*const builtinfunc) (int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001575 //unsigned flags;
Eric Andersen2870d962001-07-02 17:27:21 +00001576};
1577
Eric Andersencb57d552001-06-28 07:25:16 +00001578
1579/* It is CRUCIAL that this listing be kept in ascii order, otherwise
1580 * the binary search in find_builtin() will stop working. If you value
1581 * your kneecaps, you'll be sure to *make sure* that any changes made
1582 * to this array result in the listing remaining in ascii order. You
1583 * have been warned.
1584 */
1585static const struct builtincmd builtincmds[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001586 {BUILTIN_SPECIAL ".", dotcmd}, /* first, see declare DOTCMD */
1587 {BUILTIN_SPECIAL ":", true_main},
Eric Andersend35c5df2002-01-09 15:37:36 +00001588#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001589 {BUILTIN_REG_ASSG "alias", aliascmd},
Eric Andersencb57d552001-06-28 07:25:16 +00001590#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001591#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001592 {BUILTIN_REGULAR "bg", bgcmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001593#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001594 {BUILTIN_SPECIAL "break", breakcmd},
1595 {BUILTIN_SPECIAL "builtin", bltincmd},
1596 {BUILTIN_REGULAR "cd", cdcmd},
1597 {BUILTIN_NOSPEC "chdir", cdcmd},
Eric Andersend35c5df2002-01-09 15:37:36 +00001598#ifdef CONFIG_ASH_CMDCMD
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001599 {BUILTIN_REGULAR "command", commandcmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001600#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001601 {BUILTIN_SPECIAL "continue", breakcmd},
1602 {BUILTIN_SPECIAL "eval", evalcmd},
1603 {BUILTIN_SPECIAL "exec", execcmd},
1604 {BUILTIN_SPECIAL "exit", exitcmd},
1605 {BUILTIN_SPEC_ASSG "export", exportcmd},
1606 {BUILTIN_REGULAR "false", false_main},
1607 {BUILTIN_REGULAR "fc", histcmd},
Eric Andersend35c5df2002-01-09 15:37:36 +00001608#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001609 {BUILTIN_REGULAR "fg", fgcmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001610#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001611#ifdef CONFIG_ASH_GETOPTS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001612 {BUILTIN_REGULAR "getopts", getoptscmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001613#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001614 {BUILTIN_NOSPEC "hash", hashcmd},
1615 {BUILTIN_NOSPEC "help", helpcmd},
1616 {BUILTIN_REGULAR "jobs", jobscmd},
Eric Andersend35c5df2002-01-09 15:37:36 +00001617#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001618 {BUILTIN_REGULAR "kill", killcmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001619#endif
Eric Andersend35c5df2002-01-09 15:37:36 +00001620#ifdef CONFIG_ASH_MATH_SUPPORT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001621 {BUILTIN_REGULAR "let", letcmd},
Eric Andersencb57d552001-06-28 07:25:16 +00001622#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001623 {BUILTIN_ASSIGN "local", localcmd},
1624 {BUILTIN_NOSPEC "pwd", pwdcmd},
1625 {BUILTIN_REGULAR "read", readcmd},
1626 {BUILTIN_SPEC_ASSG "readonly", exportcmd},
1627 {BUILTIN_SPECIAL "return", returncmd},
1628 {BUILTIN_SPECIAL "set", setcmd},
1629 {BUILTIN_NOSPEC "setvar", setvarcmd},
1630 {BUILTIN_SPECIAL "shift", shiftcmd},
1631 {BUILTIN_SPECIAL "times", timescmd},
1632 {BUILTIN_SPECIAL "trap", trapcmd},
1633 {BUILTIN_REGULAR "true", true_main},
1634 {BUILTIN_NOSPEC "type", typecmd},
1635 {BUILTIN_NOSPEC "ulimit", ulimitcmd},
1636 {BUILTIN_REGULAR "umask", umaskcmd},
Eric Andersend35c5df2002-01-09 15:37:36 +00001637#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001638 {BUILTIN_REGULAR "unalias", unaliascmd},
Eric Andersen2870d962001-07-02 17:27:21 +00001639#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001640 {BUILTIN_SPECIAL "unset", unsetcmd},
1641 {BUILTIN_REGULAR "wait", waitcmd},
Eric Andersencb57d552001-06-28 07:25:16 +00001642};
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001643
Eric Andersencb57d552001-06-28 07:25:16 +00001644#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
1645
Manuel Novoa III 16815d42001-08-10 19:36:07 +00001646#define DOTCMD &builtincmds[0]
Eric Andersen2870d962001-07-02 17:27:21 +00001647static struct builtincmd *BLTINCMD;
1648static struct builtincmd *EXECCMD;
1649static struct builtincmd *EVALCMD;
Eric Andersencb57d552001-06-28 07:25:16 +00001650
Eric Andersen2870d962001-07-02 17:27:21 +00001651/* states */
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001652#define JOBSTOPPED 1 /* all procs are stopped */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001653#define JOBDONE 2 /* all procs are completed */
Eric Andersencb57d552001-06-28 07:25:16 +00001654
Eric Andersen2870d962001-07-02 17:27:21 +00001655/*
1656 * A job structure contains information about a job. A job is either a
1657 * single process or a set of processes contained in a pipeline. In the
1658 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1659 * array of pids.
1660 */
Eric Andersencb57d552001-06-28 07:25:16 +00001661
Eric Andersen2870d962001-07-02 17:27:21 +00001662struct procstat {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001663 pid_t pid; /* process id */
1664 int status; /* status flags (defined above) */
1665 char *cmd; /* text of command being run */
Eric Andersen2870d962001-07-02 17:27:21 +00001666};
Eric Andersencb57d552001-06-28 07:25:16 +00001667
Eric Andersen2870d962001-07-02 17:27:21 +00001668
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001669static int job_warning; /* user was warned about stopped jobs */
Eric Andersen2870d962001-07-02 17:27:21 +00001670
Eric Andersend35c5df2002-01-09 15:37:36 +00001671#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00001672static void setjobctl(int enable);
1673#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001674#define setjobctl(on) /* do nothing */
Eric Andersencb57d552001-06-28 07:25:16 +00001675#endif
1676
Eric Andersen2870d962001-07-02 17:27:21 +00001677
1678struct job {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001679 struct procstat ps0; /* status of process */
1680 struct procstat *ps; /* status or processes when more than one */
1681 short nprocs; /* number of processes */
1682 short pgrp; /* process group of this job */
1683 char state; /* true if job is finished */
1684 char used; /* true if this entry is in used */
1685 char changed; /* true if status has changed */
Eric Andersend35c5df2002-01-09 15:37:36 +00001686#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001687 char jobctl; /* job running under job control */
Eric Andersen2870d962001-07-02 17:27:21 +00001688#endif
1689};
1690
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001691static struct job *jobtab; /* array of jobs */
1692static int njobs; /* size of array */
1693static int backgndpid = -1; /* pid of last background process */
1694
Eric Andersend35c5df2002-01-09 15:37:36 +00001695#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001696static int initialpgrp; /* pgrp of shell on invocation */
1697static int curjob; /* current job */
Eric Andersen2870d962001-07-02 17:27:21 +00001698static int jobctl;
1699#endif
Eric Andersen2870d962001-07-02 17:27:21 +00001700
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001701static struct job *makejob(const union node *, int);
1702static int forkshell(struct job *, const union node *, int);
1703static int waitforjob(struct job *);
Eric Andersen2870d962001-07-02 17:27:21 +00001704
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001705static int docd(char *, int);
1706static void getpwd(void);
Eric Andersen2870d962001-07-02 17:27:21 +00001707
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001708static char *padvance(const char **, const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00001709
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001710static char nullstr[1]; /* zero length string */
1711static char *curdir = nullstr; /* current working directory */
Eric Andersen2870d962001-07-02 17:27:21 +00001712
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001713static int cdcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001714{
1715 const char *dest;
1716 const char *path;
1717 char *p;
1718 struct stat statb;
1719 int print = 0;
1720
1721 nextopt(nullstr);
1722 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
1723 error("HOME not set");
1724 if (*dest == '\0')
Eric Andersen2870d962001-07-02 17:27:21 +00001725 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001726 if (dest[0] == '-' && dest[1] == '\0') {
1727 dest = bltinlookup("OLDPWD");
1728 if (!dest || !*dest) {
1729 dest = curdir;
1730 }
1731 print = 1;
1732 if (dest)
Eric Andersen2870d962001-07-02 17:27:21 +00001733 print = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00001734 else
Eric Andersen2870d962001-07-02 17:27:21 +00001735 dest = ".";
Eric Andersencb57d552001-06-28 07:25:16 +00001736 }
1737 if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
1738 path = nullstr;
1739 while ((p = padvance(&path, dest)) != NULL) {
1740 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
1741 if (!print) {
1742 /*
1743 * XXX - rethink
1744 */
1745 if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
1746 p += 2;
1747 print = strcmp(p, dest);
1748 }
1749 if (docd(p, print) >= 0)
1750 return 0;
1751
1752 }
1753 }
1754 error("can't cd to %s", dest);
1755 /* NOTREACHED */
1756}
1757
1758
1759/*
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001760 * Update curdir (the name of the current directory) in response to a
1761 * cd command. We also call hashcd to let the routines in exec.c know
1762 * that the current directory has changed.
1763 */
1764
1765static void hashcd(void);
1766
1767static inline void updatepwd(const char *dir)
1768{
1769 hashcd(); /* update command hash table */
1770
1771 /*
1772 * If our argument is NULL, we don't know the current directory
1773 */
1774 if (dir == NULL || curdir == nullstr) {
1775 setpwd(0, 1);
1776 } else {
1777 setpwd(dir, 1);
1778 }
1779}
1780
1781/*
Eric Andersencb57d552001-06-28 07:25:16 +00001782 * Actually do the chdir. In an interactive shell, print the
1783 * directory name if "print" is nonzero.
1784 */
1785
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001786static int docd(char *dest, int print)
Eric Andersencb57d552001-06-28 07:25:16 +00001787{
Eric Andersencb57d552001-06-28 07:25:16 +00001788 TRACE(("docd(\"%s\", %d) called\n", dest, print));
Eric Andersencb57d552001-06-28 07:25:16 +00001789 INTOFF;
1790 if (chdir(dest) < 0) {
1791 INTON;
1792 return -1;
1793 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001794 updatepwd(dest);
Eric Andersencb57d552001-06-28 07:25:16 +00001795 INTON;
1796 if (print && iflag)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001797 puts(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001798 return 0;
1799}
1800
1801
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001802static int pwdcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00001803{
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00001804 puts(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001805 return 0;
1806}
Eric Andersencb57d552001-06-28 07:25:16 +00001807
Eric Andersena3483db2001-10-24 08:01:06 +00001808/* Ask system the current directory */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001809static void getpwd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00001810{
Eric Andersen2870d962001-07-02 17:27:21 +00001811 curdir = xgetcwd(0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001812 if (curdir == 0)
Eric Andersen2870d962001-07-02 17:27:21 +00001813 curdir = nullstr;
Eric Andersencb57d552001-06-28 07:25:16 +00001814}
1815
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001816static void setpwd(const char *val, int setold)
Eric Andersencb57d552001-06-28 07:25:16 +00001817{
Eric Andersena3483db2001-10-24 08:01:06 +00001818 char *cated = NULL;
1819
Eric Andersencb57d552001-06-28 07:25:16 +00001820 if (setold) {
1821 setvar("OLDPWD", curdir, VEXPORT);
1822 }
1823 INTOFF;
1824 if (curdir != nullstr) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001825 if (val != NULL && *val != '/')
Eric Andersena3483db2001-10-24 08:01:06 +00001826 val = cated = concat_path_file(curdir, val);
Eric Andersencb57d552001-06-28 07:25:16 +00001827 free(curdir);
Eric Andersencb57d552001-06-28 07:25:16 +00001828 }
Eric Andersena3483db2001-10-24 08:01:06 +00001829 if (!val)
Eric Andersencb57d552001-06-28 07:25:16 +00001830 getpwd();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001831 else
Eric Andersena3483db2001-10-24 08:01:06 +00001832 curdir = simplify_path(val);
1833 free(cated);
Eric Andersencb57d552001-06-28 07:25:16 +00001834 INTON;
1835 setvar("PWD", curdir, VEXPORT);
1836}
1837
Eric Andersencb57d552001-06-28 07:25:16 +00001838/*
1839 * Errors and exceptions.
1840 */
1841
1842/*
1843 * Code to handle exceptions in C.
1844 */
1845
Eric Andersen2870d962001-07-02 17:27:21 +00001846/*
1847 * We enclose jmp_buf in a structure so that we can declare pointers to
1848 * jump locations. The global variable handler contains the location to
1849 * jump to when an exception occurs, and the global variable exception
1850 * contains a code identifying the exeception. To implement nested
1851 * exception handlers, the user should save the value of handler on entry
1852 * to an inner scope, set handler to point to a jmploc structure for the
1853 * inner scope, and restore handler on exit from the scope.
1854 */
1855
1856struct jmploc {
1857 jmp_buf loc;
1858};
1859
1860/* exceptions */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001861#define EXINT 0 /* SIGINT received */
1862#define EXERROR 1 /* a generic error */
1863#define EXSHELLPROC 2 /* execute a shell procedure */
1864#define EXEXEC 3 /* command execution failed */
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001865#define EXREDIR 4 /* redirection error */
Eric Andersen2870d962001-07-02 17:27:21 +00001866
1867static struct jmploc *handler;
Eric Andersencb57d552001-06-28 07:25:16 +00001868static int exception;
Eric Andersencb57d552001-06-28 07:25:16 +00001869
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001870static void exverror(int, const char *, va_list)
1871 __attribute__ ((__noreturn__));
Eric Andersencb57d552001-06-28 07:25:16 +00001872
1873/*
1874 * Called to raise an exception. Since C doesn't include exceptions, we
1875 * just do a longjmp to the exception handler. The type of exception is
1876 * stored in the global variable "exception".
1877 */
1878
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001879static void exraise(int) __attribute__ ((__noreturn__));
Eric Andersen2870d962001-07-02 17:27:21 +00001880
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001881static void exraise(int e)
Eric Andersencb57d552001-06-28 07:25:16 +00001882{
1883#ifdef DEBUG
1884 if (handler == NULL)
1885 abort();
1886#endif
Eric Andersen62483552001-07-10 06:09:16 +00001887 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00001888 exception = e;
1889 longjmp(handler->loc, 1);
1890}
1891
1892
1893/*
1894 * Called from trap.c when a SIGINT is received. (If the user specifies
1895 * that SIGINT is to be trapped or ignored using the trap builtin, then
1896 * this routine is not called.) Suppressint is nonzero when interrupts
1897 * are held using the INTOFF macro. The call to _exit is necessary because
1898 * there is a short period after a fork before the signal handlers are
1899 * set to the appropriate value for the child. (The test for iflag is
1900 * just defensive programming.)
1901 */
1902
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001903static void onint(void)
1904{
Eric Andersencb57d552001-06-28 07:25:16 +00001905 sigset_t mysigset;
1906
1907 if (suppressint) {
1908 intpending++;
1909 return;
1910 }
1911 intpending = 0;
1912 sigemptyset(&mysigset);
1913 sigprocmask(SIG_SETMASK, &mysigset, NULL);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001914 if (!(rootshell && iflag)) {
Eric Andersencb57d552001-06-28 07:25:16 +00001915 signal(SIGINT, SIG_DFL);
1916 raise(SIGINT);
1917 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00001918 exraise(EXINT);
Eric Andersencb57d552001-06-28 07:25:16 +00001919 /* NOTREACHED */
1920}
1921
1922
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001923static char *commandname; /* currently executing command */
Eric Andersen2870d962001-07-02 17:27:21 +00001924
Eric Andersencb57d552001-06-28 07:25:16 +00001925/*
1926 * Exverror is called to raise the error exception. If the first argument
1927 * is not NULL then error prints an error message using printf style
1928 * formatting. It then raises the error exception.
1929 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001930static void exverror(int cond, const char *msg, va_list ap)
Eric Andersencb57d552001-06-28 07:25:16 +00001931{
1932 CLEAR_PENDING_INT;
1933 INTOFF;
1934
1935#ifdef DEBUG
1936 if (msg)
1937 TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
1938 else
1939 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
1940#endif
1941 if (msg) {
1942 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +00001943 out2fmt("%s: ", commandname);
1944 vfprintf(stderr, msg, ap);
1945 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00001946 }
Eric Andersencb57d552001-06-28 07:25:16 +00001947 exraise(cond);
1948 /* NOTREACHED */
1949}
1950
1951
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001952static void error(const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00001953{
Eric Andersencb57d552001-06-28 07:25:16 +00001954 va_list ap;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001955
Eric Andersencb57d552001-06-28 07:25:16 +00001956 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00001957 exverror(EXERROR, msg, ap);
1958 /* NOTREACHED */
1959 va_end(ap);
1960}
1961
1962
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001963static void exerror(int cond, const char *msg, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00001964{
Eric Andersencb57d552001-06-28 07:25:16 +00001965 va_list ap;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001966
Eric Andersencb57d552001-06-28 07:25:16 +00001967 va_start(ap, msg);
Eric Andersencb57d552001-06-28 07:25:16 +00001968 exverror(cond, msg, ap);
1969 /* NOTREACHED */
1970 va_end(ap);
1971}
1972
1973
1974
1975/*
1976 * Table of error messages.
1977 */
1978
1979struct errname {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001980 short errcode; /* error number */
1981 short action; /* operation which encountered the error */
Eric Andersencb57d552001-06-28 07:25:16 +00001982};
1983
Eric Andersen2870d962001-07-02 17:27:21 +00001984/*
1985 * Types of operations (passed to the errmsg routine).
1986 */
1987
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001988#define E_OPEN 01 /* opening a file */
1989#define E_CREAT 02 /* creating a file */
1990#define E_EXEC 04 /* executing a program */
Eric Andersencb57d552001-06-28 07:25:16 +00001991
1992#define ALL (E_OPEN|E_CREAT|E_EXEC)
1993
1994static const struct errname errormsg[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00001995 {EINTR, ALL},
1996 {EACCES, ALL},
1997 {EIO, ALL},
1998 {ENOENT, E_OPEN},
1999 {ENOENT, E_CREAT},
2000 {ENOENT, E_EXEC},
2001 {ENOTDIR, E_OPEN},
2002 {ENOTDIR, E_CREAT},
2003 {ENOTDIR, E_EXEC},
2004 {EISDIR, ALL},
2005 {EEXIST, E_CREAT},
Eric Andersen2870d962001-07-02 17:27:21 +00002006#ifdef EMFILE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002007 {EMFILE, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002008#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002009 {ENFILE, ALL},
2010 {ENOSPC, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002011#ifdef EDQUOT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002012 {EDQUOT, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002013#endif
2014#ifdef ENOSR
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002015 {ENOSR, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002016#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002017 {ENXIO, ALL},
2018 {EROFS, ALL},
2019 {ETXTBSY, ALL},
Eric Andersen2870d962001-07-02 17:27:21 +00002020#ifdef EAGAIN
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002021 {EAGAIN, E_EXEC},
Eric Andersencb57d552001-06-28 07:25:16 +00002022#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002023 {ENOMEM, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002024#ifdef ENOLINK
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002025 {ENOLINK, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002026#endif
2027#ifdef EMULTIHOP
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002028 {EMULTIHOP, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002029#endif
2030#ifdef ECOMM
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002031 {ECOMM, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002032#endif
2033#ifdef ESTALE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002034 {ESTALE, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002035#endif
2036#ifdef ETIMEDOUT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002037 {ETIMEDOUT, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002038#endif
2039#ifdef ELOOP
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002040 {ELOOP, ALL},
Eric Andersencb57d552001-06-28 07:25:16 +00002041#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002042 {E2BIG, E_EXEC},
Eric Andersencb57d552001-06-28 07:25:16 +00002043#ifdef ELIBACC
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002044 {ELIBACC, E_EXEC},
Eric Andersencb57d552001-06-28 07:25:16 +00002045#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002046};
2047
Eric Andersen2870d962001-07-02 17:27:21 +00002048#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname))
Eric Andersencb57d552001-06-28 07:25:16 +00002049
2050/*
2051 * Return a string describing an error. The returned string may be a
2052 * pointer to a static buffer that will be overwritten on the next call.
2053 * Action describes the operation that got the error.
2054 */
2055
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002056static const char *errmsg(int e, int action)
Eric Andersencb57d552001-06-28 07:25:16 +00002057{
2058 struct errname const *ep;
2059 static char buf[12];
2060
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002061 for (ep = errormsg; ep < errormsg + ERRNAME_SIZE; ep++) {
Eric Andersencb57d552001-06-28 07:25:16 +00002062 if (ep->errcode == e && (ep->action & action) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00002063 return strerror(e);
Eric Andersencb57d552001-06-28 07:25:16 +00002064 }
Eric Andersen2870d962001-07-02 17:27:21 +00002065
Eric Andersen3102ac42001-07-06 04:26:23 +00002066 snprintf(buf, sizeof buf, "error %d", e);
Eric Andersencb57d552001-06-28 07:25:16 +00002067 return buf;
2068}
2069
2070
Eric Andersend35c5df2002-01-09 15:37:36 +00002071#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002072static void __inton()
2073{
Eric Andersencb57d552001-06-28 07:25:16 +00002074 if (--suppressint == 0 && intpending) {
2075 onint();
2076 }
2077}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002078static void forceinton(void)
2079{
Eric Andersen3102ac42001-07-06 04:26:23 +00002080 suppressint = 0;
2081 if (intpending)
2082 onint();
2083}
Eric Andersencb57d552001-06-28 07:25:16 +00002084#endif
Eric Andersencb57d552001-06-28 07:25:16 +00002085
2086/* flags in argument to evaltree */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002087#define EV_EXIT 01 /* exit after evaluating tree */
2088#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2089#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00002090
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002091static int evalskip; /* set if we are skipping commands */
2092static int skipcount; /* number of levels to skip */
2093static int loopnest; /* current loop nesting level */
2094static int funcnest; /* depth of function calls */
Eric Andersencb57d552001-06-28 07:25:16 +00002095
2096
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002097static struct strlist *cmdenviron; /* environment for builtin command */
2098static int exitstatus; /* exit status of last command */
2099static int oexitstatus; /* saved exit status */
Eric Andersencb57d552001-06-28 07:25:16 +00002100
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002101static void evalsubshell(const union node *, int);
2102static void expredir(union node *);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002103static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002104static void eprintlist(struct strlist *);
Eric Andersencb57d552001-06-28 07:25:16 +00002105
Eric Andersen2870d962001-07-02 17:27:21 +00002106static union node *parsecmd(int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002107
Eric Andersencb57d552001-06-28 07:25:16 +00002108/*
2109 * Called to reset things after an exception.
2110 */
2111
Eric Andersencb57d552001-06-28 07:25:16 +00002112/*
2113 * The eval commmand.
2114 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002115static void evalstring(char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00002116
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002117static int evalcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002118{
Eric Andersen2870d962001-07-02 17:27:21 +00002119 char *p;
2120 char *concat;
2121 char **ap;
Eric Andersencb57d552001-06-28 07:25:16 +00002122
Eric Andersen2870d962001-07-02 17:27:21 +00002123 if (argc > 1) {
2124 p = argv[1];
2125 if (argc > 2) {
2126 STARTSTACKSTR(concat);
2127 ap = argv + 2;
2128 for (;;) {
2129 while (*p)
2130 STPUTC(*p++, concat);
2131 if ((p = *ap++) == NULL)
2132 break;
2133 STPUTC(' ', concat);
2134 }
2135 STPUTC('\0', concat);
2136 p = grabstackstr(concat);
2137 }
2138 evalstring(p, EV_TESTED);
2139 }
2140 return exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00002141}
2142
Eric Andersencb57d552001-06-28 07:25:16 +00002143/*
2144 * Execute a command or commands contained in a string.
2145 */
2146
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002147static void evaltree(union node *, int);
2148static void setinputstring(char *);
2149static void popfile(void);
Eric Andersen2870d962001-07-02 17:27:21 +00002150static void setstackmark(struct stackmark *mark);
2151static void popstackmark(struct stackmark *mark);
2152
2153
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002154static void evalstring(char *s, int flag)
Eric Andersen2870d962001-07-02 17:27:21 +00002155{
Eric Andersencb57d552001-06-28 07:25:16 +00002156 union node *n;
2157 struct stackmark smark;
2158
2159 setstackmark(&smark);
2160 setinputstring(s);
2161 while ((n = parsecmd(0)) != NEOF) {
2162 evaltree(n, flag);
2163 popstackmark(&smark);
2164 }
2165 popfile();
2166 popstackmark(&smark);
2167}
2168
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002169static struct builtincmd *find_builtin(const char *);
2170static void expandarg(union node *, struct arglist *, int);
2171static void calcsize(const union node *);
2172static union node *copynode(const union node *);
Eric Andersen62483552001-07-10 06:09:16 +00002173
2174/*
2175 * Make a copy of a parse tree.
2176 */
2177
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002178static int funcblocksize; /* size of structures in function */
2179static int funcstringsize; /* size of strings in node */
2180static pointer funcblock; /* block to allocate function from */
2181static char *funcstring; /* block to allocate strings from */
Eric Andersen62483552001-07-10 06:09:16 +00002182
2183
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002184static inline union node *copyfunc(union node *n)
Eric Andersen62483552001-07-10 06:09:16 +00002185{
2186 if (n == NULL)
2187 return NULL;
2188 funcblocksize = 0;
2189 funcstringsize = 0;
2190 calcsize(n);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00002191 funcblock = xmalloc(funcblocksize + funcstringsize);
Eric Andersen62483552001-07-10 06:09:16 +00002192 funcstring = (char *) funcblock + funcblocksize;
2193 return copynode(n);
2194}
2195
2196/*
Eric Andersen62483552001-07-10 06:09:16 +00002197 * Add a new command entry, replacing any existing command entry for
2198 * the same name.
2199 */
2200
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002201static inline void addcmdentry(char *name, struct cmdentry *entry)
Eric Andersen62483552001-07-10 06:09:16 +00002202{
2203 struct tblentry *cmdp;
2204
2205 INTOFF;
2206 cmdp = cmdlookup(name, 1);
2207 if (cmdp->cmdtype == CMDFUNCTION) {
Aaron Lehmann49c024a2002-08-02 06:39:47 +00002208 free(cmdp->param.func);
Eric Andersen62483552001-07-10 06:09:16 +00002209 }
2210 cmdp->cmdtype = entry->cmdtype;
2211 cmdp->param = entry->u;
2212 INTON;
2213}
2214
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002215static inline void evalloop(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002216{
2217 int status;
2218
2219 loopnest++;
2220 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002221 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00002222 for (;;) {
2223 evaltree(n->nbinary.ch1, EV_TESTED);
2224 if (evalskip) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002225 skipping:if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00002226 evalskip = 0;
2227 continue;
2228 }
2229 if (evalskip == SKIPBREAK && --skipcount <= 0)
2230 evalskip = 0;
2231 break;
2232 }
2233 if (n->type == NWHILE) {
2234 if (exitstatus != 0)
2235 break;
2236 } else {
2237 if (exitstatus == 0)
2238 break;
2239 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002240 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00002241 status = exitstatus;
2242 if (evalskip)
2243 goto skipping;
2244 }
2245 loopnest--;
2246 exitstatus = status;
2247}
2248
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002249static void evalfor(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002250{
2251 struct arglist arglist;
2252 union node *argp;
2253 struct strlist *sp;
2254 struct stackmark smark;
2255
2256 setstackmark(&smark);
2257 arglist.lastp = &arglist.list;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002258 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00002259 oexitstatus = exitstatus;
2260 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2261 if (evalskip)
2262 goto out;
2263 }
2264 *arglist.lastp = NULL;
2265
2266 exitstatus = 0;
2267 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002268 flags &= EV_TESTED;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002269 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00002270 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002271 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00002272 if (evalskip) {
2273 if (evalskip == SKIPCONT && --skipcount <= 0) {
2274 evalskip = 0;
2275 continue;
2276 }
2277 if (evalskip == SKIPBREAK && --skipcount <= 0)
2278 evalskip = 0;
2279 break;
2280 }
2281 }
2282 loopnest--;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002283 out:
Eric Andersencb57d552001-06-28 07:25:16 +00002284 popstackmark(&smark);
2285}
2286
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002287static inline void evalcase(const union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002288{
2289 union node *cp;
2290 union node *patp;
2291 struct arglist arglist;
2292 struct stackmark smark;
2293
2294 setstackmark(&smark);
2295 arglist.lastp = &arglist.list;
2296 oexitstatus = exitstatus;
2297 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002298 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
2299 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00002300 if (casematch(patp, arglist.list->text)) {
2301 if (evalskip == 0) {
2302 evaltree(cp->nclist.body, flags);
2303 }
2304 goto out;
2305 }
2306 }
2307 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002308 out:
Eric Andersencb57d552001-06-28 07:25:16 +00002309 popstackmark(&smark);
2310}
2311
Eric Andersencb57d552001-06-28 07:25:16 +00002312/*
Eric Andersencb57d552001-06-28 07:25:16 +00002313 * Evaluate a pipeline. All the processes in the pipeline are children
2314 * of the process creating the pipeline. (This differs from some versions
2315 * of the shell, which make the last process in a pipeline the parent
2316 * of all the rest.)
2317 */
2318
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002319static inline void evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002320{
2321 struct job *jp;
2322 struct nodelist *lp;
2323 int pipelen;
2324 int prevfd;
2325 int pip[2];
2326
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002327 TRACE(("evalpipe(0x%lx) called\n", (long) n));
Eric Andersencb57d552001-06-28 07:25:16 +00002328 pipelen = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002329 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00002330 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002331 flags |= EV_EXIT;
Eric Andersencb57d552001-06-28 07:25:16 +00002332 INTOFF;
2333 jp = makejob(n, pipelen);
2334 prevfd = -1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002335 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002336 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00002337 pip[1] = -1;
2338 if (lp->next) {
2339 if (pipe(pip) < 0) {
2340 close(prevfd);
2341 error("Pipe call failed");
2342 }
2343 }
2344 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
2345 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002346 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002347 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00002348 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002349 if (prevfd > 0) {
2350 dup2(prevfd, 0);
2351 close(prevfd);
2352 }
2353 if (pip[1] > 1) {
2354 dup2(pip[1], 1);
2355 close(pip[1]);
2356 }
2357 evaltree(lp->n, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00002358 }
2359 if (prevfd >= 0)
2360 close(prevfd);
2361 prevfd = pip[0];
2362 close(pip[1]);
2363 }
Eric Andersencb57d552001-06-28 07:25:16 +00002364 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00002365 exitstatus = waitforjob(jp);
2366 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00002367 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002368 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002369}
2370
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002371static void find_command(const char *, struct cmdentry *, int, const char *);
2372
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002373static int isassignment(const char *word)
2374{
Eric Andersen2870d962001-07-02 17:27:21 +00002375 if (!is_name(*word)) {
2376 return 0;
2377 }
2378 do {
2379 word++;
2380 } while (is_in_name(*word));
2381 return *word == '=';
2382}
2383
Eric Andersen62483552001-07-10 06:09:16 +00002384
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002385static void evalcommand(union node *cmd, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00002386{
2387 struct stackmark smark;
2388 union node *argp;
2389 struct arglist arglist;
2390 struct arglist varlist;
2391 char **argv;
2392 int argc;
2393 char **envp;
2394 struct strlist *sp;
2395 int mode;
Eric Andersencb57d552001-06-28 07:25:16 +00002396 struct cmdentry cmdentry;
2397 struct job *jp;
2398 char *volatile savecmdname;
2399 volatile struct shparam saveparam;
2400 struct localvar *volatile savelocalvars;
2401 volatile int e;
2402 char *lastarg;
2403 const char *path;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002404 int spclbltin;
Eric Andersencb57d552001-06-28 07:25:16 +00002405 struct jmploc *volatile savehandler;
2406 struct jmploc jmploc;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002407
Eric Andersencb57d552001-06-28 07:25:16 +00002408#if __GNUC__
2409 /* Avoid longjmp clobbering */
2410 (void) &argv;
2411 (void) &argc;
2412 (void) &lastarg;
2413 (void) &flags;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002414 (void) &spclbltin;
Eric Andersencb57d552001-06-28 07:25:16 +00002415#endif
2416
2417 /* First expand the arguments. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002418 TRACE(("evalcommand(0x%lx, %d) called\n", (long) cmd, flags));
Eric Andersencb57d552001-06-28 07:25:16 +00002419 setstackmark(&smark);
2420 arglist.lastp = &arglist.list;
2421 varlist.lastp = &varlist.list;
2422 arglist.list = 0;
2423 oexitstatus = exitstatus;
2424 exitstatus = 0;
2425 path = pathval();
2426 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
2427 expandarg(argp, &varlist, EXP_VARTILDE);
2428 }
Eric Andersen1887b042002-10-22 11:58:59 +00002429 for (argp = cmd->ncmd.args; argp && !arglist.list; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00002430 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2431 }
Eric Andersen1887b042002-10-22 11:58:59 +00002432 if (argp) {
2433 struct builtincmd *bcmd;
2434 int pseudovarflag;
2435
2436 bcmd = find_builtin(arglist.list->text);
2437 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
2438 for (; argp; argp = argp->narg.next) {
2439 if (pseudovarflag && isassignment(argp->narg.text)) {
2440 expandarg(argp, &arglist, EXP_VARTILDE);
2441 continue;
2442 }
2443 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
2444 }
2445 }
Eric Andersencb57d552001-06-28 07:25:16 +00002446 *arglist.lastp = NULL;
2447 *varlist.lastp = NULL;
2448 expredir(cmd->ncmd.redirect);
2449 argc = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002450 for (sp = arglist.list; sp; sp = sp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00002451 argc++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002452 argv = stalloc(sizeof(char *) * (argc + 1));
Eric Andersencb57d552001-06-28 07:25:16 +00002453
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002454 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00002455 TRACE(("evalcommand arg: %s\n", sp->text));
2456 *argv++ = sp->text;
2457 }
2458 *argv = NULL;
2459 lastarg = NULL;
2460 if (iflag && funcnest == 0 && argc > 0)
2461 lastarg = argv[-1];
2462 argv -= argc;
2463
2464 /* Print the command if xflag is set. */
2465 if (xflag) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002466 out2c('+');
Eric Andersencb57d552001-06-28 07:25:16 +00002467 eprintlist(varlist.list);
2468 eprintlist(arglist.list);
Eric Andersen3102ac42001-07-06 04:26:23 +00002469 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00002470 }
2471
2472 /* Now locate the command. */
2473 if (argc == 0) {
2474 cmdentry.cmdtype = CMDBUILTIN;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002475 cmdentry.u.cmd = BLTINCMD;
2476 spclbltin = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00002477 } else {
2478 const char *oldpath;
2479 int findflag = DO_ERR;
2480 int oldfindflag;
2481
2482 /*
2483 * Modify the command lookup path, if a PATH= assignment
2484 * is present
2485 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002486 for (sp = varlist.list; sp; sp = sp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00002487 if (varequal(sp->text, defpathvar)) {
2488 path = sp->text + 5;
2489 findflag |= DO_BRUTE;
2490 }
2491 oldpath = path;
2492 oldfindflag = findflag;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002493 spclbltin = -1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002494 for (;;) {
Eric Andersencb57d552001-06-28 07:25:16 +00002495 find_command(argv[0], &cmdentry, findflag, path);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002496 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
Eric Andersencb57d552001-06-28 07:25:16 +00002497 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002498 goto out;
2499 }
2500 /* implement bltin and command here */
2501 if (cmdentry.cmdtype != CMDBUILTIN) {
2502 break;
2503 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002504 if (spclbltin < 0) {
2505 spclbltin = !!(IS_BUILTIN_SPECIAL(cmdentry.u.cmd)) * 2;
Eric Andersencb57d552001-06-28 07:25:16 +00002506 }
2507 if (cmdentry.u.cmd == BLTINCMD) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002508 for (;;) {
Eric Andersencb57d552001-06-28 07:25:16 +00002509 struct builtincmd *bcmd;
2510
2511 argv++;
2512 if (--argc == 0)
2513 goto found;
2514 if (!(bcmd = find_builtin(*argv))) {
Eric Andersen3102ac42001-07-06 04:26:23 +00002515 out2fmt("%s: not found\n", *argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002516 exitstatus = 127;
Eric Andersencb57d552001-06-28 07:25:16 +00002517 goto out;
2518 }
2519 cmdentry.u.cmd = bcmd;
2520 if (bcmd != BLTINCMD)
2521 break;
2522 }
2523 }
Eric Andersen2870d962001-07-02 17:27:21 +00002524 if (cmdentry.u.cmd == find_builtin("command")) {
Eric Andersencb57d552001-06-28 07:25:16 +00002525 argv++;
2526 if (--argc == 0) {
2527 goto found;
2528 }
2529 if (*argv[0] == '-') {
2530 if (!equal(argv[0], "-p")) {
2531 argv--;
2532 argc++;
2533 break;
2534 }
2535 argv++;
2536 if (--argc == 0) {
2537 goto found;
2538 }
2539 path = defpath;
2540 findflag |= DO_BRUTE;
2541 } else {
2542 path = oldpath;
2543 findflag = oldfindflag;
2544 }
2545 findflag |= DO_NOFUN;
2546 continue;
2547 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002548 found:
Eric Andersencb57d552001-06-28 07:25:16 +00002549 break;
2550 }
2551 }
2552
2553 /* Fork off a child process if necessary. */
2554 if (cmd->ncmd.backgnd
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002555 || (cmdentry.cmdtype == CMDNORMAL && (!(flags & EV_EXIT) || trap[0]))
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002556 ) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002557 INTOFF;
Eric Andersencb57d552001-06-28 07:25:16 +00002558 jp = makejob(cmd, 1);
2559 mode = cmd->ncmd.backgnd;
Eric Andersencb57d552001-06-28 07:25:16 +00002560 if (forkshell(jp, cmd, mode) != 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002561 goto parent; /* at end of routine */
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002562 FORCEINTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002563 flags |= EV_EXIT;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002564 } else {
2565 flags &= ~EV_EXIT;
Eric Andersencb57d552001-06-28 07:25:16 +00002566 }
2567
2568 /* This is the child process if a fork occurred. */
2569 /* Execute the command. */
2570 if (cmdentry.cmdtype == CMDFUNCTION) {
2571#ifdef DEBUG
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002572 trputs("Shell function: ");
2573 trargs(argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002574#endif
2575 exitstatus = oexitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +00002576 saveparam = shellparam;
2577 shellparam.malloc = 0;
2578 shellparam.nparam = argc - 1;
2579 shellparam.p = argv + 1;
2580 INTOFF;
2581 savelocalvars = localvars;
2582 localvars = NULL;
2583 INTON;
2584 if (setjmp(jmploc.loc)) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002585 if (exception == EXREDIR) {
2586 exitstatus = 2;
2587 goto funcdone;
Eric Andersencb57d552001-06-28 07:25:16 +00002588 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002589 saveparam.optind = shellparam.optind;
2590 saveparam.optoff = shellparam.optoff;
2591 freeparam(&shellparam);
2592 shellparam = saveparam;
Eric Andersencb57d552001-06-28 07:25:16 +00002593 poplocalvars();
2594 localvars = savelocalvars;
2595 handler = savehandler;
2596 longjmp(handler->loc, 1);
2597 }
2598 savehandler = handler;
2599 handler = &jmploc;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002600 redirect(cmd->ncmd.redirect, REDIR_PUSH);
2601 listsetvar(varlist.list);
Eric Andersencb57d552001-06-28 07:25:16 +00002602 funcnest++;
2603 evaltree(cmdentry.u.func, flags & EV_TESTED);
2604 funcnest--;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002605 funcdone:
Eric Andersencb57d552001-06-28 07:25:16 +00002606 INTOFF;
2607 poplocalvars();
2608 localvars = savelocalvars;
2609 saveparam.optind = shellparam.optind;
2610 saveparam.optoff = shellparam.optoff;
2611 freeparam(&shellparam);
2612 shellparam = saveparam;
2613 handler = savehandler;
2614 popredir();
2615 INTON;
2616 if (evalskip == SKIPFUNC) {
2617 evalskip = 0;
2618 skipcount = 0;
2619 }
Eric Andersencb57d552001-06-28 07:25:16 +00002620 } else if (cmdentry.cmdtype == CMDBUILTIN) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002621 int redir;
2622
Eric Andersencb57d552001-06-28 07:25:16 +00002623#ifdef DEBUG
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002624 trputs("builtin command: ");
2625 trargs(argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002626#endif
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002627 redir = (cmdentry.u.cmd == EXECCMD) ? 0 : REDIR_PUSH;
Eric Andersencb57d552001-06-28 07:25:16 +00002628 savecmdname = commandname;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002629 if (spclbltin) {
Eric Andersencb57d552001-06-28 07:25:16 +00002630 listsetvar(varlist.list);
2631 } else {
2632 cmdenviron = varlist.list;
2633 }
2634 e = -1;
2635 if (setjmp(jmploc.loc)) {
2636 e = exception;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002637 exitstatus = (e == EXINT) ? SIGINT + 128 : 2;
Eric Andersencb57d552001-06-28 07:25:16 +00002638 goto cmddone;
2639 }
2640 savehandler = handler;
2641 handler = &jmploc;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002642 redirect(cmd->ncmd.redirect, redir);
Eric Andersencb57d552001-06-28 07:25:16 +00002643 commandname = argv[0];
2644 argptr = argv + 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002645 optptr = NULL; /* initialize nextopt */
2646 exitstatus = (*cmdentry.u.cmd->builtinfunc) (argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002647 flushall();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002648 cmddone:
Eric Andersencb57d552001-06-28 07:25:16 +00002649 cmdenviron = NULL;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002650 commandname = savecmdname;
Eric Andersencb57d552001-06-28 07:25:16 +00002651 handler = savehandler;
2652 if (e != -1) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002653 if (e == EXINT || spclbltin & 2) {
2654 if (e == EXREDIR)
2655 exraise(e);
2656 }
Eric Andersencb57d552001-06-28 07:25:16 +00002657 FORCEINTON;
2658 }
2659 if (cmdentry.u.cmd != EXECCMD)
2660 popredir();
Eric Andersencb57d552001-06-28 07:25:16 +00002661 } else {
2662#ifdef DEBUG
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002663 trputs("normal command: ");
2664 trargs(argv);
Eric Andersencb57d552001-06-28 07:25:16 +00002665#endif
2666 redirect(cmd->ncmd.redirect, 0);
2667 clearredir();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002668 for (sp = varlist.list; sp; sp = sp->next)
2669 setvareq(sp->text, VEXPORT | VSTACK);
Eric Andersencb57d552001-06-28 07:25:16 +00002670 envp = environment();
2671 shellexec(argv, envp, path, cmdentry.u.index);
2672 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002673 if (flags & EV_EXIT)
2674 exitshell(exitstatus);
Eric Andersencb57d552001-06-28 07:25:16 +00002675 goto out;
2676
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002677 parent: /* parent process gets here (if we forked) */
2678 if (mode == 0) { /* argument to fork */
Eric Andersencb57d552001-06-28 07:25:16 +00002679 exitstatus = waitforjob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00002680 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002681 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00002682
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002683 out:
Eric Andersencb57d552001-06-28 07:25:16 +00002684 if (lastarg)
2685 setvar("_", lastarg, 0);
2686 popstackmark(&smark);
2687}
2688
Eric Andersen62483552001-07-10 06:09:16 +00002689/*
2690 * Evaluate a parse tree. The value is left in the global variable
2691 * exitstatus.
2692 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002693static void evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00002694{
2695 int checkexit = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002696
Eric Andersen62483552001-07-10 06:09:16 +00002697 if (n == NULL) {
2698 TRACE(("evaltree(NULL) called\n"));
2699 goto out;
2700 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002701 TRACE(("evaltree(0x%lx: %d) called\n", (long) n, n->type));
Eric Andersen62483552001-07-10 06:09:16 +00002702 switch (n->type) {
2703 case NSEMI:
2704 evaltree(n->nbinary.ch1, flags & EV_TESTED);
2705 if (evalskip)
2706 goto out;
2707 evaltree(n->nbinary.ch2, flags);
2708 break;
2709 case NAND:
2710 evaltree(n->nbinary.ch1, EV_TESTED);
2711 if (evalskip || exitstatus != 0)
2712 goto out;
2713 evaltree(n->nbinary.ch2, flags);
2714 break;
2715 case NOR:
2716 evaltree(n->nbinary.ch1, EV_TESTED);
2717 if (evalskip || exitstatus == 0)
2718 goto out;
2719 evaltree(n->nbinary.ch2, flags);
2720 break;
2721 case NREDIR:
2722 expredir(n->nredir.redirect);
2723 redirect(n->nredir.redirect, REDIR_PUSH);
2724 evaltree(n->nredir.n, flags);
2725 popredir();
2726 break;
2727 case NSUBSHELL:
2728 evalsubshell(n, flags);
2729 break;
2730 case NBACKGND:
2731 evalsubshell(n, flags);
2732 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002733 case NIF:{
Eric Andersen62483552001-07-10 06:09:16 +00002734 evaltree(n->nif.test, EV_TESTED);
2735 if (evalskip)
2736 goto out;
2737 if (exitstatus == 0)
2738 evaltree(n->nif.ifpart, flags);
2739 else if (n->nif.elsepart)
2740 evaltree(n->nif.elsepart, flags);
2741 else
2742 exitstatus = 0;
2743 break;
2744 }
2745 case NWHILE:
2746 case NUNTIL:
2747 evalloop(n, flags);
2748 break;
2749 case NFOR:
2750 evalfor(n, flags);
2751 break;
2752 case NCASE:
2753 evalcase(n, flags);
2754 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002755 case NDEFUN:{
Eric Andersen62483552001-07-10 06:09:16 +00002756 struct builtincmd *bcmd;
2757 struct cmdentry entry;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002758
2759 if ((bcmd = find_builtin(n->narg.text)) && IS_BUILTIN_SPECIAL(bcmd)
2760 ) {
Eric Andersen62483552001-07-10 06:09:16 +00002761 out2fmt("%s is a special built-in\n", n->narg.text);
2762 exitstatus = 1;
2763 break;
2764 }
2765 entry.cmdtype = CMDFUNCTION;
2766 entry.u.func = copyfunc(n->narg.next);
2767 addcmdentry(n->narg.text, &entry);
2768 exitstatus = 0;
2769 break;
2770 }
2771 case NNOT:
2772 evaltree(n->nnot.com, EV_TESTED);
2773 exitstatus = !exitstatus;
2774 break;
Eric Andersencb57d552001-06-28 07:25:16 +00002775
Eric Andersen62483552001-07-10 06:09:16 +00002776 case NPIPE:
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002777 evalpipe(n, flags);
Eric Andersen62483552001-07-10 06:09:16 +00002778 checkexit = 1;
2779 break;
2780 case NCMD:
2781 evalcommand(n, flags);
2782 checkexit = 1;
2783 break;
2784#ifdef DEBUG
2785 default:
2786 printf("Node type = %d\n", n->type);
2787 break;
2788#endif
2789 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002790 out:
Eric Andersen62483552001-07-10 06:09:16 +00002791 if (pendingsigs)
2792 dotrap();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002793 if (flags & EV_EXIT ||
Eric Andersen62483552001-07-10 06:09:16 +00002794 (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002795 )
Eric Andersen62483552001-07-10 06:09:16 +00002796 exitshell(exitstatus);
2797}
2798
2799/*
2800 * Kick off a subshell to evaluate a tree.
2801 */
2802
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002803static void evalsubshell(const union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00002804{
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002805 struct job *jp = 0;
Eric Andersen62483552001-07-10 06:09:16 +00002806 int backgnd = (n->type == NBACKGND);
2807
2808 expredir(n->nredir.redirect);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002809 if (!backgnd && flags & EV_EXIT && !trap[0])
2810 goto nofork;
2811 INTOFF;
Eric Andersen62483552001-07-10 06:09:16 +00002812 jp = makejob(n, 1);
2813 if (forkshell(jp, n, backgnd) == 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002814 INTON;
2815 flags |= EV_EXIT;
Eric Andersen62483552001-07-10 06:09:16 +00002816 if (backgnd)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002817 flags &= ~EV_TESTED;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002818 nofork:
Eric Andersen62483552001-07-10 06:09:16 +00002819 redirect(n->nredir.redirect, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002820 evaltree(n->nredir.n, flags); /* never returns */
Eric Andersen62483552001-07-10 06:09:16 +00002821 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002822 if (!backgnd) {
Eric Andersen62483552001-07-10 06:09:16 +00002823 exitstatus = waitforjob(jp);
Eric Andersen62483552001-07-10 06:09:16 +00002824 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002825 INTON;
Eric Andersen62483552001-07-10 06:09:16 +00002826}
2827
2828/*
2829 * Compute the names of the files in a redirection list.
2830 */
2831
2832static void fixredir(union node *n, const char *text, int err);
2833
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002834static void expredir(union node *n)
Eric Andersen62483552001-07-10 06:09:16 +00002835{
2836 union node *redir;
2837
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002838 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersen62483552001-07-10 06:09:16 +00002839 struct arglist fn;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002840
Eric Andersen62483552001-07-10 06:09:16 +00002841 fn.lastp = &fn.list;
2842 oexitstatus = exitstatus;
2843 switch (redir->type) {
2844 case NFROMTO:
2845 case NFROM:
2846 case NTO:
2847 case NAPPEND:
2848 case NTOOV:
2849 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
2850 redir->nfile.expfname = fn.list->text;
2851 break;
2852 case NFROMFD:
2853 case NTOFD:
2854 if (redir->ndup.vname) {
2855 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
2856 fixredir(redir, fn.list->text, 1);
2857 }
2858 break;
2859 }
2860 }
2861}
2862
2863
2864/*
2865 * Execute a command inside back quotes. If it's a builtin command, we
2866 * want to save its output in a block obtained from malloc. Otherwise
2867 * we fork off a subprocess and get the output of the command via a pipe.
2868 * Should be called with interrupts off.
2869 */
2870
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002871static void evalbackcmd(union node *n, struct backcmd *result)
Eric Andersen62483552001-07-10 06:09:16 +00002872{
2873 int pip[2];
2874 struct job *jp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002875 struct stackmark smark; /* unnecessary */
Eric Andersen62483552001-07-10 06:09:16 +00002876
2877 setstackmark(&smark);
2878 result->fd = -1;
2879 result->buf = NULL;
2880 result->nleft = 0;
2881 result->jp = NULL;
2882 if (n == NULL) {
2883 exitstatus = 0;
2884 goto out;
2885 }
2886 exitstatus = 0;
2887 if (pipe(pip) < 0)
2888 error("Pipe call failed");
2889 jp = makejob(n, 1);
2890 if (forkshell(jp, n, FORK_NOJOB) == 0) {
2891 FORCEINTON;
2892 close(pip[0]);
2893 if (pip[1] != 1) {
2894 close(1);
2895 dup_as_newfd(pip[1], 1);
2896 close(pip[1]);
2897 }
2898 eflag = 0;
2899 evaltree(n, EV_EXIT);
2900 }
2901 close(pip[1]);
2902 result->fd = pip[0];
2903 result->jp = jp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002904 out:
Eric Andersen62483552001-07-10 06:09:16 +00002905 popstackmark(&smark);
2906 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002907 result->fd, result->buf, result->nleft, result->jp));
Eric Andersen62483552001-07-10 06:09:16 +00002908}
2909
2910
2911/*
2912 * Execute a simple command.
2913 */
Eric Andersencb57d552001-06-28 07:25:16 +00002914
Glenn L McGrath50812ff2002-08-23 13:14:48 +00002915/*
2916 * Search for a command. This is called before we fork so that the
2917 * location of the command will be available in the parent as well as
2918 * the child. The check for "goodname" is an overly conservative
2919 * check that the name will not be subject to expansion.
2920 */
2921
2922static void prehash(union node *n)
2923{
2924 struct cmdentry entry;
2925
2926 if (n->type == NCMD && n->ncmd.args)
2927 if (goodname(n->ncmd.args->narg.text))
2928 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
2929}
2930
Eric Andersencb57d552001-06-28 07:25:16 +00002931
Eric Andersencb57d552001-06-28 07:25:16 +00002932/*
2933 * Builtin commands. Builtin commands whose functions are closely
2934 * tied to evaluation are implemented here.
2935 */
2936
2937/*
2938 * No command given, or a bltin command with no arguments. Set the
2939 * specified variables.
2940 */
2941
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002942int bltincmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002943{
2944 /*
2945 * Preserve exitstatus of a previous possible redirection
2946 * as POSIX mandates
2947 */
2948 return exitstatus;
2949}
2950
2951
2952/*
2953 * Handle break and continue commands. Break, continue, and return are
2954 * all handled by setting the evalskip flag. The evaluation routines
2955 * above all check this flag, and if it is set they start skipping
2956 * commands rather than executing them. The variable skipcount is
2957 * the number of loops to break/continue, or the number of function
2958 * levels to return. (The latter is always 1.) It should probably
2959 * be an error to break out of more loops than exist, but it isn't
2960 * in the standard shell so we don't make it one here.
2961 */
2962
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002963static int breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002964{
2965 int n = argc > 1 ? number(argv[1]) : 1;
2966
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00002967 if (n <= 0)
2968 error("Illegal number: %s", argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00002969 if (n > loopnest)
2970 n = loopnest;
2971 if (n > 0) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002972 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00002973 skipcount = n;
2974 }
2975 return 0;
2976}
2977
2978
2979/*
2980 * The return command.
2981 */
2982
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002983static int returncmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00002984{
2985 int ret = argc > 1 ? number(argv[1]) : oexitstatus;
2986
2987 if (funcnest) {
2988 evalskip = SKIPFUNC;
2989 skipcount = 1;
2990 return ret;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002991 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00002992 /* Do what ksh does; skip the rest of the file */
2993 evalskip = SKIPFILE;
2994 skipcount = 1;
2995 return ret;
2996 }
2997}
2998
2999
Eric Andersen69a20f02001-10-31 10:40:37 +00003000#ifndef CONFIG_FALSE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003001static int false_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003002{
3003 return 1;
3004}
Eric Andersen69a20f02001-10-31 10:40:37 +00003005#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003006
Eric Andersen69a20f02001-10-31 10:40:37 +00003007#ifndef CONFIG_TRUE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003008static int true_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003009{
3010 return 0;
3011}
3012#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003013
3014/*
3015 * Controls whether the shell is interactive or not.
3016 */
3017
3018static void setsignal(int signo);
Eric Andersen2870d962001-07-02 17:27:21 +00003019
Eric Andersend35c5df2002-01-09 15:37:36 +00003020#ifdef CONFIG_ASH_MAIL
Eric Andersenec074692001-10-31 11:05:49 +00003021static void chkmail(int silent);
3022#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003023
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003024static void setinteractive(int on)
Eric Andersen2870d962001-07-02 17:27:21 +00003025{
3026 static int is_interactive;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003027 static int do_banner = 0;
Eric Andersen2870d962001-07-02 17:27:21 +00003028
3029 if (on == is_interactive)
3030 return;
3031 setsignal(SIGINT);
3032 setsignal(SIGQUIT);
3033 setsignal(SIGTERM);
Eric Andersend35c5df2002-01-09 15:37:36 +00003034#ifdef CONFIG_ASH_MAIL
Eric Andersen2870d962001-07-02 17:27:21 +00003035 chkmail(1);
Eric Andersenec074692001-10-31 11:05:49 +00003036#endif
Eric Andersen2870d962001-07-02 17:27:21 +00003037 is_interactive = on;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003038 if (do_banner == 0 && is_interactive) {
Eric Andersen1c039232001-07-07 00:05:55 +00003039 /* Looks like they want an interactive shell */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003040#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
3041 printf("\n\n" BB_BANNER " Built-in shell (ash)\n");
3042 printf("Enter 'help' for a list of built-in commands.\n\n");
Eric Andersend63dee42001-10-19 00:22:23 +00003043#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003044 do_banner = 1;
Eric Andersen1c039232001-07-07 00:05:55 +00003045 }
Eric Andersen2870d962001-07-02 17:27:21 +00003046}
3047
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003048static void optschanged(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003049{
3050 setinteractive(iflag);
3051 setjobctl(mflag);
3052}
3053
Eric Andersencb57d552001-06-28 07:25:16 +00003054
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003055static int execcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003056{
3057 if (argc > 1) {
3058 struct strlist *sp;
3059
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003060 iflag = 0; /* exit on error */
Eric Andersencb57d552001-06-28 07:25:16 +00003061 mflag = 0;
3062 optschanged();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003063 for (sp = cmdenviron; sp; sp = sp->next)
3064 setvareq(sp->text, VEXPORT | VSTACK);
Eric Andersencb57d552001-06-28 07:25:16 +00003065 shellexec(argv + 1, environment(), pathval(), 0);
3066 }
3067 return 0;
3068}
3069
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003070static void eprintlist(struct strlist *sp)
Eric Andersencb57d552001-06-28 07:25:16 +00003071{
3072 for (; sp; sp = sp->next) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003073 out2fmt(" %s", sp->text);
Eric Andersencb57d552001-06-28 07:25:16 +00003074 }
3075}
Eric Andersencb57d552001-06-28 07:25:16 +00003076
3077/*
3078 * Exec a program. Never returns. If you change this routine, you may
3079 * have to change the find_command routine as well.
3080 */
3081
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003082static const char *pathopt; /* set by padvance */
Eric Andersen2870d962001-07-02 17:27:21 +00003083
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003084static void shellexec(char **argv, char **envp, const char *path, int idx)
Eric Andersencb57d552001-06-28 07:25:16 +00003085{
3086 char *cmdname;
3087 int e;
3088
Eric Andersenbf8bf102002-09-17 08:41:08 +00003089 if (strchr(argv[0], '/') != NULL
3090#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3091 || find_applet_by_name(argv[0])
3092#endif
3093 )
3094 {
Eric Andersencb57d552001-06-28 07:25:16 +00003095 tryexec(argv[0], argv, envp);
3096 e = errno;
3097 } else {
3098 e = ENOENT;
3099 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3100 if (--idx < 0 && pathopt == NULL) {
3101 tryexec(cmdname, argv, envp);
3102 if (errno != ENOENT && errno != ENOTDIR)
3103 e = errno;
3104 }
3105 stunalloc(cmdname);
3106 }
3107 }
3108
3109 /* Map to POSIX errors */
3110 switch (e) {
3111 case EACCES:
3112 exerrno = 126;
3113 break;
3114 case ENOENT:
3115 exerrno = 127;
3116 break;
3117 default:
3118 exerrno = 2;
3119 break;
3120 }
3121 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3122 /* NOTREACHED */
3123}
3124
Eric Andersen2870d962001-07-02 17:27:21 +00003125/*
3126 * Clear traps on a fork.
3127 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003128static void clear_traps(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003129{
Eric Andersen2870d962001-07-02 17:27:21 +00003130 char **tp;
3131
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003132 for (tp = trap; tp < &trap[NSIG]; tp++) {
3133 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
Eric Andersen2870d962001-07-02 17:27:21 +00003134 INTOFF;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003135 free(*tp);
Eric Andersen2870d962001-07-02 17:27:21 +00003136 *tp = NULL;
3137 if (tp != &trap[0])
3138 setsignal(tp - trap);
3139 INTON;
3140 }
3141 }
3142}
3143
3144
Eric Andersen2870d962001-07-02 17:27:21 +00003145static int preadbuffer(void);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003146static void pushfile(void);
Eric Andersen2870d962001-07-02 17:27:21 +00003147
3148/*
3149 * Read a character from the script, returning PEOF on end of file.
3150 * Nul characters in the input are silently discarded.
3151 */
3152
Eric Andersend35c5df2002-01-09 15:37:36 +00003153#ifndef CONFIG_ASH_OPTIMIZE_FOR_SIZE
Eric Andersen2870d962001-07-02 17:27:21 +00003154#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003155static int pgetc(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003156{
3157 return pgetc_macro();
3158}
3159#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003160static int pgetc_macro(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003161{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003162 return --parsenleft >= 0 ? *parsenextc++ : preadbuffer();
Eric Andersen2870d962001-07-02 17:27:21 +00003163}
3164
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003165static inline int pgetc(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003166{
3167 return pgetc_macro();
3168}
3169#endif
3170
3171
3172/*
3173 * Undo the last call to pgetc. Only one character may be pushed back.
3174 * PEOF may be pushed back.
3175 */
3176
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003177static void pungetc(void)
Eric Andersen74400cc2001-10-18 04:11:39 +00003178{
Eric Andersen2870d962001-07-02 17:27:21 +00003179 parsenleft++;
3180 parsenextc--;
3181}
3182
3183
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003184static void popfile(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003185{
Eric Andersen2870d962001-07-02 17:27:21 +00003186 struct parsefile *pf = parsefile;
3187
3188 INTOFF;
3189 if (pf->fd >= 0)
3190 close(pf->fd);
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003191 free(pf->buf);
Eric Andersen2870d962001-07-02 17:27:21 +00003192 while (pf->strpush)
3193 popstring();
3194 parsefile = pf->prev;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003195 free(pf);
Eric Andersen2870d962001-07-02 17:27:21 +00003196 parsenleft = parsefile->nleft;
3197 parselleft = parsefile->lleft;
3198 parsenextc = parsefile->nextc;
3199 plinno = parsefile->linno;
3200 INTON;
3201}
3202
3203
3204/*
3205 * Return to top level.
3206 */
3207
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003208static void popallfiles(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003209{
Eric Andersen2870d962001-07-02 17:27:21 +00003210 while (parsefile != &basepf)
3211 popfile();
3212}
3213
3214/*
3215 * Close the file(s) that the shell is reading commands from. Called
3216 * after a fork is done.
3217 */
3218
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003219static void closescript(void)
Eric Andersen74400cc2001-10-18 04:11:39 +00003220{
Eric Andersen2870d962001-07-02 17:27:21 +00003221 popallfiles();
3222 if (parsefile->fd > 0) {
3223 close(parsefile->fd);
3224 parsefile->fd = 0;
3225 }
3226}
3227
3228
3229/*
3230 * Like setinputfile, but takes an open file descriptor. Call this with
3231 * interrupts off.
3232 */
3233
Eric Andersen74400cc2001-10-18 04:11:39 +00003234static void setinputfd(int fd, int push)
Eric Andersen2870d962001-07-02 17:27:21 +00003235{
3236 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
3237 if (push) {
3238 pushfile();
3239 parsefile->buf = 0;
3240 } else {
3241 closescript();
3242 while (parsefile->strpush)
3243 popstring();
3244 }
3245 parsefile->fd = fd;
3246 if (parsefile->buf == NULL)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003247 parsefile->buf = xmalloc(BUFSIZ);
Eric Andersen2870d962001-07-02 17:27:21 +00003248 parselleft = parsenleft = 0;
3249 plinno = 1;
3250}
3251
3252
3253/*
3254 * Set the input to take input from a file. If push is set, push the
3255 * old input onto the stack first.
3256 */
3257
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003258static void setinputfile(const char *fname, int push)
Eric Andersen2870d962001-07-02 17:27:21 +00003259{
3260 int fd;
3261 int myfileno2;
3262
3263 INTOFF;
3264 if ((fd = open(fname, O_RDONLY)) < 0)
3265 error("Can't open %s", fname);
3266 if (fd < 10) {
3267 myfileno2 = dup_as_newfd(fd, 10);
3268 close(fd);
3269 if (myfileno2 < 0)
3270 error("Out of file descriptors");
3271 fd = myfileno2;
3272 }
3273 setinputfd(fd, push);
3274 INTON;
3275}
3276
Eric Andersencb57d552001-06-28 07:25:16 +00003277
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003278static void tryexec(char *cmd, char **argv, char **envp)
Eric Andersen62483552001-07-10 06:09:16 +00003279{
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003280 int repeated = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003281
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003282#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
Eric Andersen3102ac42001-07-06 04:26:23 +00003283 char *name = cmd;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003284 char **argv_l = argv;
Eric Andersen3102ac42001-07-06 04:26:23 +00003285 int argc_l;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003286
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003287#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
Eric Andersen3102ac42001-07-06 04:26:23 +00003288 name = get_last_path_component(name);
3289#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003290 argv_l = envp;
3291 for (argc_l = 0; *argv_l != NULL; argv_l++, argc_l++)
Eric Andersen3102ac42001-07-06 04:26:23 +00003292 putenv(*argv_l);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003293 argv_l = argv;
3294 for (argc_l = 0; *argv_l != NULL; argv_l++, argc_l++)
3295 optind = 1;
Eric Andersen3102ac42001-07-06 04:26:23 +00003296 run_applet_by_name(name, argc_l, argv);
3297#endif
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003298 repeat:
Eric Andersencb57d552001-06-28 07:25:16 +00003299 execve(cmd, argv, envp);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003300 if (repeated++) {
3301 free(argv);
3302 } else if (errno == ENOEXEC) {
3303 char **ap;
3304 char **new;
3305
3306 for (ap = argv; *ap; ap++);
3307 ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
3308 *ap++ = cmd = "/bin/sh";
3309 while ((*ap++ = *argv++));
3310 argv = new;
3311 goto repeat;
Eric Andersencb57d552001-06-28 07:25:16 +00003312 }
Eric Andersencb57d552001-06-28 07:25:16 +00003313}
3314
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003315static char *commandtext(const union node *);
Eric Andersencb57d552001-06-28 07:25:16 +00003316
3317/*
3318 * Do a path search. The variable path (passed by reference) should be
3319 * set to the start of the path before the first call; padvance will update
3320 * this value as it proceeds. Successive calls to padvance will return
3321 * the possible path expansions in sequence. If an option (indicated by
3322 * a percent sign) appears in the path entry then the global variable
3323 * pathopt will be set to point to it; otherwise pathopt will be set to
3324 * NULL.
3325 */
3326
3327static const char *pathopt;
3328
Eric Andersen2870d962001-07-02 17:27:21 +00003329static void growstackblock(void);
3330
3331
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003332static char *padvance(const char **path, const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003333{
Eric Andersencb57d552001-06-28 07:25:16 +00003334 const char *p;
3335 char *q;
3336 const char *start;
3337 int len;
3338
3339 if (*path == NULL)
3340 return NULL;
3341 start = *path;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003342 for (p = start; *p && *p != ':' && *p != '%'; p++);
3343 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +00003344 while (stackblocksize() < len)
3345 growstackblock();
3346 q = stackblock();
3347 if (p != start) {
3348 memcpy(q, start, p - start);
3349 q += p - start;
3350 *q++ = '/';
3351 }
3352 strcpy(q, name);
3353 pathopt = NULL;
3354 if (*p == '%') {
3355 pathopt = ++p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003356 while (*p && *p != ':')
3357 p++;
Eric Andersencb57d552001-06-28 07:25:16 +00003358 }
3359 if (*p == ':')
3360 *path = p + 1;
3361 else
3362 *path = NULL;
3363 return stalloc(len);
3364}
3365
Eric Andersen62483552001-07-10 06:09:16 +00003366/*
3367 * Wrapper around strcmp for qsort/bsearch/...
3368 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003369static int pstrcmp(const void *a, const void *b)
Eric Andersen62483552001-07-10 06:09:16 +00003370{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003371 return strcmp((const char *) a, (*(const char *const *) b) + 1);
Eric Andersen62483552001-07-10 06:09:16 +00003372}
3373
3374/*
3375 * Find a keyword is in a sorted array.
3376 */
3377
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003378static const char *const *findkwd(const char *s)
Eric Andersen62483552001-07-10 06:09:16 +00003379{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003380 return bsearch(s, tokname_array + KWDOFFSET,
3381 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
3382 sizeof(const char *), pstrcmp);
Eric Andersen62483552001-07-10 06:09:16 +00003383}
Eric Andersencb57d552001-06-28 07:25:16 +00003384
3385
3386/*** Command hashing code ***/
3387
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003388static int hashcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003389{
3390 struct tblentry **pp;
3391 struct tblentry *cmdp;
3392 int c;
3393 int verbose;
3394 struct cmdentry entry;
3395 char *name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003396
Eric Andersend35c5df2002-01-09 15:37:36 +00003397#ifdef CONFIG_ASH_ALIAS
Eric Andersen62483552001-07-10 06:09:16 +00003398 const struct alias *ap;
3399#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003400
3401 verbose = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003402 while ((c = nextopt("rvV")) != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00003403 if (c == 'r') {
3404 clearcmdentry(0);
3405 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00003406 } else if (c == 'v' || c == 'V') {
3407 verbose = c;
Eric Andersencb57d552001-06-28 07:25:16 +00003408 }
3409 }
3410 if (*argptr == NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003411 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
3412 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003413 if (cmdp->cmdtype != CMDBUILTIN) {
3414 printentry(cmdp, verbose);
3415 }
3416 }
3417 }
3418 return 0;
3419 }
3420 c = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003421 while ((name = *argptr++) != NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00003422 if ((cmdp = cmdlookup(name, 0)) != NULL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003423 && (cmdp->cmdtype == CMDNORMAL
3424 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
Eric Andersencb57d552001-06-28 07:25:16 +00003425 delete_cmd_entry();
Eric Andersend35c5df2002-01-09 15:37:36 +00003426#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003427 /* Then look at the aliases */
Eric Andersenec074692001-10-31 11:05:49 +00003428 if ((ap = *__lookupalias(name)) != NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003429 if (verbose == 'v')
Eric Andersen62483552001-07-10 06:09:16 +00003430 printf("%s is an alias for %s\n", name, ap->val);
3431 else
3432 printalias(ap);
3433 continue;
3434 }
3435#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003436 /* First look at the keywords */
3437 if (findkwd(name) != 0) {
3438 if (verbose == 'v')
Eric Andersen62483552001-07-10 06:09:16 +00003439 printf("%s is a shell keyword\n", name);
3440 else
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003441 puts(name);
Eric Andersen62483552001-07-10 06:09:16 +00003442 continue;
3443 }
3444
Eric Andersencb57d552001-06-28 07:25:16 +00003445 find_command(name, &entry, DO_ERR, pathval());
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003446 if (entry.cmdtype == CMDUNKNOWN)
3447 c = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003448 else if (verbose) {
3449 cmdp = cmdlookup(name, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003450 if (cmdp)
3451 printentry(cmdp, verbose == 'v');
Eric Andersencb57d552001-06-28 07:25:16 +00003452 flushall();
3453 }
Eric Andersencb57d552001-06-28 07:25:16 +00003454 }
3455 return c;
3456}
3457
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003458static void printentry(struct tblentry *cmdp, int verbose)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003459{
Eric Andersencb57d552001-06-28 07:25:16 +00003460 int idx;
3461 const char *path;
3462 char *name;
3463
Eric Andersen62483552001-07-10 06:09:16 +00003464 printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00003465 if (cmdp->cmdtype == CMDNORMAL) {
3466 idx = cmdp->param.index;
3467 path = pathval();
3468 do {
3469 name = padvance(&path, cmdp->cmdname);
3470 stunalloc(name);
3471 } while (--idx >= 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003472 if (verbose)
Eric Andersen62483552001-07-10 06:09:16 +00003473 out1str(name);
Eric Andersencb57d552001-06-28 07:25:16 +00003474 } else if (cmdp->cmdtype == CMDBUILTIN) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003475 if (verbose)
Eric Andersen62483552001-07-10 06:09:16 +00003476 out1str("a shell builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00003477 } else if (cmdp->cmdtype == CMDFUNCTION) {
Eric Andersencb57d552001-06-28 07:25:16 +00003478 if (verbose) {
3479 INTOFF;
Eric Andersen62483552001-07-10 06:09:16 +00003480 out1str("a function\n");
Eric Andersencb57d552001-06-28 07:25:16 +00003481 name = commandtext(cmdp->param.func);
Eric Andersen62483552001-07-10 06:09:16 +00003482 printf("%s() {\n %s\n}", cmdp->cmdname, name);
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003483 free(name);
Eric Andersencb57d552001-06-28 07:25:16 +00003484 INTON;
3485 }
3486#ifdef DEBUG
3487 } else {
3488 error("internal error: cmdtype %d", cmdp->cmdtype);
3489#endif
3490 }
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003491 puts(cmdp->rehash ? "*" : nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00003492}
3493
3494
3495
Eric Andersen1c039232001-07-07 00:05:55 +00003496/*** List the available builtins ***/
3497
3498
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003499static int helpcmd(int argc, char **argv)
Eric Andersen1c039232001-07-07 00:05:55 +00003500{
3501 int col, i;
Eric Andersen1c039232001-07-07 00:05:55 +00003502
Eric Andersen62483552001-07-10 06:09:16 +00003503 printf("\nBuilt-in commands:\n-------------------\n");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003504 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
Eric Andersen62483552001-07-10 06:09:16 +00003505 col += printf("%c%s", ((col == 0) ? '\t' : ' '),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003506 builtincmds[i].name + 1);
Eric Andersen1c039232001-07-07 00:05:55 +00003507 if (col > 60) {
3508 printf("\n");
3509 col = 0;
3510 }
3511 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003512#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
Eric Andersen1c039232001-07-07 00:05:55 +00003513 {
Eric Andersen1c039232001-07-07 00:05:55 +00003514 extern const struct BB_applet applets[];
3515 extern const size_t NUM_APPLETS;
3516
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003517 for (i = 0; i < NUM_APPLETS; i++) {
Eric Andersen62483552001-07-10 06:09:16 +00003518
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003519 col += printf("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
Eric Andersen1c039232001-07-07 00:05:55 +00003520 if (col > 60) {
3521 printf("\n");
3522 col = 0;
3523 }
3524 }
3525 }
3526#endif
3527 printf("\n\n");
3528 return EXIT_SUCCESS;
3529}
3530
Eric Andersencb57d552001-06-28 07:25:16 +00003531/*
3532 * Resolve a command name. If you change this routine, you may have to
3533 * change the shellexec routine as well.
3534 */
3535
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003536static int prefix(const char *, const char *);
Eric Andersen2870d962001-07-02 17:27:21 +00003537
Eric Andersencb57d552001-06-28 07:25:16 +00003538static void
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003539find_command(const char *name, struct cmdentry *entry, int act,
3540 const char *path)
Eric Andersencb57d552001-06-28 07:25:16 +00003541{
3542 struct tblentry *cmdp;
3543 int idx;
3544 int prev;
3545 char *fullname;
3546 struct stat statb;
3547 int e;
3548 int bltin;
3549 int firstchange;
3550 int updatetbl;
Eric Andersen62483552001-07-10 06:09:16 +00003551 int regular;
Eric Andersencb57d552001-06-28 07:25:16 +00003552 struct builtincmd *bcmd;
3553
3554 /* If name contains a slash, don't use the hash table */
3555 if (strchr(name, '/') != NULL) {
3556 if (act & DO_ABS) {
3557 while (stat(name, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003558 if (errno != ENOENT && errno != ENOTDIR)
3559 e = errno;
3560 entry->cmdtype = CMDUNKNOWN;
3561 entry->u.index = -1;
3562 return;
3563 }
3564 entry->cmdtype = CMDNORMAL;
3565 entry->u.index = -1;
3566 return;
3567 }
3568 entry->cmdtype = CMDNORMAL;
3569 entry->u.index = 0;
3570 return;
3571 }
3572
Eric Andersenbf8bf102002-09-17 08:41:08 +00003573#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3574 if (find_applet_by_name(name)) {
3575 entry->cmdtype = CMDNORMAL;
3576 entry->u.index = -1;
3577 return;
3578 }
3579#endif
3580
Eric Andersencb57d552001-06-28 07:25:16 +00003581 updatetbl = 1;
3582 if (act & DO_BRUTE) {
3583 firstchange = path_change(path, &bltin);
3584 } else {
3585 bltin = builtinloc;
3586 firstchange = 9999;
3587 }
3588
3589 /* If name is in the table, and not invalidated by cd, we're done */
3590 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
3591 if (cmdp->cmdtype == CMDFUNCTION) {
3592 if (act & DO_NOFUN) {
3593 updatetbl = 0;
3594 } else {
3595 goto success;
3596 }
3597 } else if (act & DO_BRUTE) {
3598 if ((cmdp->cmdtype == CMDNORMAL &&
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003599 cmdp->param.index >= firstchange) ||
3600 (cmdp->cmdtype == CMDBUILTIN &&
3601 ((builtinloc < 0 && bltin >= 0) ?
3602 bltin : builtinloc) >= firstchange)) {
Eric Andersencb57d552001-06-28 07:25:16 +00003603 /* need to recompute the entry */
3604 } else {
3605 goto success;
3606 }
3607 } else {
3608 goto success;
3609 }
3610 }
3611
3612 bcmd = find_builtin(name);
Eric Andersen2870d962001-07-02 17:27:21 +00003613 regular = bcmd && IS_BUILTIN_REGULAR(bcmd);
Eric Andersencb57d552001-06-28 07:25:16 +00003614
3615 if (regular) {
3616 if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
Eric Andersen2870d962001-07-02 17:27:21 +00003617 goto success;
Eric Andersencb57d552001-06-28 07:25:16 +00003618 }
3619 } else if (act & DO_BRUTE) {
3620 if (firstchange == 0) {
3621 updatetbl = 0;
3622 }
3623 }
3624
3625 /* If %builtin not in path, check for builtin next */
3626 if (regular || (bltin < 0 && bcmd)) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003627 builtin:
Eric Andersencb57d552001-06-28 07:25:16 +00003628 if (!updatetbl) {
3629 entry->cmdtype = CMDBUILTIN;
3630 entry->u.cmd = bcmd;
3631 return;
3632 }
3633 INTOFF;
3634 cmdp = cmdlookup(name, 1);
3635 cmdp->cmdtype = CMDBUILTIN;
3636 cmdp->param.cmd = bcmd;
3637 INTON;
3638 goto success;
3639 }
3640
3641 /* We have to search path. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003642 prev = -1; /* where to start */
3643 if (cmdp && cmdp->rehash) { /* doing a rehash */
Eric Andersencb57d552001-06-28 07:25:16 +00003644 if (cmdp->cmdtype == CMDBUILTIN)
3645 prev = builtinloc;
3646 else
3647 prev = cmdp->param.index;
3648 }
3649
3650 e = ENOENT;
3651 idx = -1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003652 loop:
Eric Andersencb57d552001-06-28 07:25:16 +00003653 while ((fullname = padvance(&path, name)) != NULL) {
3654 stunalloc(fullname);
3655 idx++;
3656 if (idx >= firstchange) {
3657 updatetbl = 0;
3658 }
3659 if (pathopt) {
3660 if (prefix("builtin", pathopt)) {
3661 if ((bcmd = find_builtin(name))) {
3662 goto builtin;
3663 }
3664 continue;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003665 } else if (!(act & DO_NOFUN) && prefix("func", pathopt)) {
Eric Andersencb57d552001-06-28 07:25:16 +00003666 /* handled below */
3667 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003668 continue; /* ignore unimplemented options */
Eric Andersencb57d552001-06-28 07:25:16 +00003669 }
3670 }
3671 /* if rehash, don't redo absolute path names */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003672 if (fullname[0] == '/' && idx <= prev && idx < firstchange) {
Eric Andersencb57d552001-06-28 07:25:16 +00003673 if (idx < prev)
3674 continue;
3675 TRACE(("searchexec \"%s\": no change\n", name));
3676 goto success;
3677 }
3678 while (stat(fullname, &statb) < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00003679 if (errno != ENOENT && errno != ENOTDIR)
3680 e = errno;
3681 goto loop;
3682 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003683 e = EACCES; /* if we fail, this will be the error */
Eric Andersencb57d552001-06-28 07:25:16 +00003684 if (!S_ISREG(statb.st_mode))
3685 continue;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003686 if (pathopt) { /* this is a %func directory */
Eric Andersencb57d552001-06-28 07:25:16 +00003687 stalloc(strlen(fullname) + 1);
3688 readcmdfile(fullname);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003689 if ((cmdp = cmdlookup(name, 0)) == NULL
3690 || cmdp->cmdtype != CMDFUNCTION)
Eric Andersencb57d552001-06-28 07:25:16 +00003691 error("%s not defined in %s", name, fullname);
3692 stunalloc(fullname);
3693 goto success;
3694 }
Eric Andersencb57d552001-06-28 07:25:16 +00003695 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
3696 /* If we aren't called with DO_BRUTE and cmdp is set, it must
3697 be a function and we're being called with DO_NOFUN */
3698 if (!updatetbl) {
3699 entry->cmdtype = CMDNORMAL;
3700 entry->u.index = idx;
3701 return;
3702 }
3703 INTOFF;
3704 cmdp = cmdlookup(name, 1);
3705 cmdp->cmdtype = CMDNORMAL;
3706 cmdp->param.index = idx;
3707 INTON;
3708 goto success;
3709 }
3710
3711 /* We failed. If there was an entry for this command, delete it */
3712 if (cmdp && updatetbl)
3713 delete_cmd_entry();
3714 if (act & DO_ERR)
Eric Andersen3102ac42001-07-06 04:26:23 +00003715 out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
Eric Andersencb57d552001-06-28 07:25:16 +00003716 entry->cmdtype = CMDUNKNOWN;
3717 return;
3718
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003719 success:
Eric Andersencb57d552001-06-28 07:25:16 +00003720 cmdp->rehash = 0;
3721 entry->cmdtype = cmdp->cmdtype;
3722 entry->u = cmdp->param;
3723}
3724
3725
3726
3727/*
3728 * Search the table of builtin commands.
3729 */
3730
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003731static struct builtincmd *find_builtin(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00003732{
3733 struct builtincmd *bp;
3734
Eric Andersen2870d962001-07-02 17:27:21 +00003735 bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003736 pstrcmp);
Eric Andersencb57d552001-06-28 07:25:16 +00003737 return bp;
3738}
3739
3740
3741/*
3742 * Called when a cd is done. Marks all commands so the next time they
3743 * are executed they will be rehashed.
3744 */
3745
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003746static void hashcd(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003747{
Eric Andersencb57d552001-06-28 07:25:16 +00003748 struct tblentry **pp;
3749 struct tblentry *cmdp;
3750
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003751 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
3752 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003753 if (cmdp->cmdtype == CMDNORMAL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003754 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
Eric Andersencb57d552001-06-28 07:25:16 +00003755 cmdp->rehash = 1;
3756 }
3757 }
3758}
3759
3760
3761
3762/*
3763 * Called before PATH is changed. The argument is the new value of PATH;
3764 * pathval() still returns the old value at this point. Called with
3765 * interrupts off.
3766 */
3767
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003768static void changepath(const char *newval)
Eric Andersencb57d552001-06-28 07:25:16 +00003769{
3770 int firstchange;
3771 int bltin;
3772
3773 firstchange = path_change(newval, &bltin);
3774 if (builtinloc < 0 && bltin >= 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003775 builtinloc = bltin; /* zap builtins */
Eric Andersencb57d552001-06-28 07:25:16 +00003776 clearcmdentry(firstchange);
3777 builtinloc = bltin;
Eric Andersen1a923762002-06-06 12:07:28 +00003778 /* Ensure that getenv("PATH") stays current */
3779 setenv("PATH", newval, 1);
Eric Andersencb57d552001-06-28 07:25:16 +00003780}
3781
3782
3783/*
3784 * Clear out command entries. The argument specifies the first entry in
3785 * PATH which has changed.
3786 */
3787
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003788static void clearcmdentry(int firstchange)
Eric Andersencb57d552001-06-28 07:25:16 +00003789{
3790 struct tblentry **tblp;
3791 struct tblentry **pp;
3792 struct tblentry *cmdp;
3793
3794 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003795 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003796 pp = tblp;
3797 while ((cmdp = *pp) != NULL) {
3798 if ((cmdp->cmdtype == CMDNORMAL &&
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003799 cmdp->param.index >= firstchange)
3800 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange)) {
Eric Andersencb57d552001-06-28 07:25:16 +00003801 *pp = cmdp->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003802 free(cmdp);
Eric Andersencb57d552001-06-28 07:25:16 +00003803 } else {
3804 pp = &cmdp->next;
3805 }
3806 }
3807 }
3808 INTON;
3809}
3810
3811
3812/*
Eric Andersencb57d552001-06-28 07:25:16 +00003813 * Locate a command in the command hash table. If "add" is nonzero,
3814 * add the command to the table if it is not already present. The
3815 * variable "lastcmdentry" is set to point to the address of the link
3816 * pointing to the entry, so that delete_cmd_entry can delete the
3817 * entry.
3818 */
3819
Eric Andersen2870d962001-07-02 17:27:21 +00003820static struct tblentry **lastcmdentry;
Eric Andersencb57d552001-06-28 07:25:16 +00003821
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003822static struct tblentry *cmdlookup(const char *name, int add)
Eric Andersencb57d552001-06-28 07:25:16 +00003823{
3824 int hashval;
Eric Andersen2870d962001-07-02 17:27:21 +00003825 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +00003826 struct tblentry *cmdp;
3827 struct tblentry **pp;
3828
3829 p = name;
3830 hashval = *p << 4;
3831 while (*p)
3832 hashval += *p++;
3833 hashval &= 0x7FFF;
3834 pp = &cmdtable[hashval % CMDTABLESIZE];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003835 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00003836 if (equal(cmdp->cmdname, name))
3837 break;
3838 pp = &cmdp->next;
3839 }
3840 if (add && cmdp == NULL) {
3841 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003842 cmdp = *pp = xmalloc(sizeof(struct tblentry) - ARB
3843 + strlen(name) + 1);
Eric Andersencb57d552001-06-28 07:25:16 +00003844 cmdp->next = NULL;
3845 cmdp->cmdtype = CMDUNKNOWN;
3846 cmdp->rehash = 0;
3847 strcpy(cmdp->cmdname, name);
3848 INTON;
3849 }
3850 lastcmdentry = pp;
3851 return cmdp;
3852}
3853
3854/*
3855 * Delete the command entry returned on the last lookup.
3856 */
3857
Glenn L McGrath50812ff2002-08-23 13:14:48 +00003858static void delete_cmd_entry(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00003859{
Eric Andersencb57d552001-06-28 07:25:16 +00003860 struct tblentry *cmdp;
3861
3862 INTOFF;
3863 cmdp = *lastcmdentry;
3864 *lastcmdentry = cmdp->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003865 free(cmdp);
Eric Andersencb57d552001-06-28 07:25:16 +00003866 INTON;
3867}
3868
3869
3870
Eric Andersencb57d552001-06-28 07:25:16 +00003871
3872
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003873static const unsigned char nodesize[26] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003874 ALIGN(sizeof(struct nbinary)),
3875 ALIGN(sizeof(struct ncmd)),
3876 ALIGN(sizeof(struct npipe)),
3877 ALIGN(sizeof(struct nredir)),
3878 ALIGN(sizeof(struct nredir)),
3879 ALIGN(sizeof(struct nredir)),
3880 ALIGN(sizeof(struct nbinary)),
3881 ALIGN(sizeof(struct nbinary)),
3882 ALIGN(sizeof(struct nif)),
3883 ALIGN(sizeof(struct nbinary)),
3884 ALIGN(sizeof(struct nbinary)),
3885 ALIGN(sizeof(struct nfor)),
3886 ALIGN(sizeof(struct ncase)),
3887 ALIGN(sizeof(struct nclist)),
3888 ALIGN(sizeof(struct narg)),
3889 ALIGN(sizeof(struct narg)),
3890 ALIGN(sizeof(struct nfile)),
3891 ALIGN(sizeof(struct nfile)),
3892 ALIGN(sizeof(struct nfile)),
3893 ALIGN(sizeof(struct nfile)),
3894 ALIGN(sizeof(struct nfile)),
3895 ALIGN(sizeof(struct ndup)),
3896 ALIGN(sizeof(struct ndup)),
3897 ALIGN(sizeof(struct nhere)),
3898 ALIGN(sizeof(struct nhere)),
3899 ALIGN(sizeof(struct nnot)),
Eric Andersen62483552001-07-10 06:09:16 +00003900};
Eric Andersencb57d552001-06-28 07:25:16 +00003901
Eric Andersencb57d552001-06-28 07:25:16 +00003902
3903
3904/*
3905 * Delete a function if it exists.
3906 */
3907
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003908static void unsetfunc(char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003909{
Eric Andersencb57d552001-06-28 07:25:16 +00003910 struct tblentry *cmdp;
3911
3912 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
Aaron Lehmann49c024a2002-08-02 06:39:47 +00003913 free(cmdp->param.func);
Eric Andersencb57d552001-06-28 07:25:16 +00003914 delete_cmd_entry();
3915 }
3916}
3917
Eric Andersen2870d962001-07-02 17:27:21 +00003918
3919/*
Eric Andersencb57d552001-06-28 07:25:16 +00003920 * Locate and print what a word is...
3921 */
3922
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003923static int typecmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003924{
3925 int i;
3926 int err = 0;
Eric Andersen62483552001-07-10 06:09:16 +00003927 char *argv_a[2];
3928
3929 argv_a[1] = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003930
3931 for (i = 1; i < argc; i++) {
Eric Andersen62483552001-07-10 06:09:16 +00003932 argv_a[0] = argv[i];
3933 argptr = argv_a;
3934 optptr = "v";
Glenn L McGrath4501dbe2002-12-11 21:13:00 +00003935 err |= hashcmd(2, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00003936 }
3937 return err;
3938}
3939
Eric Andersend35c5df2002-01-09 15:37:36 +00003940#ifdef CONFIG_ASH_CMDCMD
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003941static int commandcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003942{
3943 int c;
3944 int default_path = 0;
3945 int verify_only = 0;
3946 int verbose_verify_only = 0;
3947
3948 while ((c = nextopt("pvV")) != '\0')
3949 switch (c) {
3950 case 'p':
3951 default_path = 1;
3952 break;
3953 case 'v':
3954 verify_only = 1;
3955 break;
3956 case 'V':
3957 verbose_verify_only = 1;
3958 break;
Eric Andersencb57d552001-06-28 07:25:16 +00003959 }
3960
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003961 if (default_path + verify_only + verbose_verify_only > 1 || !*argptr) {
3962 out2str("command [-p] command [arg ...]\n"
Eric Andersen62483552001-07-10 06:09:16 +00003963 "command {-v|-V} command\n");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003964 return EX_USAGE;
Eric Andersencb57d552001-06-28 07:25:16 +00003965 }
3966
Eric Andersencb57d552001-06-28 07:25:16 +00003967 if (verify_only || verbose_verify_only) {
Eric Andersen62483552001-07-10 06:09:16 +00003968 char *argv_a[2];
3969
3970 argv_a[1] = 0;
3971 argv_a[0] = *argptr;
3972 argptr = argv_a;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003973 optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
Eric Andersen62483552001-07-10 06:09:16 +00003974 return hashcmd(argc, argv);
Eric Andersencb57d552001-06-28 07:25:16 +00003975 }
Eric Andersencb57d552001-06-28 07:25:16 +00003976
3977 return 0;
3978}
Eric Andersen2870d962001-07-02 17:27:21 +00003979#endif
Eric Andersencb57d552001-06-28 07:25:16 +00003980
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003981static int path_change(const char *newval, int *bltin)
Eric Andersencb57d552001-06-28 07:25:16 +00003982{
3983 const char *old, *new;
3984 int idx;
3985 int firstchange;
3986
3987 old = pathval();
3988 new = newval;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003989 firstchange = 9999; /* assume no change */
Eric Andersencb57d552001-06-28 07:25:16 +00003990 idx = 0;
3991 *bltin = -1;
3992 for (;;) {
3993 if (*old != *new) {
3994 firstchange = idx;
3995 if ((*old == '\0' && *new == ':')
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003996 || (*old == ':' && *new == '\0'))
Eric Andersencb57d552001-06-28 07:25:16 +00003997 firstchange++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003998 old = new; /* ignore subsequent differences */
Eric Andersencb57d552001-06-28 07:25:16 +00003999 }
4000 if (*new == '\0')
4001 break;
4002 if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
4003 *bltin = idx;
4004 if (*new == ':') {
4005 idx++;
4006 }
4007 new++, old++;
4008 }
4009 if (builtinloc >= 0 && *bltin < 0)
4010 firstchange = 0;
4011 return firstchange;
4012}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004013
Eric Andersencb57d552001-06-28 07:25:16 +00004014/*
4015 * Routines to expand arguments to commands. We have to deal with
4016 * backquotes, shell variables, and file metacharacters.
4017 */
4018/*
4019 * _rmescape() flags
4020 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004021#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4022#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Eric Andersencb57d552001-06-28 07:25:16 +00004023
4024/*
4025 * Structure specifying which parts of the string should be searched
4026 * for IFS characters.
4027 */
4028
4029struct ifsregion {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004030 struct ifsregion *next; /* next region in list */
4031 int begoff; /* offset of start of region */
4032 int endoff; /* offset of end of region */
4033 int nulonly; /* search for nul bytes only */
Eric Andersencb57d552001-06-28 07:25:16 +00004034};
4035
4036
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004037static char *expdest; /* output of current string */
4038static struct nodelist *argbackq; /* list of back quote expressions */
4039static struct ifsregion ifsfirst; /* first struct in list of ifs regions */
4040static struct ifsregion *ifslastp; /* last struct in list */
4041static struct arglist exparg; /* holds expanded arg list */
Eric Andersencb57d552001-06-28 07:25:16 +00004042
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004043static void argstr(char *, int);
4044static char *exptilde(char *, int);
4045static void expbackq(union node *, int, int);
4046static int subevalvar(char *, char *, int, int, int, int, int);
4047static int varisset(char *, int);
4048static void strtodest(const char *, int, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004049static void varvalue(char *, int, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004050static void recordregion(int, int, int);
4051static void removerecordregions(int);
4052static void ifsbreakup(char *, struct arglist *);
4053static void ifsfree(void);
4054static void expandmeta(struct strlist *, int);
4055
Eric Andersen62483552001-07-10 06:09:16 +00004056#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersencb57d552001-06-28 07:25:16 +00004057#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
4058#if !defined(GLOB_BROKEN)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004059static void addglob(const glob_t *);
Eric Andersencb57d552001-06-28 07:25:16 +00004060#endif
4061#endif
Eric Andersen62483552001-07-10 06:09:16 +00004062#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004063static void expmeta(char *, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004064#endif
Eric Andersen62483552001-07-10 06:09:16 +00004065#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004066static struct strlist *expsort(struct strlist *);
4067static struct strlist *msort(struct strlist *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004068#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004069static int patmatch(char *, char *, int);
4070
Eric Andersen62483552001-07-10 06:09:16 +00004071#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004072static int patmatch2(char *, char *, int);
Eric Andersencb57d552001-06-28 07:25:16 +00004073#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004074static int pmatch(char *, char *, int);
4075
Eric Andersencb57d552001-06-28 07:25:16 +00004076#define patmatch2 patmatch
4077#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004078static char *cvtnum(int, char *);
Eric Andersencb57d552001-06-28 07:25:16 +00004079
4080/*
4081 * Expand shell variables and backquotes inside a here document.
4082 */
4083
Eric Andersen2870d962001-07-02 17:27:21 +00004084/* arg: the document, fd: where to write the expanded version */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004085static inline void expandhere(union node *arg, int fd)
Eric Andersen2870d962001-07-02 17:27:21 +00004086{
Eric Andersencb57d552001-06-28 07:25:16 +00004087 herefd = fd;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004088 expandarg(arg, (struct arglist *) NULL, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00004089 xwrite(fd, stackblock(), expdest - stackblock());
4090}
4091
4092
4093/*
4094 * Perform variable substitution and command substitution on an argument,
4095 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4096 * perform splitting and file name expansion. When arglist is NULL, perform
4097 * here document expansion.
4098 */
4099
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004100static void expandarg(union node *arg, struct arglist *arglist, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004101{
4102 struct strlist *sp;
4103 char *p;
4104
4105 argbackq = arg->narg.backquote;
4106 STARTSTACKSTR(expdest);
4107 ifsfirst.next = NULL;
4108 ifslastp = NULL;
4109 argstr(arg->narg.text, flag);
4110 if (arglist == NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004111 return; /* here document expanded */
Eric Andersencb57d552001-06-28 07:25:16 +00004112 }
4113 STPUTC('\0', expdest);
4114 p = grabstackstr(expdest);
4115 exparg.lastp = &exparg.list;
4116 /*
4117 * TODO - EXP_REDIR
4118 */
4119 if (flag & EXP_FULL) {
4120 ifsbreakup(p, &exparg);
4121 *exparg.lastp = NULL;
4122 exparg.lastp = &exparg.list;
4123 expandmeta(exparg.list, flag);
4124 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004125 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Eric Andersencb57d552001-06-28 07:25:16 +00004126 rmescapes(p);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004127 sp = (struct strlist *) stalloc(sizeof(struct strlist));
Eric Andersencb57d552001-06-28 07:25:16 +00004128 sp->text = p;
4129 *exparg.lastp = sp;
4130 exparg.lastp = &sp->next;
4131 }
4132 ifsfree();
4133 *exparg.lastp = NULL;
4134 if (exparg.list) {
4135 *arglist->lastp = exparg.list;
4136 arglist->lastp = exparg.lastp;
4137 }
4138}
4139
4140
Eric Andersen62483552001-07-10 06:09:16 +00004141/*
4142 * Expand a variable, and return a pointer to the next character in the
4143 * input string.
4144 */
4145
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004146static inline char *evalvar(char *p, int flag)
Eric Andersen62483552001-07-10 06:09:16 +00004147{
4148 int subtype;
4149 int varflags;
4150 char *var;
4151 const char *val;
4152 int patloc;
4153 int c;
4154 int set;
4155 int special;
4156 int startloc;
4157 int varlen;
4158 int easy;
4159 int quotes = flag & (EXP_FULL | EXP_CASE);
4160
4161 varflags = *p++;
4162 subtype = varflags & VSTYPE;
4163 var = p;
4164 special = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004165 if (!is_name(*p))
Eric Andersen62483552001-07-10 06:09:16 +00004166 special = 1;
4167 p = strchr(p, '=') + 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004168 again: /* jump here after setting a variable with ${var=text} */
Eric Andersen62483552001-07-10 06:09:16 +00004169 if (special) {
4170 set = varisset(var, varflags & VSNUL);
4171 val = NULL;
4172 } else {
4173 val = lookupvar(var);
4174 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
4175 val = NULL;
4176 set = 0;
4177 } else
4178 set = 1;
4179 }
4180 varlen = 0;
4181 startloc = expdest - stackblock();
4182 if (set && subtype != VSPLUS) {
4183 /* insert the value of the variable */
4184 if (special) {
4185 varvalue(var, varflags & VSQUOTE, flag);
4186 if (subtype == VSLENGTH) {
4187 varlen = expdest - stackblock() - startloc;
4188 STADJUST(-varlen, expdest);
4189 }
4190 } else {
4191 if (subtype == VSLENGTH) {
4192 varlen = strlen(val);
4193 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004194 strtodest(val,
4195 varflags & VSQUOTE ? DQSYNTAX : BASESYNTAX, quotes);
Eric Andersen62483552001-07-10 06:09:16 +00004196 }
4197 }
4198 }
4199
4200 if (subtype == VSPLUS)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004201 set = !set;
Eric Andersen62483552001-07-10 06:09:16 +00004202
4203 easy = ((varflags & VSQUOTE) == 0 ||
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004204 (*var == '@' && shellparam.nparam != 1));
Eric Andersen62483552001-07-10 06:09:16 +00004205
4206
4207 switch (subtype) {
4208 case VSLENGTH:
4209 expdest = cvtnum(varlen, expdest);
4210 goto record;
4211
4212 case VSNORMAL:
4213 if (!easy)
4214 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004215 record:
4216 recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE);
Eric Andersen62483552001-07-10 06:09:16 +00004217 break;
4218
4219 case VSPLUS:
4220 case VSMINUS:
4221 if (!set) {
4222 argstr(p, flag);
4223 break;
4224 }
4225 if (easy)
4226 goto record;
4227 break;
4228
4229 case VSTRIMLEFT:
4230 case VSTRIMLEFTMAX:
4231 case VSTRIMRIGHT:
4232 case VSTRIMRIGHTMAX:
4233 if (!set)
4234 break;
4235 /*
4236 * Terminate the string and start recording the pattern
4237 * right after it
4238 */
4239 STPUTC('\0', expdest);
4240 patloc = expdest - stackblock();
4241 if (subevalvar(p, NULL, patloc, subtype,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004242 startloc, varflags, quotes) == 0) {
Eric Andersen62483552001-07-10 06:09:16 +00004243 int amount = (expdest - stackblock() - patloc) + 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004244
Eric Andersen62483552001-07-10 06:09:16 +00004245 STADJUST(-amount, expdest);
4246 }
4247 /* Remove any recorded regions beyond start of variable */
4248 removerecordregions(startloc);
4249 goto record;
4250
4251 case VSASSIGN:
4252 case VSQUESTION:
4253 if (!set) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004254 if (subevalvar(p, var, 0, subtype, startloc, varflags, quotes)) {
Eric Andersen62483552001-07-10 06:09:16 +00004255 varflags &= ~VSNUL;
4256 /*
4257 * Remove any recorded regions beyond
4258 * start of variable
4259 */
4260 removerecordregions(startloc);
4261 goto again;
4262 }
4263 break;
4264 }
4265 if (easy)
4266 goto record;
4267 break;
4268
4269#ifdef DEBUG
4270 default:
4271 abort();
4272#endif
4273 }
4274
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004275 if (subtype != VSNORMAL) { /* skip to end of alternative */
Eric Andersen62483552001-07-10 06:09:16 +00004276 int nesting = 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004277
Eric Andersen62483552001-07-10 06:09:16 +00004278 for (;;) {
4279 if ((c = *p++) == CTLESC)
4280 p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004281 else if (c == CTLBACKQ || c == (CTLBACKQ | CTLQUOTE)) {
Eric Andersen62483552001-07-10 06:09:16 +00004282 if (set)
4283 argbackq = argbackq->next;
4284 } else if (c == CTLVAR) {
4285 if ((*p++ & VSTYPE) != VSNORMAL)
4286 nesting++;
4287 } else if (c == CTLENDVAR) {
4288 if (--nesting == 0)
4289 break;
4290 }
4291 }
4292 }
4293 return p;
4294}
4295
Eric Andersencb57d552001-06-28 07:25:16 +00004296
4297/*
4298 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4299 * characters to allow for further processing. Otherwise treat
4300 * $@ like $* since no splitting will be performed.
4301 */
4302
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004303static void argstr(char *p, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004304{
4305 char c;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004306 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
Eric Andersencb57d552001-06-28 07:25:16 +00004307 int firsteq = 1;
4308
4309 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
4310 p = exptilde(p, flag);
4311 for (;;) {
4312 switch (c = *p++) {
4313 case '\0':
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004314 case CTLENDVAR: /* ??? */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004315 return;
Eric Andersencb57d552001-06-28 07:25:16 +00004316 case CTLQUOTEMARK:
4317 /* "$@" syntax adherence hack */
4318 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
4319 break;
4320 if ((flag & EXP_FULL) != 0)
4321 STPUTC(c, expdest);
4322 break;
4323 case CTLESC:
4324 if (quotes)
4325 STPUTC(c, expdest);
4326 c = *p++;
4327 STPUTC(c, expdest);
4328 break;
4329 case CTLVAR:
4330 p = evalvar(p, flag);
4331 break;
4332 case CTLBACKQ:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004333 case CTLBACKQ | CTLQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +00004334 expbackq(argbackq->n, c & CTLQUOTE, flag);
4335 argbackq = argbackq->next;
4336 break;
Eric Andersend35c5df2002-01-09 15:37:36 +00004337#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00004338 case CTLENDARI:
4339 expari(flag);
4340 break;
Eric Andersen2870d962001-07-02 17:27:21 +00004341#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004342 case ':':
4343 case '=':
4344 /*
4345 * sort of a hack - expand tildes in variable
4346 * assignments (after the first '=' and after ':'s).
4347 */
4348 STPUTC(c, expdest);
4349 if (flag & EXP_VARTILDE && *p == '~') {
4350 if (c == '=') {
4351 if (firsteq)
4352 firsteq = 0;
4353 else
4354 break;
4355 }
4356 p = exptilde(p, flag);
4357 }
4358 break;
4359 default:
4360 STPUTC(c, expdest);
4361 }
4362 }
Eric Andersencb57d552001-06-28 07:25:16 +00004363}
4364
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004365static char *exptilde(char *p, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004366{
4367 char c, *startp = p;
4368 struct passwd *pw;
4369 const char *home;
4370 int quotes = flag & (EXP_FULL | EXP_CASE);
4371
4372 while ((c = *p) != '\0') {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004373 switch (c) {
Eric Andersencb57d552001-06-28 07:25:16 +00004374 case CTLESC:
4375 return (startp);
4376 case CTLQUOTEMARK:
4377 return (startp);
4378 case ':':
4379 if (flag & EXP_VARTILDE)
4380 goto done;
4381 break;
4382 case '/':
4383 goto done;
4384 }
4385 p++;
4386 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004387 done:
Eric Andersencb57d552001-06-28 07:25:16 +00004388 *p = '\0';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004389 if (*(startp + 1) == '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00004390 if ((home = lookupvar("HOME")) == NULL)
4391 goto lose;
4392 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004393 if ((pw = getpwnam(startp + 1)) == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00004394 goto lose;
4395 home = pw->pw_dir;
4396 }
4397 if (*home == '\0')
4398 goto lose;
4399 *p = c;
4400 strtodest(home, SQSYNTAX, quotes);
4401 return (p);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004402 lose:
Eric Andersencb57d552001-06-28 07:25:16 +00004403 *p = c;
4404 return (startp);
4405}
4406
4407
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004408static void removerecordregions(int endoff)
Eric Andersencb57d552001-06-28 07:25:16 +00004409{
4410 if (ifslastp == NULL)
4411 return;
4412
4413 if (ifsfirst.endoff > endoff) {
4414 while (ifsfirst.next != NULL) {
4415 struct ifsregion *ifsp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004416
Eric Andersencb57d552001-06-28 07:25:16 +00004417 INTOFF;
4418 ifsp = ifsfirst.next->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00004419 free(ifsfirst.next);
Eric Andersencb57d552001-06-28 07:25:16 +00004420 ifsfirst.next = ifsp;
4421 INTON;
4422 }
4423 if (ifsfirst.begoff > endoff)
4424 ifslastp = NULL;
4425 else {
4426 ifslastp = &ifsfirst;
4427 ifsfirst.endoff = endoff;
4428 }
4429 return;
4430 }
Eric Andersen2870d962001-07-02 17:27:21 +00004431
Eric Andersencb57d552001-06-28 07:25:16 +00004432 ifslastp = &ifsfirst;
4433 while (ifslastp->next && ifslastp->next->begoff < endoff)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004434 ifslastp = ifslastp->next;
Eric Andersencb57d552001-06-28 07:25:16 +00004435 while (ifslastp->next != NULL) {
4436 struct ifsregion *ifsp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004437
Eric Andersencb57d552001-06-28 07:25:16 +00004438 INTOFF;
4439 ifsp = ifslastp->next->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00004440 free(ifslastp->next);
Eric Andersencb57d552001-06-28 07:25:16 +00004441 ifslastp->next = ifsp;
4442 INTON;
4443 }
4444 if (ifslastp->endoff > endoff)
4445 ifslastp->endoff = endoff;
4446}
4447
4448
Eric Andersend35c5df2002-01-09 15:37:36 +00004449#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +00004450/*
4451 * Expand arithmetic expression. Backup to start of expression,
4452 * evaluate, place result in (backed up) result, adjust string position.
4453 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004454static void expari(int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004455{
4456 char *p, *start;
Eric Andersen34506362001-08-02 05:02:46 +00004457 int errcode;
Eric Andersencb57d552001-06-28 07:25:16 +00004458 int result;
4459 int begoff;
4460 int quotes = flag & (EXP_FULL | EXP_CASE);
4461 int quoted;
4462
Eric Andersen2870d962001-07-02 17:27:21 +00004463 /* ifsfree(); */
Eric Andersencb57d552001-06-28 07:25:16 +00004464
4465 /*
4466 * This routine is slightly over-complicated for
4467 * efficiency. First we make sure there is
4468 * enough space for the result, which may be bigger
4469 * than the expression if we add exponentation. Next we
4470 * scan backwards looking for the start of arithmetic. If the
4471 * next previous character is a CTLESC character, then we
4472 * have to rescan starting from the beginning since CTLESC
4473 * characters have to be processed left to right.
4474 */
4475 CHECKSTRSPACE(10, expdest);
4476 USTPUTC('\0', expdest);
4477 start = stackblock();
4478 p = expdest - 1;
4479 while (*p != CTLARI && p >= start)
4480 --p;
4481 if (*p != CTLARI)
4482 error("missing CTLARI (shouldn't happen)");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004483 if (p > start && *(p - 1) == CTLESC)
Eric Andersencb57d552001-06-28 07:25:16 +00004484 for (p = start; *p != CTLARI; p++)
4485 if (*p == CTLESC)
4486 p++;
4487
4488 if (p[1] == '"')
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004489 quoted = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00004490 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004491 quoted = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00004492 begoff = p - start;
4493 removerecordregions(begoff);
4494 if (quotes)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004495 rmescapes(p + 2);
4496 result = arith(p + 2, &errcode);
Eric Andersen34506362001-08-02 05:02:46 +00004497 if (errcode < 0) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004498 if (errcode == -2)
Eric Andersen34506362001-08-02 05:02:46 +00004499 error("divide by zero");
4500 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004501 error("syntax error: \"%s\"\n", p + 2);
Eric Andersen34506362001-08-02 05:02:46 +00004502 }
Eric Andersen3102ac42001-07-06 04:26:23 +00004503 snprintf(p, 12, "%d", result);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004504 while (*p++);
Eric Andersencb57d552001-06-28 07:25:16 +00004505
4506 if (quoted == 0)
4507 recordregion(begoff, p - 1 - start, 0);
4508 result = expdest - p + 1;
4509 STADJUST(-result, expdest);
4510}
Eric Andersen2870d962001-07-02 17:27:21 +00004511#endif
Eric Andersencb57d552001-06-28 07:25:16 +00004512
4513/*
4514 * Expand stuff in backwards quotes.
4515 */
4516
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004517static void expbackq(union node *cmd, int quoted, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00004518{
4519 volatile struct backcmd in;
4520 int i;
4521 char buf[128];
4522 char *p;
4523 char *dest = expdest;
4524 volatile struct ifsregion saveifs;
4525 struct ifsregion *volatile savelastp;
4526 struct nodelist *volatile saveargbackq;
4527 char lastc;
4528 int startloc = dest - stackblock();
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004529 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Eric Andersencb57d552001-06-28 07:25:16 +00004530 volatile int saveherefd;
4531 int quotes = flag & (EXP_FULL | EXP_CASE);
4532 struct jmploc jmploc;
4533 struct jmploc *volatile savehandler;
4534 int ex;
4535
4536#if __GNUC__
4537 /* Avoid longjmp clobbering */
4538 (void) &dest;
4539 (void) &syntax;
4540#endif
4541
4542 in.fd = -1;
4543 in.buf = 0;
4544 in.jp = 0;
4545
4546 INTOFF;
4547 saveifs = ifsfirst;
4548 savelastp = ifslastp;
4549 saveargbackq = argbackq;
4550 saveherefd = herefd;
4551 herefd = -1;
4552 if ((ex = setjmp(jmploc.loc))) {
4553 goto err1;
4554 }
4555 savehandler = handler;
4556 handler = &jmploc;
4557 INTON;
4558 p = grabstackstr(dest);
4559 evalbackcmd(cmd, (struct backcmd *) &in);
4560 ungrabstackstr(p, dest);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004561 err1:
Eric Andersencb57d552001-06-28 07:25:16 +00004562 INTOFF;
4563 ifsfirst = saveifs;
4564 ifslastp = savelastp;
4565 argbackq = saveargbackq;
4566 herefd = saveherefd;
4567 if (ex) {
4568 goto err2;
4569 }
4570
4571 p = in.buf;
4572 lastc = '\0';
4573 for (;;) {
4574 if (--in.nleft < 0) {
4575 if (in.fd < 0)
4576 break;
Eric Andersen7467c8d2001-07-12 20:26:32 +00004577 i = safe_read(in.fd, buf, sizeof buf);
Eric Andersencb57d552001-06-28 07:25:16 +00004578 TRACE(("expbackq: read returns %d\n", i));
4579 if (i <= 0)
4580 break;
4581 p = buf;
4582 in.nleft = i - 1;
4583 }
4584 lastc = *p++;
4585 if (lastc != '\0') {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004586 if (quotes && SIT(lastc, syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00004587 STPUTC(CTLESC, dest);
4588 STPUTC(lastc, dest);
4589 }
4590 }
4591
4592 /* Eat all trailing newlines */
4593 for (; dest > stackblock() && dest[-1] == '\n';)
4594 STUNPUTC(dest);
4595
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004596 err2:
Eric Andersencb57d552001-06-28 07:25:16 +00004597 if (in.fd >= 0)
4598 close(in.fd);
Aaron Lehmann49c024a2002-08-02 06:39:47 +00004599 free(in.buf);
Eric Andersencb57d552001-06-28 07:25:16 +00004600 if (in.jp)
4601 exitstatus = waitforjob(in.jp);
4602 handler = savehandler;
4603 if (ex) {
4604 longjmp(handler->loc, 1);
4605 }
4606 if (quoted == 0)
4607 recordregion(startloc, dest - stackblock(), 0);
4608 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004609 (dest - stackblock()) - startloc,
4610 (dest - stackblock()) - startloc, stackblock() + startloc));
Eric Andersencb57d552001-06-28 07:25:16 +00004611 expdest = dest;
4612 INTON;
4613}
4614
Eric Andersencb57d552001-06-28 07:25:16 +00004615static int
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004616subevalvar(char *p, char *str, int strloc, int subtype, int startloc,
4617 int varflags, int quotes)
Eric Andersencb57d552001-06-28 07:25:16 +00004618{
4619 char *startp;
4620 char *loc = NULL;
4621 char *q;
4622 int c = 0;
4623 int saveherefd = herefd;
4624 struct nodelist *saveargbackq = argbackq;
4625 int amount;
4626
4627 herefd = -1;
4628 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
4629 STACKSTRNUL(expdest);
4630 herefd = saveherefd;
4631 argbackq = saveargbackq;
4632 startp = stackblock() + startloc;
4633 if (str == NULL)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004634 str = stackblock() + strloc;
Eric Andersencb57d552001-06-28 07:25:16 +00004635
4636 switch (subtype) {
4637 case VSASSIGN:
4638 setvar(str, startp, 0);
4639 amount = startp - expdest;
4640 STADJUST(amount, expdest);
4641 varflags &= ~VSNUL;
4642 if (c != 0)
4643 *loc = c;
4644 return 1;
4645
4646 case VSQUESTION:
4647 if (*p != CTLENDVAR) {
Eric Andersen3102ac42001-07-06 04:26:23 +00004648 out2fmt(snlfmt, startp);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004649 error((char *) NULL);
Eric Andersencb57d552001-06-28 07:25:16 +00004650 }
4651 error("%.*s: parameter %snot set", p - str - 1,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004652 str, (varflags & VSNUL) ? "null or " : nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00004653 /* NOTREACHED */
4654
4655 case VSTRIMLEFT:
4656 for (loc = startp; loc < str; loc++) {
4657 c = *loc;
4658 *loc = '\0';
4659 if (patmatch2(str, startp, quotes))
4660 goto recordleft;
4661 *loc = c;
4662 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00004663 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00004664 }
4665 return 0;
4666
4667 case VSTRIMLEFTMAX:
4668 for (loc = str - 1; loc >= startp;) {
4669 c = *loc;
4670 *loc = '\0';
4671 if (patmatch2(str, startp, quotes))
4672 goto recordleft;
4673 *loc = c;
4674 loc--;
4675 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
4676 for (q = startp; q < loc; q++)
4677 if (*q == CTLESC)
4678 q++;
4679 if (q > loc)
4680 loc--;
4681 }
4682 }
4683 return 0;
4684
4685 case VSTRIMRIGHT:
Eric Andersen2870d962001-07-02 17:27:21 +00004686 for (loc = str - 1; loc >= startp;) {
Eric Andersencb57d552001-06-28 07:25:16 +00004687 if (patmatch2(str, loc, quotes))
4688 goto recordright;
4689 loc--;
Eric Andersen2870d962001-07-02 17:27:21 +00004690 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
Eric Andersencb57d552001-06-28 07:25:16 +00004691 for (q = startp; q < loc; q++)
4692 if (*q == CTLESC)
4693 q++;
4694 if (q > loc)
4695 loc--;
4696 }
4697 }
4698 return 0;
4699
4700 case VSTRIMRIGHTMAX:
4701 for (loc = startp; loc < str - 1; loc++) {
4702 if (patmatch2(str, loc, quotes))
4703 goto recordright;
4704 if (quotes && *loc == CTLESC)
Eric Andersen2870d962001-07-02 17:27:21 +00004705 loc++;
Eric Andersencb57d552001-06-28 07:25:16 +00004706 }
4707 return 0;
4708
4709#ifdef DEBUG
4710 default:
4711 abort();
4712#endif
4713 }
4714
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004715 recordleft:
Eric Andersencb57d552001-06-28 07:25:16 +00004716 *loc = c;
4717 amount = ((str - 1) - (loc - startp)) - expdest;
4718 STADJUST(amount, expdest);
4719 while (loc != str - 1)
4720 *startp++ = *loc++;
4721 return 1;
4722
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004723 recordright:
Eric Andersencb57d552001-06-28 07:25:16 +00004724 amount = loc - expdest;
4725 STADJUST(amount, expdest);
4726 STPUTC('\0', expdest);
4727 STADJUST(-1, expdest);
4728 return 1;
4729}
4730
4731
4732/*
Eric Andersencb57d552001-06-28 07:25:16 +00004733 * Test whether a specialized variable is set.
4734 */
4735
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004736static int varisset(char *name, int nulok)
Eric Andersencb57d552001-06-28 07:25:16 +00004737{
4738 if (*name == '!')
4739 return backgndpid != -1;
4740 else if (*name == '@' || *name == '*') {
4741 if (*shellparam.p == NULL)
4742 return 0;
4743
4744 if (nulok) {
4745 char **av;
4746
4747 for (av = shellparam.p; *av; av++)
4748 if (**av != '\0')
4749 return 1;
4750 return 0;
4751 }
4752 } else if (is_digit(*name)) {
4753 char *ap;
4754 int num = atoi(name);
4755
4756 if (num > shellparam.nparam)
4757 return 0;
4758
4759 if (num == 0)
4760 ap = arg0;
4761 else
4762 ap = shellparam.p[num - 1];
4763
4764 if (nulok && (ap == NULL || *ap == '\0'))
4765 return 0;
4766 }
4767 return 1;
4768}
4769
Eric Andersencb57d552001-06-28 07:25:16 +00004770/*
4771 * Put a string on the stack.
4772 */
4773
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004774static void strtodest(const char *p, int syntax, int quotes)
Eric Andersencb57d552001-06-28 07:25:16 +00004775{
4776 while (*p) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004777 if (quotes && SIT(*p, syntax) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +00004778 STPUTC(CTLESC, expdest);
4779 STPUTC(*p++, expdest);
4780 }
4781}
4782
Eric Andersencb57d552001-06-28 07:25:16 +00004783/*
4784 * Add the value of a specialized variable to the stack string.
4785 */
4786
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004787static void varvalue(char *name, int quoted, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00004788{
4789 int num;
4790 char *p;
4791 int i;
4792 int sep;
4793 int sepq = 0;
4794 char **ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00004795 int syntax;
Eric Andersencb57d552001-06-28 07:25:16 +00004796 int allow_split = flags & EXP_FULL;
4797 int quotes = flags & (EXP_FULL | EXP_CASE);
4798
4799 syntax = quoted ? DQSYNTAX : BASESYNTAX;
4800 switch (*name) {
4801 case '$':
4802 num = rootpid;
4803 goto numvar;
4804 case '?':
4805 num = oexitstatus;
4806 goto numvar;
4807 case '#':
4808 num = shellparam.nparam;
4809 goto numvar;
4810 case '!':
4811 num = backgndpid;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004812 numvar:
Eric Andersencb57d552001-06-28 07:25:16 +00004813 expdest = cvtnum(num, expdest);
4814 break;
4815 case '-':
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004816 for (i = 0; i < NOPTS; i++) {
Eric Andersen2870d962001-07-02 17:27:21 +00004817 if (optent_val(i))
4818 STPUTC(optent_letter(optlist[i]), expdest);
Eric Andersencb57d552001-06-28 07:25:16 +00004819 }
4820 break;
4821 case '@':
4822 if (allow_split && quoted) {
4823 sep = 1 << CHAR_BIT;
4824 goto param;
4825 }
4826 /* fall through */
4827 case '*':
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004828 sep = ifsset()? ifsval()[0] : ' ';
Eric Andersencb57d552001-06-28 07:25:16 +00004829 if (quotes) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004830 sepq = SIT(sep, syntax) == CCTL;
Eric Andersencb57d552001-06-28 07:25:16 +00004831 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004832 param:
4833 for (ap = shellparam.p; (p = *ap++) != NULL;) {
Eric Andersencb57d552001-06-28 07:25:16 +00004834 strtodest(p, syntax, quotes);
4835 if (*ap && sep) {
4836 if (sepq)
4837 STPUTC(CTLESC, expdest);
4838 STPUTC(sep, expdest);
4839 }
4840 }
4841 break;
4842 case '0':
4843 strtodest(arg0, syntax, quotes);
4844 break;
4845 default:
4846 num = atoi(name);
4847 if (num > 0 && num <= shellparam.nparam) {
4848 strtodest(shellparam.p[num - 1], syntax, quotes);
4849 }
4850 break;
4851 }
4852}
4853
4854
Eric Andersencb57d552001-06-28 07:25:16 +00004855/*
4856 * Record the fact that we have to scan this region of the
4857 * string for IFS characters.
4858 */
4859
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004860static void recordregion(int start, int end, int nulonly)
Eric Andersencb57d552001-06-28 07:25:16 +00004861{
4862 struct ifsregion *ifsp;
4863
4864 if (ifslastp == NULL) {
4865 ifsp = &ifsfirst;
4866 } else {
4867 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004868 ifsp = (struct ifsregion *) xmalloc(sizeof(struct ifsregion));
Eric Andersencb57d552001-06-28 07:25:16 +00004869 ifsp->next = NULL;
4870 ifslastp->next = ifsp;
4871 INTON;
4872 }
4873 ifslastp = ifsp;
4874 ifslastp->begoff = start;
4875 ifslastp->endoff = end;
4876 ifslastp->nulonly = nulonly;
4877}
4878
4879
4880
4881/*
4882 * Break the argument string into pieces based upon IFS and add the
4883 * strings to the argument list. The regions of the string to be
4884 * searched for IFS characters have been stored by recordregion.
4885 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004886static void ifsbreakup(char *string, struct arglist *arglist)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00004887{
Eric Andersencb57d552001-06-28 07:25:16 +00004888 struct ifsregion *ifsp;
4889 struct strlist *sp;
4890 char *start;
4891 char *p;
4892 char *q;
4893 const char *ifs, *realifs;
4894 int ifsspc;
4895 int nulonly;
4896
4897
4898 start = string;
4899 ifsspc = 0;
4900 nulonly = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004901 realifs = ifsset()? ifsval() : defifs;
Eric Andersencb57d552001-06-28 07:25:16 +00004902 if (ifslastp != NULL) {
4903 ifsp = &ifsfirst;
4904 do {
4905 p = string + ifsp->begoff;
4906 nulonly = ifsp->nulonly;
4907 ifs = nulonly ? nullstr : realifs;
4908 ifsspc = 0;
4909 while (p < string + ifsp->endoff) {
4910 q = p;
4911 if (*p == CTLESC)
4912 p++;
4913 if (strchr(ifs, *p)) {
4914 if (!nulonly)
4915 ifsspc = (strchr(defifs, *p) != NULL);
4916 /* Ignore IFS whitespace at start */
4917 if (q == start && ifsspc) {
4918 p++;
4919 start = p;
4920 continue;
4921 }
4922 *q = '\0';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004923 sp = (struct strlist *) stalloc(sizeof *sp);
Eric Andersencb57d552001-06-28 07:25:16 +00004924 sp->text = start;
4925 *arglist->lastp = sp;
4926 arglist->lastp = &sp->next;
4927 p++;
4928 if (!nulonly) {
4929 for (;;) {
4930 if (p >= string + ifsp->endoff) {
4931 break;
4932 }
4933 q = p;
4934 if (*p == CTLESC)
4935 p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004936 if (strchr(ifs, *p) == NULL) {
Eric Andersencb57d552001-06-28 07:25:16 +00004937 p = q;
4938 break;
4939 } else if (strchr(defifs, *p) == NULL) {
4940 if (ifsspc) {
4941 p++;
4942 ifsspc = 0;
4943 } else {
4944 p = q;
4945 break;
4946 }
4947 } else
4948 p++;
4949 }
4950 }
4951 start = p;
4952 } else
4953 p++;
4954 }
4955 } while ((ifsp = ifsp->next) != NULL);
4956 if (!(*start || (!ifsspc && start > string && nulonly))) {
4957 return;
4958 }
4959 }
4960
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004961 sp = (struct strlist *) stalloc(sizeof *sp);
Eric Andersencb57d552001-06-28 07:25:16 +00004962 sp->text = start;
4963 *arglist->lastp = sp;
4964 arglist->lastp = &sp->next;
4965}
4966
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004967static void ifsfree(void)
Eric Andersencb57d552001-06-28 07:25:16 +00004968{
4969 while (ifsfirst.next != NULL) {
4970 struct ifsregion *ifsp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004971
Eric Andersencb57d552001-06-28 07:25:16 +00004972 INTOFF;
4973 ifsp = ifsfirst.next->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00004974 free(ifsfirst.next);
Eric Andersencb57d552001-06-28 07:25:16 +00004975 ifsfirst.next = ifsp;
4976 INTON;
4977 }
4978 ifslastp = NULL;
4979 ifsfirst.next = NULL;
4980}
4981
Eric Andersen2870d962001-07-02 17:27:21 +00004982/*
4983 * Add a file name to the list.
4984 */
Eric Andersencb57d552001-06-28 07:25:16 +00004985
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004986static void addfname(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00004987{
Eric Andersen2870d962001-07-02 17:27:21 +00004988 struct strlist *sp;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004989 size_t len = strlen(name) + 1;
Eric Andersen2870d962001-07-02 17:27:21 +00004990
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00004991 sp = (struct strlist *) stalloc(sizeof *sp);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00004992 sp->text = memcpy(stalloc(len), name, len);
Eric Andersen2870d962001-07-02 17:27:21 +00004993 *exparg.lastp = sp;
4994 exparg.lastp = &sp->next;
4995}
Eric Andersencb57d552001-06-28 07:25:16 +00004996
4997/*
4998 * Expand shell metacharacters. At this point, the only control characters
4999 * should be escapes. The results are stored in the list exparg.
5000 */
5001
Eric Andersen62483552001-07-10 06:09:16 +00005002#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005003static void expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005004{
5005 const char *p;
5006 glob_t pglob;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005007
Eric Andersencb57d552001-06-28 07:25:16 +00005008 /* TODO - EXP_REDIR */
5009
5010 while (str) {
5011 if (fflag)
5012 goto nometa;
5013 p = preglob(str->text);
5014 INTOFF;
Eric Andersen34506362001-08-02 05:02:46 +00005015 switch (glob(p, 0, 0, &pglob)) {
Eric Andersencb57d552001-06-28 07:25:16 +00005016 case 0:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005017 if (pglob.gl_pathv[1] == 0 && !strcmp(p, pglob.gl_pathv[0]))
Eric Andersencb57d552001-06-28 07:25:16 +00005018 goto nometa2;
5019 addglob(&pglob);
5020 globfree(&pglob);
5021 INTON;
5022 break;
5023 case GLOB_NOMATCH:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005024 nometa2:
Eric Andersencb57d552001-06-28 07:25:16 +00005025 globfree(&pglob);
5026 INTON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005027 nometa:
Eric Andersencb57d552001-06-28 07:25:16 +00005028 *exparg.lastp = str;
5029 rmescapes(str->text);
5030 exparg.lastp = &str->next;
5031 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005032 default: /* GLOB_NOSPACE */
Eric Andersencb57d552001-06-28 07:25:16 +00005033 error("Out of space");
5034 }
5035 str = str->next;
5036 }
5037}
5038
5039
5040/*
5041 * Add the result of glob(3) to the list.
5042 */
5043
Glenn L McGrath50812ff2002-08-23 13:14:48 +00005044static void addglob(const glob_t * pglob)
Eric Andersencb57d552001-06-28 07:25:16 +00005045{
5046 char **p = pglob->gl_pathv;
5047
5048 do {
5049 addfname(*p);
5050 } while (*++p);
5051}
5052
5053
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005054#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005055static char *expdir;
5056
5057
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005058static void expandmeta(struct strlist *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005059{
5060 char *p;
5061 struct strlist **savelastp;
5062 struct strlist *sp;
5063 char c;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005064
Eric Andersencb57d552001-06-28 07:25:16 +00005065 /* TODO - EXP_REDIR */
5066
5067 while (str) {
5068 if (fflag)
5069 goto nometa;
5070 p = str->text;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005071 for (;;) { /* fast check for meta chars */
Eric Andersencb57d552001-06-28 07:25:16 +00005072 if ((c = *p++) == '\0')
5073 goto nometa;
5074 if (c == '*' || c == '?' || c == '[' || c == '!')
5075 break;
5076 }
5077 savelastp = exparg.lastp;
5078 INTOFF;
5079 if (expdir == NULL) {
5080 int i = strlen(str->text);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005081
5082 expdir = xmalloc(i < 2048 ? 2048 : i); /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00005083 }
5084
5085 expmeta(expdir, str->text);
Aaron Lehmann49c024a2002-08-02 06:39:47 +00005086 free(expdir);
Eric Andersencb57d552001-06-28 07:25:16 +00005087 expdir = NULL;
5088 INTON;
5089 if (exparg.lastp == savelastp) {
5090 /*
5091 * no matches
5092 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005093 nometa:
Eric Andersencb57d552001-06-28 07:25:16 +00005094 *exparg.lastp = str;
5095 rmescapes(str->text);
5096 exparg.lastp = &str->next;
5097 } else {
5098 *exparg.lastp = NULL;
5099 *savelastp = sp = expsort(*savelastp);
5100 while (sp->next != NULL)
5101 sp = sp->next;
5102 exparg.lastp = &sp->next;
5103 }
5104 str = str->next;
5105 }
5106}
5107
5108
5109/*
5110 * Do metacharacter (i.e. *, ?, [...]) expansion.
5111 */
5112
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005113static void expmeta(char *enddir, char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005114{
Eric Andersencb57d552001-06-28 07:25:16 +00005115 char *p;
5116 const char *cp;
5117 char *q;
5118 char *start;
5119 char *endname;
5120 int metaflag;
5121 struct stat statb;
5122 DIR *dirp;
5123 struct dirent *dp;
5124 int atend;
5125 int matchdot;
5126
5127 metaflag = 0;
5128 start = name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005129 for (p = name;; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +00005130 if (*p == '*' || *p == '?')
5131 metaflag = 1;
5132 else if (*p == '[') {
5133 q = p + 1;
5134 if (*q == '!')
5135 q++;
5136 for (;;) {
5137 while (*q == CTLQUOTEMARK)
5138 q++;
5139 if (*q == CTLESC)
5140 q++;
5141 if (*q == '/' || *q == '\0')
5142 break;
5143 if (*++q == ']') {
5144 metaflag = 1;
5145 break;
5146 }
5147 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00005148 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
Eric Andersencb57d552001-06-28 07:25:16 +00005149 metaflag = 1;
5150 } else if (*p == '\0')
5151 break;
5152 else if (*p == CTLQUOTEMARK)
5153 continue;
5154 else if (*p == CTLESC)
5155 p++;
5156 if (*p == '/') {
5157 if (metaflag)
5158 break;
5159 start = p + 1;
5160 }
5161 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005162 if (metaflag == 0) { /* we've reached the end of the file name */
Eric Andersencb57d552001-06-28 07:25:16 +00005163 if (enddir != expdir)
5164 metaflag++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005165 for (p = name;; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +00005166 if (*p == CTLQUOTEMARK)
5167 continue;
5168 if (*p == CTLESC)
5169 p++;
5170 *enddir++ = *p;
5171 if (*p == '\0')
5172 break;
5173 }
5174 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
5175 addfname(expdir);
5176 return;
5177 }
5178 endname = p;
5179 if (start != name) {
5180 p = name;
5181 while (p < start) {
5182 while (*p == CTLQUOTEMARK)
5183 p++;
5184 if (*p == CTLESC)
5185 p++;
5186 *enddir++ = *p++;
5187 }
5188 }
5189 if (enddir == expdir) {
5190 cp = ".";
5191 } else if (enddir == expdir + 1 && *expdir == '/') {
5192 cp = "/";
5193 } else {
5194 cp = expdir;
5195 enddir[-1] = '\0';
5196 }
5197 if ((dirp = opendir(cp)) == NULL)
5198 return;
5199 if (enddir != expdir)
5200 enddir[-1] = '/';
5201 if (*endname == 0) {
5202 atend = 1;
5203 } else {
5204 atend = 0;
5205 *endname++ = '\0';
5206 }
5207 matchdot = 0;
5208 p = start;
5209 while (*p == CTLQUOTEMARK)
5210 p++;
5211 if (*p == CTLESC)
5212 p++;
5213 if (*p == '.')
5214 matchdot++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005215 while (!int_pending() && (dp = readdir(dirp)) != NULL) {
5216 if (dp->d_name[0] == '.' && !matchdot)
Eric Andersencb57d552001-06-28 07:25:16 +00005217 continue;
5218 if (patmatch(start, dp->d_name, 0)) {
5219 if (atend) {
Eric Andersen2870d962001-07-02 17:27:21 +00005220 strcpy(enddir, dp->d_name);
Eric Andersencb57d552001-06-28 07:25:16 +00005221 addfname(expdir);
5222 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005223 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
Eric Andersencb57d552001-06-28 07:25:16 +00005224 continue;
5225 p[-1] = '/';
5226 expmeta(p, endname);
5227 }
5228 }
5229 }
5230 closedir(dirp);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005231 if (!atend)
Eric Andersencb57d552001-06-28 07:25:16 +00005232 endname[-1] = '/';
5233}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005234#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
Eric Andersencb57d552001-06-28 07:25:16 +00005235
5236
Eric Andersencb57d552001-06-28 07:25:16 +00005237
Eric Andersen62483552001-07-10 06:09:16 +00005238#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
Eric Andersencb57d552001-06-28 07:25:16 +00005239/*
5240 * Sort the results of file name expansion. It calculates the number of
5241 * strings to sort and then calls msort (short for merge sort) to do the
5242 * work.
5243 */
5244
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005245static struct strlist *expsort(struct strlist *str)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005246{
Eric Andersencb57d552001-06-28 07:25:16 +00005247 int len;
5248 struct strlist *sp;
5249
5250 len = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005251 for (sp = str; sp; sp = sp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00005252 len++;
5253 return msort(str, len);
5254}
5255
5256
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005257static struct strlist *msort(struct strlist *list, int len)
Eric Andersencb57d552001-06-28 07:25:16 +00005258{
5259 struct strlist *p, *q = NULL;
5260 struct strlist **lpp;
5261 int half;
5262 int n;
5263
5264 if (len <= 1)
5265 return list;
5266 half = len >> 1;
5267 p = list;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005268 for (n = half; --n >= 0;) {
Eric Andersencb57d552001-06-28 07:25:16 +00005269 q = p;
5270 p = p->next;
5271 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005272 q->next = NULL; /* terminate first half of list */
5273 q = msort(list, half); /* sort first half of list */
5274 p = msort(p, len - half); /* sort second half */
Eric Andersencb57d552001-06-28 07:25:16 +00005275 lpp = &list;
5276 for (;;) {
5277 if (strcmp(p->text, q->text) < 0) {
5278 *lpp = p;
5279 lpp = &p->next;
5280 if ((p = *lpp) == NULL) {
5281 *lpp = q;
5282 break;
5283 }
5284 } else {
5285 *lpp = q;
5286 lpp = &q->next;
5287 if ((q = *lpp) == NULL) {
5288 *lpp = p;
5289 break;
5290 }
5291 }
5292 }
5293 return list;
5294}
5295#endif
5296
5297
5298
5299/*
5300 * Returns true if the pattern matches the string.
5301 */
5302
Eric Andersen62483552001-07-10 06:09:16 +00005303#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Eric Andersen2870d962001-07-02 17:27:21 +00005304/* squoted: string might have quote chars */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005305static int patmatch(char *pattern, char *string, int squoted)
Eric Andersen2870d962001-07-02 17:27:21 +00005306{
Eric Andersencb57d552001-06-28 07:25:16 +00005307 const char *p;
5308 char *q;
5309
5310 p = preglob(pattern);
5311 q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string;
5312
5313 return !fnmatch(p, q, 0);
5314}
5315
5316
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005317static int patmatch2(char *pattern, char *string, int squoted)
Eric Andersen2870d962001-07-02 17:27:21 +00005318{
Eric Andersencb57d552001-06-28 07:25:16 +00005319 char *p;
5320 int res;
5321
5322 sstrnleft--;
5323 p = grabstackstr(expdest);
5324 res = patmatch(pattern, string, squoted);
5325 ungrabstackstr(p, expdest);
5326 return res;
5327}
5328#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005329static int patmatch(char *pattern, char *string, int squoted)
5330{
Eric Andersen2870d962001-07-02 17:27:21 +00005331 return pmatch(pattern, string, squoted);
Eric Andersencb57d552001-06-28 07:25:16 +00005332}
5333
5334
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005335static int pmatch(char *pattern, char *string, int squoted)
Eric Andersen2870d962001-07-02 17:27:21 +00005336{
Eric Andersencb57d552001-06-28 07:25:16 +00005337 char *p, *q;
5338 char c;
5339
5340 p = pattern;
5341 q = string;
5342 for (;;) {
5343 switch (c = *p++) {
5344 case '\0':
5345 goto breakloop;
5346 case CTLESC:
5347 if (squoted && *q == CTLESC)
5348 q++;
5349 if (*q++ != *p++)
5350 return 0;
5351 break;
5352 case CTLQUOTEMARK:
5353 continue;
5354 case '?':
5355 if (squoted && *q == CTLESC)
5356 q++;
5357 if (*q++ == '\0')
5358 return 0;
5359 break;
5360 case '*':
5361 c = *p;
5362 while (c == CTLQUOTEMARK || c == '*')
5363 c = *++p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005364 if (c != CTLESC && c != CTLQUOTEMARK &&
5365 c != '?' && c != '*' && c != '[') {
Eric Andersencb57d552001-06-28 07:25:16 +00005366 while (*q != c) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005367 if (squoted && *q == CTLESC && q[1] == c)
Eric Andersencb57d552001-06-28 07:25:16 +00005368 break;
5369 if (*q == '\0')
5370 return 0;
5371 if (squoted && *q == CTLESC)
5372 q++;
5373 q++;
5374 }
5375 }
5376 do {
5377 if (pmatch(p, q, squoted))
5378 return 1;
5379 if (squoted && *q == CTLESC)
5380 q++;
5381 } while (*q++ != '\0');
5382 return 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005383 case '[':{
Eric Andersencb57d552001-06-28 07:25:16 +00005384 char *endp;
5385 int invert, found;
5386 char chr;
5387
5388 endp = p;
5389 if (*endp == '!')
5390 endp++;
5391 for (;;) {
5392 while (*endp == CTLQUOTEMARK)
5393 endp++;
5394 if (*endp == '\0')
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005395 goto dft; /* no matching ] */
Eric Andersencb57d552001-06-28 07:25:16 +00005396 if (*endp == CTLESC)
5397 endp++;
5398 if (*++endp == ']')
5399 break;
5400 }
5401 invert = 0;
5402 if (*p == '!') {
5403 invert++;
5404 p++;
5405 }
5406 found = 0;
5407 chr = *q++;
5408 if (squoted && chr == CTLESC)
5409 chr = *q++;
5410 if (chr == '\0')
5411 return 0;
5412 c = *p++;
5413 do {
5414 if (c == CTLQUOTEMARK)
5415 continue;
5416 if (c == CTLESC)
5417 c = *p++;
5418 if (*p == '-' && p[1] != ']') {
5419 p++;
5420 while (*p == CTLQUOTEMARK)
5421 p++;
5422 if (*p == CTLESC)
5423 p++;
5424 if (chr >= c && chr <= *p)
5425 found = 1;
5426 p++;
5427 } else {
5428 if (chr == c)
5429 found = 1;
5430 }
5431 } while ((c = *p++) != ']');
5432 if (found == invert)
5433 return 0;
5434 break;
5435 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005436 dft: default:
Eric Andersencb57d552001-06-28 07:25:16 +00005437 if (squoted && *q == CTLESC)
5438 q++;
5439 if (*q++ != c)
5440 return 0;
5441 break;
5442 }
5443 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005444 breakloop:
Eric Andersencb57d552001-06-28 07:25:16 +00005445 if (*q != '\0')
5446 return 0;
5447 return 1;
5448}
5449#endif
5450
5451
5452
5453/*
5454 * Remove any CTLESC characters from a string.
5455 */
5456
Eric Andersen62483552001-07-10 06:09:16 +00005457#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005458static char *_rmescapes(char *str, int flag)
Eric Andersencb57d552001-06-28 07:25:16 +00005459{
5460 char *p, *q, *r;
5461 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5462
5463 p = strpbrk(str, qchars);
5464 if (!p) {
5465 return str;
5466 }
5467 q = p;
5468 r = str;
5469 if (flag & RMESCAPE_ALLOC) {
5470 size_t len = p - str;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005471
Eric Andersencb57d552001-06-28 07:25:16 +00005472 q = r = stalloc(strlen(p) + len + 1);
5473 if (len > 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00005474 memcpy(q, str, len);
5475 q += len;
Eric Andersencb57d552001-06-28 07:25:16 +00005476 }
5477 }
5478 while (*p) {
5479 if (*p == CTLQUOTEMARK) {
5480 p++;
5481 continue;
5482 }
5483 if (*p == CTLESC) {
5484 p++;
5485 if (flag & RMESCAPE_GLOB && *p != '/') {
5486 *q++ = '\\';
5487 }
5488 }
5489 *q++ = *p++;
5490 }
5491 *q = '\0';
5492 return r;
5493}
5494#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005495static void rmescapes(char *str)
Eric Andersencb57d552001-06-28 07:25:16 +00005496{
5497 char *p, *q;
5498
5499 p = str;
5500 while (*p != CTLESC && *p != CTLQUOTEMARK) {
5501 if (*p++ == '\0')
5502 return;
5503 }
5504 q = p;
5505 while (*p) {
5506 if (*p == CTLQUOTEMARK) {
5507 p++;
5508 continue;
5509 }
5510 if (*p == CTLESC)
5511 p++;
5512 *q++ = *p++;
5513 }
5514 *q = '\0';
5515}
5516#endif
5517
5518
5519
5520/*
5521 * See if a pattern matches in a case statement.
5522 */
5523
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005524static int casematch(union node *pattern, const char *val)
Eric Andersen2870d962001-07-02 17:27:21 +00005525{
Eric Andersencb57d552001-06-28 07:25:16 +00005526 struct stackmark smark;
5527 int result;
5528 char *p;
5529
5530 setstackmark(&smark);
5531 argbackq = pattern->narg.backquote;
5532 STARTSTACKSTR(expdest);
5533 ifslastp = NULL;
5534 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5535 STPUTC('\0', expdest);
5536 p = grabstackstr(expdest);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005537 result = patmatch(p, (char *) val, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00005538 popstackmark(&smark);
5539 return result;
5540}
5541
5542/*
5543 * Our own itoa().
5544 */
5545
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005546static char *cvtnum(int num, char *buf)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005547{
Eric Andersencb57d552001-06-28 07:25:16 +00005548 int len;
5549
5550 CHECKSTRSPACE(32, buf);
5551 len = sprintf(buf, "%d", num);
5552 STADJUST(len, buf);
5553 return buf;
5554}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005555
Eric Andersencb57d552001-06-28 07:25:16 +00005556/*
5557 * Editline and history functions (and glue).
5558 */
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005559static int histcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00005560{
5561 error("not compiled with history support");
5562 /* NOTREACHED */
5563}
5564
5565
Eric Andersencb57d552001-06-28 07:25:16 +00005566struct redirtab {
5567 struct redirtab *next;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005568 short renamed[10]; /* Current ash support only 0-9 descriptors */
Eric Andersen34506362001-08-02 05:02:46 +00005569 /* char on arm (and others) can't be negative */
Eric Andersencb57d552001-06-28 07:25:16 +00005570};
5571
Eric Andersen2870d962001-07-02 17:27:21 +00005572static struct redirtab *redirlist;
Eric Andersencb57d552001-06-28 07:25:16 +00005573
5574extern char **environ;
5575
5576
5577
5578/*
5579 * Initialization code.
5580 */
5581
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005582static void init(void)
5583{
Eric Andersencb57d552001-06-28 07:25:16 +00005584
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005585 /* from cd.c: */
5586 {
5587 curdir = nullstr;
5588 setpwd(0, 0);
5589 }
Eric Andersencb57d552001-06-28 07:25:16 +00005590
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005591 /* from input.c: */
5592 {
5593 basepf.nextc = basepf.buf = basebuf;
5594 }
Eric Andersencb57d552001-06-28 07:25:16 +00005595
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005596 /* from var.c: */
5597 {
5598 char **envp;
5599 char ppid[32];
Eric Andersencb57d552001-06-28 07:25:16 +00005600
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005601 initvar();
5602 for (envp = environ; *envp; envp++) {
5603 if (strchr(*envp, '=')) {
5604 setvareq(*envp, VEXPORT | VTEXTFIXED);
5605 }
5606 }
Eric Andersencb57d552001-06-28 07:25:16 +00005607
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005608 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
5609 setvar("PPID", ppid, 0);
5610 }
Eric Andersencb57d552001-06-28 07:25:16 +00005611}
5612
5613
5614
5615/*
5616 * This routine is called when an error or an interrupt occurs in an
5617 * interactive shell and control is returned to the main command loop.
5618 */
5619
Eric Andersen2870d962001-07-02 17:27:21 +00005620/* 1 == check for aliases, 2 == also check for assignments */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005621static int checkalias; /* also used in no alias mode for check assignments */
Eric Andersen2870d962001-07-02 17:27:21 +00005622
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005623static void reset(void)
5624{
Eric Andersencb57d552001-06-28 07:25:16 +00005625
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005626 /* from eval.c: */
5627 {
5628 evalskip = 0;
5629 loopnest = 0;
5630 funcnest = 0;
5631 }
Eric Andersencb57d552001-06-28 07:25:16 +00005632
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005633 /* from input.c: */
5634 {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00005635 parselleft = parsenleft = 0; /* clear input buffer */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005636 popallfiles();
5637 }
Eric Andersencb57d552001-06-28 07:25:16 +00005638
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005639 /* from parser.c: */
5640 {
5641 tokpushback = 0;
5642 checkkwd = 0;
5643 checkalias = 0;
5644 }
Eric Andersencb57d552001-06-28 07:25:16 +00005645
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005646 /* from redir.c: */
5647 {
5648 while (redirlist)
5649 popredir();
5650 }
Eric Andersencb57d552001-06-28 07:25:16 +00005651
Eric Andersencb57d552001-06-28 07:25:16 +00005652}
5653
5654
5655
5656/*
Eric Andersencb57d552001-06-28 07:25:16 +00005657 * This file implements the input routines used by the parser.
5658 */
5659
Eric Andersenbdfd0d72001-10-24 05:00:29 +00005660#ifdef CONFIG_FEATURE_COMMAND_EDITING
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005661static const char *cmdedit_prompt;
5662static inline void putprompt(const char *s)
5663{
5664 cmdedit_prompt = s;
Eric Andersencb57d552001-06-28 07:25:16 +00005665}
5666#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005667static inline void putprompt(const char *s)
5668{
5669 out2str(s);
Eric Andersencb57d552001-06-28 07:25:16 +00005670}
5671#endif
5672
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005673#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00005674
Eric Andersencb57d552001-06-28 07:25:16 +00005675
Eric Andersencb57d552001-06-28 07:25:16 +00005676
Eric Andersen2870d962001-07-02 17:27:21 +00005677/*
5678 * Same as pgetc(), but ignores PEOA.
5679 */
Eric Andersencb57d552001-06-28 07:25:16 +00005680
Eric Andersend35c5df2002-01-09 15:37:36 +00005681#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005682static int pgetc2(void)
Eric Andersen2870d962001-07-02 17:27:21 +00005683{
5684 int c;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005685
Eric Andersen2870d962001-07-02 17:27:21 +00005686 do {
5687 c = pgetc_macro();
5688 } while (c == PEOA);
5689 return c;
Eric Andersencb57d552001-06-28 07:25:16 +00005690}
Eric Andersen2870d962001-07-02 17:27:21 +00005691#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005692static inline int pgetc2(void)
5693{
5694 return pgetc_macro();
5695}
Eric Andersencb57d552001-06-28 07:25:16 +00005696#endif
5697
Eric Andersencb57d552001-06-28 07:25:16 +00005698/*
5699 * Read a line from the script.
5700 */
5701
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005702static inline char *pfgets(char *line, int len)
Eric Andersencb57d552001-06-28 07:25:16 +00005703{
5704 char *p = line;
5705 int nleft = len;
5706 int c;
5707
5708 while (--nleft > 0) {
5709 c = pgetc2();
5710 if (c == PEOF) {
5711 if (p == line)
5712 return NULL;
5713 break;
5714 }
5715 *p++ = c;
5716 if (c == '\n')
5717 break;
5718 }
5719 *p = '\0';
5720 return line;
5721}
5722
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005723static inline int preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00005724{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005725 int nr;
5726 char *buf = parsefile->buf;
Eric Andersencb57d552001-06-28 07:25:16 +00005727
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005728 parsenextc = buf;
5729
5730 retry:
Eric Andersenbdfd0d72001-10-24 05:00:29 +00005731#ifdef CONFIG_FEATURE_COMMAND_EDITING
Eric Andersencb57d552001-06-28 07:25:16 +00005732 {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005733 if (!iflag || parsefile->fd)
5734 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
5735 else {
5736 nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
5737 }
Eric Andersencb57d552001-06-28 07:25:16 +00005738 }
5739#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00005740 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00005741#endif
5742
5743 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00005744 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
5745 int flags = fcntl(0, F_GETFL, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005746
Eric Andersencb57d552001-06-28 07:25:16 +00005747 if (flags >= 0 && flags & O_NONBLOCK) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005748 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00005749 if (fcntl(0, F_SETFL, flags) >= 0) {
5750 out2str("sh: turning off NDELAY mode\n");
5751 goto retry;
5752 }
5753 }
5754 }
5755 }
5756 return nr;
5757}
5758
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005759static void popstring(void)
Eric Andersen2870d962001-07-02 17:27:21 +00005760{
5761 struct strpush *sp = parsefile->strpush;
5762
5763 INTOFF;
Eric Andersend35c5df2002-01-09 15:37:36 +00005764#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00005765 if (sp->ap) {
5766 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
5767 if (!checkalias) {
5768 checkalias = 1;
5769 }
5770 }
5771 if (sp->string != sp->ap->val) {
Aaron Lehmann49c024a2002-08-02 06:39:47 +00005772 free(sp->string);
Eric Andersen2870d962001-07-02 17:27:21 +00005773 }
5774
5775 sp->ap->flag &= ~ALIASINUSE;
5776 if (sp->ap->flag & ALIASDEAD) {
5777 unalias(sp->ap->name);
5778 }
5779 }
5780#endif
5781 parsenextc = sp->prevstring;
5782 parsenleft = sp->prevnleft;
5783/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
5784 parsefile->strpush = sp->prev;
5785 if (sp != &(parsefile->basestrpush))
Aaron Lehmann49c024a2002-08-02 06:39:47 +00005786 free(sp);
Eric Andersen2870d962001-07-02 17:27:21 +00005787 INTON;
5788}
5789
5790
Eric Andersencb57d552001-06-28 07:25:16 +00005791/*
5792 * Refill the input buffer and return the next input character:
5793 *
5794 * 1) If a string was pushed back on the input, pop it;
5795 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
5796 * from a string so we can't refill the buffer, return EOF.
5797 * 3) If the is more stuff in this buffer, use it else call read to fill it.
5798 * 4) Process input up to the next newline, deleting nul characters.
5799 */
5800
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005801static int preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00005802{
5803 char *p, *q;
5804 int more;
5805 char savec;
5806
5807 while (parsefile->strpush) {
Eric Andersend35c5df2002-01-09 15:37:36 +00005808#ifdef CONFIG_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00005809 if (parsenleft == -1 && parsefile->strpush->ap &&
5810 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00005811 return PEOA;
5812 }
Eric Andersen2870d962001-07-02 17:27:21 +00005813#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005814 popstring();
5815 if (--parsenleft >= 0)
5816 return (*parsenextc++);
5817 }
5818 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
5819 return PEOF;
Eric Andersen3102ac42001-07-06 04:26:23 +00005820 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00005821
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005822 again:
Eric Andersencb57d552001-06-28 07:25:16 +00005823 if (parselleft <= 0) {
5824 if ((parselleft = preadfd()) <= 0) {
5825 parselleft = parsenleft = EOF_NLEFT;
5826 return PEOF;
5827 }
5828 }
5829
5830 q = p = parsenextc;
5831
5832 /* delete nul characters */
5833 for (more = 1; more;) {
5834 switch (*p) {
5835 case '\0':
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005836 p++; /* Skip nul */
Eric Andersencb57d552001-06-28 07:25:16 +00005837 goto check;
5838
5839
5840 case '\n':
5841 parsenleft = q - parsenextc;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005842 more = 0; /* Stop processing here */
Eric Andersencb57d552001-06-28 07:25:16 +00005843 break;
5844 }
5845
5846 *q++ = *p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005847 check:
Eric Andersencb57d552001-06-28 07:25:16 +00005848 if (--parselleft <= 0 && more) {
5849 parsenleft = q - parsenextc - 1;
5850 if (parsenleft < 0)
5851 goto again;
5852 more = 0;
5853 }
5854 }
5855
5856 savec = *q;
5857 *q = '\0';
5858
5859 if (vflag) {
5860 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00005861 }
5862
5863 *q = savec;
5864
5865 return *parsenextc++;
5866}
5867
Eric Andersencb57d552001-06-28 07:25:16 +00005868
5869/*
5870 * Push a string back onto the input at this current parsefile level.
5871 * We handle aliases this way.
5872 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005873static void pushstring(char *s, int len, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00005874{
Eric Andersencb57d552001-06-28 07:25:16 +00005875 struct strpush *sp;
5876
5877 INTOFF;
5878/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
5879 if (parsefile->strpush) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005880 sp = xmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00005881 sp->prev = parsefile->strpush;
5882 parsefile->strpush = sp;
5883 } else
5884 sp = parsefile->strpush = &(parsefile->basestrpush);
5885 sp->prevstring = parsenextc;
5886 sp->prevnleft = parsenleft;
Eric Andersend35c5df2002-01-09 15:37:36 +00005887#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005888 sp->ap = (struct alias *) ap;
Eric Andersencb57d552001-06-28 07:25:16 +00005889 if (ap) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005890 ((struct alias *) ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00005891 sp->string = s;
5892 }
Eric Andersen2870d962001-07-02 17:27:21 +00005893#endif
Eric Andersencb57d552001-06-28 07:25:16 +00005894 parsenextc = s;
5895 parsenleft = len;
5896 INTON;
5897}
5898
Eric Andersencb57d552001-06-28 07:25:16 +00005899
Eric Andersencb57d552001-06-28 07:25:16 +00005900/*
5901 * Like setinputfile, but takes input from a string.
5902 */
5903
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005904static void setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00005905{
Eric Andersencb57d552001-06-28 07:25:16 +00005906 INTOFF;
5907 pushfile();
5908 parsenextc = string;
5909 parsenleft = strlen(string);
5910 parsefile->buf = NULL;
5911 plinno = 1;
5912 INTON;
5913}
5914
5915
5916
5917/*
5918 * To handle the "." command, a stack of input files is used. Pushfile
5919 * adds a new entry to the stack and popfile restores the previous level.
5920 */
5921
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005922static void pushfile(void)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00005923{
Eric Andersencb57d552001-06-28 07:25:16 +00005924 struct parsefile *pf;
5925
5926 parsefile->nleft = parsenleft;
5927 parsefile->lleft = parselleft;
5928 parsefile->nextc = parsenextc;
5929 parsefile->linno = plinno;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005930 pf = (struct parsefile *) xmalloc(sizeof(struct parsefile));
Eric Andersencb57d552001-06-28 07:25:16 +00005931 pf->prev = parsefile;
5932 pf->fd = -1;
5933 pf->strpush = NULL;
5934 pf->basestrpush.prev = NULL;
5935 parsefile = pf;
5936}
5937
Eric Andersend35c5df2002-01-09 15:37:36 +00005938#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005939static void restartjob(struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00005940#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005941static void freejob(struct job *);
5942static struct job *getjob(const char *);
5943static int dowait(int, struct job *);
Eric Andersencb57d552001-06-28 07:25:16 +00005944
5945
Eric Andersen2870d962001-07-02 17:27:21 +00005946/*
5947 * We keep track of whether or not fd0 has been redirected. This is for
5948 * background commands, where we want to redirect fd0 to /dev/null only
5949 * if it hasn't already been redirected.
5950*/
5951static int fd0_redirected = 0;
5952
5953/* Return true if fd 0 has already been redirected at least once. */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005954static inline int fd0_redirected_p(void)
Eric Andersen74400cc2001-10-18 04:11:39 +00005955{
Eric Andersen2870d962001-07-02 17:27:21 +00005956 return fd0_redirected != 0;
5957}
5958
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005959static void dupredirect(const union node *, int, int fd1dup);
Eric Andersen2870d962001-07-02 17:27:21 +00005960
Eric Andersend35c5df2002-01-09 15:37:36 +00005961#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00005962/*
5963 * Turn job control on and off.
5964 *
5965 * Note: This code assumes that the third arg to ioctl is a character
5966 * pointer, which is true on Berkeley systems but not System V. Since
5967 * System V doesn't have job control yet, this isn't a problem now.
5968 */
5969
Eric Andersen2870d962001-07-02 17:27:21 +00005970
Eric Andersencb57d552001-06-28 07:25:16 +00005971
5972static void setjobctl(int enable)
5973{
Eric Andersencb57d552001-06-28 07:25:16 +00005974 if (enable == jobctl || rootshell == 0)
5975 return;
5976 if (enable) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005977 do { /* while we are in the background */
Eric Andersen3102ac42001-07-06 04:26:23 +00005978 initialpgrp = tcgetpgrp(2);
Eric Andersencb57d552001-06-28 07:25:16 +00005979 if (initialpgrp < 0) {
Eric Andersen8c145dc2001-07-10 16:57:09 +00005980 out2str("sh: can't access tty; job control turned off\n");
Eric Andersencb57d552001-06-28 07:25:16 +00005981 mflag = 0;
5982 return;
5983 }
5984 if (initialpgrp == -1)
5985 initialpgrp = getpgrp();
5986 else if (initialpgrp != getpgrp()) {
5987 killpg(initialpgrp, SIGTTIN);
5988 continue;
5989 }
5990 } while (0);
Eric Andersencb57d552001-06-28 07:25:16 +00005991 setsignal(SIGTSTP);
5992 setsignal(SIGTTOU);
5993 setsignal(SIGTTIN);
5994 setpgid(0, rootpid);
Eric Andersen3102ac42001-07-06 04:26:23 +00005995 tcsetpgrp(2, rootpid);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00005996 } else { /* turning job control off */
Eric Andersencb57d552001-06-28 07:25:16 +00005997 setpgid(0, initialpgrp);
Eric Andersen3102ac42001-07-06 04:26:23 +00005998 tcsetpgrp(2, initialpgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00005999 setsignal(SIGTSTP);
6000 setsignal(SIGTTOU);
6001 setsignal(SIGTTIN);
6002 }
6003 jobctl = enable;
6004}
6005#endif
6006
6007
Eric Andersend35c5df2002-01-09 15:37:36 +00006008#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006009static int killcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006010{
6011 int signo = -1;
6012 int list = 0;
6013 int i;
6014 pid_t pid;
6015 struct job *jp;
6016
6017 if (argc <= 1) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006018 usage:
6019 error
6020 ("Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6021 "kill -l [exitstatus]");
Eric Andersencb57d552001-06-28 07:25:16 +00006022 }
6023
6024 if (*argv[1] == '-') {
6025 signo = decode_signal(argv[1] + 1, 1);
6026 if (signo < 0) {
6027 int c;
6028
6029 while ((c = nextopt("ls:")) != '\0')
6030 switch (c) {
6031 case 'l':
6032 list = 1;
6033 break;
6034 case 's':
6035 signo = decode_signal(optionarg, 1);
6036 if (signo < 0) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006037 error("invalid signal number or name: %s", optionarg);
Eric Andersencb57d552001-06-28 07:25:16 +00006038 }
Eric Andersen2870d962001-07-02 17:27:21 +00006039 break;
Eric Andersencb57d552001-06-28 07:25:16 +00006040#ifdef DEBUG
6041 default:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006042 error("nextopt returned character code 0%o", c);
Eric Andersencb57d552001-06-28 07:25:16 +00006043#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006044 }
Eric Andersencb57d552001-06-28 07:25:16 +00006045 } else
6046 argptr++;
6047 }
6048
6049 if (!list && signo < 0)
6050 signo = SIGTERM;
6051
6052 if ((signo < 0 || !*argptr) ^ list) {
6053 goto usage;
6054 }
6055
6056 if (list) {
Eric Andersen34506362001-08-02 05:02:46 +00006057 const char *name;
6058
Eric Andersencb57d552001-06-28 07:25:16 +00006059 if (!*argptr) {
6060 out1str("0\n");
6061 for (i = 1; i < NSIG; i++) {
Eric Andersen34506362001-08-02 05:02:46 +00006062 name = u_signal_names(0, &i, 1);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006063 if (name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006064 puts(name);
Eric Andersencb57d552001-06-28 07:25:16 +00006065 }
6066 return 0;
6067 }
Eric Andersen34506362001-08-02 05:02:46 +00006068 name = u_signal_names(*argptr, &signo, -1);
6069 if (name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006070 puts(name);
Eric Andersencb57d552001-06-28 07:25:16 +00006071 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006072 error("invalid signal number or exit status: %s", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00006073 return 0;
6074 }
6075
6076 do {
6077 if (**argptr == '%') {
6078 jp = getjob(*argptr);
6079 if (jp->jobctl == 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006080 error("job %s not created under job control", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00006081 pid = -jp->ps[0].pid;
6082 } else
6083 pid = atoi(*argptr);
6084 if (kill(pid, signo) != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00006085 error("%s: %m", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00006086 } while (*++argptr);
6087
6088 return 0;
6089}
6090
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006091static int fgcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006092{
6093 struct job *jp;
6094 int pgrp;
6095 int status;
6096
6097 jp = getjob(argv[1]);
6098 if (jp->jobctl == 0)
6099 error("job not created under job control");
6100 pgrp = jp->ps[0].pid;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006101 ioctl(2, TIOCSPGRP, (char *) &pgrp);
Eric Andersencb57d552001-06-28 07:25:16 +00006102 restartjob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00006103 status = waitforjob(jp);
Eric Andersencb57d552001-06-28 07:25:16 +00006104 return status;
6105}
6106
6107
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006108static int bgcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006109{
6110 struct job *jp;
6111
6112 do {
6113 jp = getjob(*++argv);
6114 if (jp->jobctl == 0)
6115 error("job not created under job control");
6116 restartjob(jp);
6117 } while (--argc > 1);
6118 return 0;
6119}
6120
6121
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006122static void restartjob(struct job *jp)
Eric Andersencb57d552001-06-28 07:25:16 +00006123{
6124 struct procstat *ps;
6125 int i;
6126
6127 if (jp->state == JOBDONE)
6128 return;
6129 INTOFF;
6130 killpg(jp->ps[0].pid, SIGCONT);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006131 for (ps = jp->ps, i = jp->nprocs; --i >= 0; ps++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006132 if (WIFSTOPPED(ps->status)) {
6133 ps->status = -1;
6134 jp->state = 0;
6135 }
6136 }
6137 INTON;
6138}
6139#endif
6140
Eric Andersen2870d962001-07-02 17:27:21 +00006141static void showjobs(int change);
6142
Eric Andersencb57d552001-06-28 07:25:16 +00006143
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006144static int jobscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006145{
6146 showjobs(0);
6147 return 0;
6148}
6149
6150
6151/*
6152 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6153 * statuses have changed since the last call to showjobs.
6154 *
6155 * If the shell is interrupted in the process of creating a job, the
6156 * result may be a job structure containing zero processes. Such structures
6157 * will be freed here.
6158 */
6159
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006160static void showjobs(int change)
Eric Andersencb57d552001-06-28 07:25:16 +00006161{
6162 int jobno;
6163 int procno;
6164 int i;
6165 struct job *jp;
6166 struct procstat *ps;
6167 int col;
6168 char s[64];
6169
6170 TRACE(("showjobs(%d) called\n", change));
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006171 while (dowait(0, (struct job *) NULL) > 0);
6172 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
6173 if (!jp->used)
Eric Andersencb57d552001-06-28 07:25:16 +00006174 continue;
6175 if (jp->nprocs == 0) {
6176 freejob(jp);
6177 continue;
6178 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006179 if (change && !jp->changed)
Eric Andersencb57d552001-06-28 07:25:16 +00006180 continue;
6181 procno = jp->nprocs;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006182 for (ps = jp->ps;; ps++) { /* for each process */
Eric Andersencb57d552001-06-28 07:25:16 +00006183 if (ps == jp->ps)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006184 snprintf(s, 64, "[%d] %ld ", jobno, (long) ps->pid);
Eric Andersencb57d552001-06-28 07:25:16 +00006185 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006186 snprintf(s, 64, " %ld ", (long) ps->pid);
Eric Andersencb57d552001-06-28 07:25:16 +00006187 out1str(s);
6188 col = strlen(s);
6189 s[0] = '\0';
6190 if (ps->status == -1) {
6191 /* don't print anything */
6192 } else if (WIFEXITED(ps->status)) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006193 snprintf(s, 64, "Exit %d", WEXITSTATUS(ps->status));
Eric Andersencb57d552001-06-28 07:25:16 +00006194 } else {
Eric Andersend35c5df2002-01-09 15:37:36 +00006195#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen2870d962001-07-02 17:27:21 +00006196 if (WIFSTOPPED(ps->status))
Eric Andersencb57d552001-06-28 07:25:16 +00006197 i = WSTOPSIG(ps->status);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006198 else /* WIFSIGNALED(ps->status) */
Eric Andersencb57d552001-06-28 07:25:16 +00006199#endif
6200 i = WTERMSIG(ps->status);
6201 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
Eric Andersen2870d962001-07-02 17:27:21 +00006202 strcpy(s, sys_siglist[i & 0x7F]);
Eric Andersencb57d552001-06-28 07:25:16 +00006203 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006204 snprintf(s, 64, "Signal %d", i & 0x7F);
Eric Andersencb57d552001-06-28 07:25:16 +00006205 if (WCOREDUMP(ps->status))
6206 strcat(s, " (core dumped)");
6207 }
6208 out1str(s);
6209 col += strlen(s);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006210 printf("%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ', ps->cmd);
Eric Andersencb57d552001-06-28 07:25:16 +00006211 if (--procno <= 0)
6212 break;
6213 }
6214 jp->changed = 0;
6215 if (jp->state == JOBDONE) {
6216 freejob(jp);
6217 }
6218 }
6219}
6220
6221
6222/*
6223 * Mark a job structure as unused.
6224 */
6225
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006226static void freejob(struct job *jp)
Eric Andersen62483552001-07-10 06:09:16 +00006227{
6228 const struct procstat *ps;
Eric Andersencb57d552001-06-28 07:25:16 +00006229 int i;
6230
6231 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006232 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006233 if (ps->cmd != nullstr)
Aaron Lehmann49c024a2002-08-02 06:39:47 +00006234 free(ps->cmd);
Eric Andersencb57d552001-06-28 07:25:16 +00006235 }
6236 if (jp->ps != &jp->ps0)
Aaron Lehmann49c024a2002-08-02 06:39:47 +00006237 free(jp->ps);
Eric Andersencb57d552001-06-28 07:25:16 +00006238 jp->used = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006239#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006240 if (curjob == jp - jobtab + 1)
6241 curjob = 0;
6242#endif
6243 INTON;
6244}
6245
6246
6247
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006248static int waitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00006249{
6250 struct job *job;
6251 int status, retval;
6252 struct job *jp;
6253
6254 if (--argc > 0) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006255 start:
Eric Andersencb57d552001-06-28 07:25:16 +00006256 job = getjob(*++argv);
6257 } else {
6258 job = NULL;
6259 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006260 for (;;) { /* loop until process terminated or stopped */
Eric Andersencb57d552001-06-28 07:25:16 +00006261 if (job != NULL) {
6262 if (job->state) {
6263 status = job->ps[job->nprocs - 1].status;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006264 if (!iflag)
Eric Andersencb57d552001-06-28 07:25:16 +00006265 freejob(job);
6266 if (--argc) {
6267 goto start;
6268 }
6269 if (WIFEXITED(status))
6270 retval = WEXITSTATUS(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006271#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006272 else if (WIFSTOPPED(status))
6273 retval = WSTOPSIG(status) + 128;
6274#endif
6275 else {
6276 /* XXX: limits number of signals */
6277 retval = WTERMSIG(status) + 128;
6278 }
6279 return retval;
6280 }
6281 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006282 for (jp = jobtab;; jp++) {
6283 if (jp >= jobtab + njobs) { /* no running procs */
Eric Andersencb57d552001-06-28 07:25:16 +00006284 return 0;
6285 }
6286 if (jp->used && jp->state == 0)
6287 break;
6288 }
6289 }
6290 if (dowait(2, 0) < 0 && errno == EINTR) {
6291 return 129;
6292 }
6293 }
6294}
6295
6296
6297
6298/*
6299 * Convert a job name to a job structure.
6300 */
6301
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006302static struct job *getjob(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00006303{
Eric Andersencb57d552001-06-28 07:25:16 +00006304 int jobno;
6305 struct job *jp;
6306 int pid;
6307 int i;
6308
6309 if (name == NULL) {
Eric Andersend35c5df2002-01-09 15:37:36 +00006310#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006311 currentjob:
Eric Andersencb57d552001-06-28 07:25:16 +00006312 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
6313 error("No current job");
6314 return &jobtab[jobno - 1];
6315#else
6316 error("No current job");
6317#endif
6318 } else if (name[0] == '%') {
6319 if (is_digit(name[1])) {
6320 jobno = number(name + 1);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006321 if (jobno > 0 && jobno <= njobs && jobtab[jobno - 1].used != 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006322 return &jobtab[jobno - 1];
Eric Andersend35c5df2002-01-09 15:37:36 +00006323#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006324 } else if (name[1] == '%' && name[2] == '\0') {
6325 goto currentjob;
6326#endif
6327 } else {
6328 struct job *found = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006329
6330 for (jp = jobtab, i = njobs; --i >= 0; jp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006331 if (jp->used && jp->nprocs > 0
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006332 && prefix(name + 1, jp->ps[0].cmd)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006333 if (found)
6334 error("%s: ambiguous", name);
6335 found = jp;
6336 }
6337 }
6338 if (found)
6339 return found;
6340 }
Eric Andersen2870d962001-07-02 17:27:21 +00006341 } else if (is_number(name, &pid)) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006342 for (jp = jobtab, i = njobs; --i >= 0; jp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006343 if (jp->used && jp->nprocs > 0
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006344 && jp->ps[jp->nprocs - 1].pid == pid)
Eric Andersencb57d552001-06-28 07:25:16 +00006345 return jp;
6346 }
6347 }
6348 error("No such job: %s", name);
6349 /* NOTREACHED */
6350}
6351
6352
6353
6354/*
6355 * Return a new job structure,
6356 */
6357
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006358static struct job *makejob(const union node *node, int nprocs)
Eric Andersencb57d552001-06-28 07:25:16 +00006359{
6360 int i;
6361 struct job *jp;
6362
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006363 for (i = njobs, jp = jobtab;; jp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006364 if (--i < 0) {
6365 INTOFF;
6366 if (njobs == 0) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006367 jobtab = xmalloc(4 * sizeof jobtab[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00006368 } else {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00006369 jp = xmalloc((njobs + 4) * sizeof jobtab[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00006370 memcpy(jp, jobtab, njobs * sizeof jp[0]);
6371 /* Relocate `ps' pointers */
6372 for (i = 0; i < njobs; i++)
6373 if (jp[i].ps == &jobtab[i].ps0)
6374 jp[i].ps = &jp[i].ps0;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00006375 free(jobtab);
Eric Andersencb57d552001-06-28 07:25:16 +00006376 jobtab = jp;
6377 }
6378 jp = jobtab + njobs;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006379 for (i = 4; --i >= 0; jobtab[njobs++].used = 0);
Eric Andersencb57d552001-06-28 07:25:16 +00006380 INTON;
6381 break;
6382 }
6383 if (jp->used == 0)
6384 break;
6385 }
6386 INTOFF;
6387 jp->state = 0;
6388 jp->used = 1;
6389 jp->changed = 0;
6390 jp->nprocs = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006391#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006392 jp->jobctl = jobctl;
6393#endif
6394 if (nprocs > 1) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006395 jp->ps = xmalloc(nprocs * sizeof(struct procstat));
Eric Andersencb57d552001-06-28 07:25:16 +00006396 } else {
6397 jp->ps = &jp->ps0;
6398 }
6399 INTON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006400 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long) node, nprocs,
6401 jp - jobtab + 1));
Eric Andersencb57d552001-06-28 07:25:16 +00006402 return jp;
6403}
6404
6405
6406/*
6407 * Fork of a subshell. If we are doing job control, give the subshell its
6408 * own process group. Jp is a job structure that the job is to be added to.
6409 * N is the command that will be evaluated by the child. Both jp and n may
6410 * be NULL. The mode parameter can be one of the following:
Eric Andersen2870d962001-07-02 17:27:21 +00006411 * FORK_FG - Fork off a foreground process.
6412 * FORK_BG - Fork off a background process.
6413 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
6414 * process group even if job control is on.
Eric Andersencb57d552001-06-28 07:25:16 +00006415 *
6416 * When job control is turned off, background processes have their standard
6417 * input redirected to /dev/null (except for the second and later processes
6418 * in a pipeline).
6419 */
6420
Eric Andersen2870d962001-07-02 17:27:21 +00006421
6422
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006423static int forkshell(struct job *jp, const union node *n, int mode)
Eric Andersencb57d552001-06-28 07:25:16 +00006424{
6425 int pid;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006426
Eric Andersend35c5df2002-01-09 15:37:36 +00006427#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006428 int pgrp;
Eric Andersen62483552001-07-10 06:09:16 +00006429#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006430 const char *devnull = _PATH_DEVNULL;
6431 const char *nullerr = "Can't open %s";
6432
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006433 TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long) n,
6434 mode));
Eric Andersencb57d552001-06-28 07:25:16 +00006435 INTOFF;
6436 pid = fork();
6437 if (pid == -1) {
6438 TRACE(("Fork failed, errno=%d\n", errno));
6439 INTON;
6440 error("Cannot fork");
6441 }
6442 if (pid == 0) {
6443 struct job *p;
6444 int wasroot;
6445 int i;
6446
6447 TRACE(("Child shell %d\n", getpid()));
6448 wasroot = rootshell;
6449 rootshell = 0;
6450 closescript();
6451 INTON;
6452 clear_traps();
Eric Andersend35c5df2002-01-09 15:37:36 +00006453#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006454 jobctl = 0; /* do job control only in root shell */
Eric Andersencb57d552001-06-28 07:25:16 +00006455 if (wasroot && mode != FORK_NOJOB && mflag) {
6456 if (jp == NULL || jp->nprocs == 0)
6457 pgrp = getpid();
6458 else
6459 pgrp = jp->ps[0].pid;
6460 setpgid(0, pgrp);
6461 if (mode == FORK_FG) {
6462 /*** this causes superfluous TIOCSPGRPS ***/
Eric Andersen3102ac42001-07-06 04:26:23 +00006463 if (tcsetpgrp(2, pgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006464 error("tcsetpgrp failed, errno=%d", errno);
Eric Andersencb57d552001-06-28 07:25:16 +00006465 }
6466 setsignal(SIGTSTP);
6467 setsignal(SIGTTOU);
6468 } else if (mode == FORK_BG) {
Eric Andersencb57d552001-06-28 07:25:16 +00006469#else
6470 if (mode == FORK_BG) {
Aaron Lehmann1a698662001-12-31 06:12:48 +00006471#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006472 ignoresig(SIGINT);
6473 ignoresig(SIGQUIT);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006474 if ((jp == NULL || jp->nprocs == 0) && !fd0_redirected_p()) {
Eric Andersencb57d552001-06-28 07:25:16 +00006475 close(0);
6476 if (open(devnull, O_RDONLY) != 0)
6477 error(nullerr, devnull);
6478 }
6479 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006480 for (i = njobs, p = jobtab; --i >= 0; p++)
Eric Andersencb57d552001-06-28 07:25:16 +00006481 if (p->used)
6482 freejob(p);
6483 if (wasroot && iflag) {
6484 setsignal(SIGINT);
6485 setsignal(SIGQUIT);
6486 setsignal(SIGTERM);
6487 }
6488 return pid;
6489 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006490#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006491 if (rootshell && mode != FORK_NOJOB && mflag) {
6492 if (jp == NULL || jp->nprocs == 0)
6493 pgrp = pid;
6494 else
6495 pgrp = jp->ps[0].pid;
6496 setpgid(pid, pgrp);
6497 }
Eric Andersen62483552001-07-10 06:09:16 +00006498#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006499 if (mode == FORK_BG)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006500 backgndpid = pid; /* set $! */
Eric Andersencb57d552001-06-28 07:25:16 +00006501 if (jp) {
6502 struct procstat *ps = &jp->ps[jp->nprocs++];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006503
Eric Andersencb57d552001-06-28 07:25:16 +00006504 ps->pid = pid;
6505 ps->status = -1;
6506 ps->cmd = nullstr;
6507 if (iflag && rootshell && n)
6508 ps->cmd = commandtext(n);
6509 }
6510 INTON;
6511 TRACE(("In parent shell: child = %d\n", pid));
6512 return pid;
6513}
6514
6515
6516
6517/*
6518 * Wait for job to finish.
6519 *
6520 * Under job control we have the problem that while a child process is
6521 * running interrupts generated by the user are sent to the child but not
6522 * to the shell. This means that an infinite loop started by an inter-
6523 * active user may be hard to kill. With job control turned off, an
6524 * interactive user may place an interactive program inside a loop. If
6525 * the interactive program catches interrupts, the user doesn't want
6526 * these interrupts to also abort the loop. The approach we take here
6527 * is to have the shell ignore interrupt signals while waiting for a
6528 * forground process to terminate, and then send itself an interrupt
6529 * signal if the child process was terminated by an interrupt signal.
6530 * Unfortunately, some programs want to do a bit of cleanup and then
6531 * exit on interrupt; unless these processes terminate themselves by
6532 * sending a signal to themselves (instead of calling exit) they will
6533 * confuse this approach.
6534 */
6535
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006536static int waitforjob(struct job *jp)
Eric Andersen62483552001-07-10 06:09:16 +00006537{
Eric Andersend35c5df2002-01-09 15:37:36 +00006538#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006539 int mypgrp = getpgrp();
6540#endif
6541 int status;
6542 int st;
Eric Andersencb57d552001-06-28 07:25:16 +00006543
6544 INTOFF;
Eric Andersencb57d552001-06-28 07:25:16 +00006545 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
6546 while (jp->state == 0) {
6547 dowait(1, jp);
6548 }
Eric Andersend35c5df2002-01-09 15:37:36 +00006549#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006550 if (jp->jobctl) {
Eric Andersen3102ac42001-07-06 04:26:23 +00006551 if (tcsetpgrp(2, mypgrp) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00006552 error("tcsetpgrp failed, errno=%d\n", errno);
Eric Andersencb57d552001-06-28 07:25:16 +00006553 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00006554 if (jp->state == JOBSTOPPED)
Eric Andersencb57d552001-06-28 07:25:16 +00006555 curjob = jp - jobtab + 1;
6556#endif
6557 status = jp->ps[jp->nprocs - 1].status;
6558 /* convert to 8 bits */
6559 if (WIFEXITED(status))
6560 st = WEXITSTATUS(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006561#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006562 else if (WIFSTOPPED(status))
6563 st = WSTOPSIG(status) + 128;
6564#endif
6565 else
6566 st = WTERMSIG(status) + 128;
Eric Andersend35c5df2002-01-09 15:37:36 +00006567#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006568 if (jp->jobctl) {
6569 /*
6570 * This is truly gross.
6571 * If we're doing job control, then we did a TIOCSPGRP which
6572 * caused us (the shell) to no longer be in the controlling
6573 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
6574 * intuit from the subprocess exit status whether a SIGINT
6575 * occured, and if so interrupt ourselves. Yuck. - mycroft
6576 */
6577 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
6578 raise(SIGINT);
6579 }
Eric Andersen2870d962001-07-02 17:27:21 +00006580 if (jp->state == JOBDONE)
Eric Andersencb57d552001-06-28 07:25:16 +00006581#endif
Eric Andersencb57d552001-06-28 07:25:16 +00006582 freejob(jp);
6583 INTON;
6584 return st;
6585}
6586
6587
6588
6589/*
6590 * Wait for a process to terminate.
6591 */
6592
Eric Andersen62483552001-07-10 06:09:16 +00006593/*
6594 * Do a wait system call. If job control is compiled in, we accept
6595 * stopped processes. If block is zero, we return a value of zero
6596 * rather than blocking.
6597 *
6598 * System V doesn't have a non-blocking wait system call. It does
6599 * have a SIGCLD signal that is sent to a process when one of it's
6600 * children dies. The obvious way to use SIGCLD would be to install
6601 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
6602 * was received, and have waitproc bump another counter when it got
6603 * the status of a process. Waitproc would then know that a wait
6604 * system call would not block if the two counters were different.
6605 * This approach doesn't work because if a process has children that
6606 * have not been waited for, System V will send it a SIGCLD when it
6607 * installs a signal handler for SIGCLD. What this means is that when
6608 * a child exits, the shell will be sent SIGCLD signals continuously
6609 * until is runs out of stack space, unless it does a wait call before
6610 * restoring the signal handler. The code below takes advantage of
6611 * this (mis)feature by installing a signal handler for SIGCLD and
6612 * then checking to see whether it was called. If there are any
6613 * children to be waited for, it will be.
6614 *
6615 */
6616
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006617static inline int waitproc(int block, int *status)
Eric Andersen62483552001-07-10 06:09:16 +00006618{
6619 int flags;
6620
6621 flags = 0;
Eric Andersend35c5df2002-01-09 15:37:36 +00006622#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersen62483552001-07-10 06:09:16 +00006623 if (jobctl)
6624 flags |= WUNTRACED;
6625#endif
6626 if (block == 0)
6627 flags |= WNOHANG;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006628 return wait3(status, flags, (struct rusage *) NULL);
Eric Andersen62483552001-07-10 06:09:16 +00006629}
6630
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006631static int dowait(int block, struct job *job)
Eric Andersencb57d552001-06-28 07:25:16 +00006632{
6633 int pid;
6634 int status;
6635 struct procstat *sp;
6636 struct job *jp;
6637 struct job *thisjob;
6638 int done;
6639 int stopped;
6640 int core;
6641 int sig;
6642
6643 TRACE(("dowait(%d) called\n", block));
6644 do {
6645 pid = waitproc(block, &status);
6646 TRACE(("wait returns %d, status=%d\n", pid, status));
6647 } while (!(block & 2) && pid == -1 && errno == EINTR);
6648 if (pid <= 0)
6649 return pid;
6650 INTOFF;
6651 thisjob = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006652 for (jp = jobtab; jp < jobtab + njobs; jp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006653 if (jp->used) {
6654 done = 1;
6655 stopped = 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006656 for (sp = jp->ps; sp < jp->ps + jp->nprocs; sp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00006657 if (sp->pid == -1)
6658 continue;
6659 if (sp->pid == pid) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006660 TRACE(("Changing status of proc %d from 0x%x to 0x%x\n",
6661 pid, sp->status, status));
Eric Andersencb57d552001-06-28 07:25:16 +00006662 sp->status = status;
6663 thisjob = jp;
6664 }
6665 if (sp->status == -1)
6666 stopped = 0;
6667 else if (WIFSTOPPED(sp->status))
6668 done = 0;
6669 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006670 if (stopped) { /* stopped or done */
Glenn L McGrath50812ff2002-08-23 13:14:48 +00006671 int state = done ? JOBDONE : JOBSTOPPED;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006672
Eric Andersencb57d552001-06-28 07:25:16 +00006673 if (jp->state != state) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006674 TRACE(("Job %d: changing state from %d to %d\n",
6675 jp - jobtab + 1, jp->state, state));
Eric Andersencb57d552001-06-28 07:25:16 +00006676 jp->state = state;
Eric Andersend35c5df2002-01-09 15:37:36 +00006677#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006678 if (done && curjob == jp - jobtab + 1)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006679 curjob = 0; /* no current job */
Eric Andersencb57d552001-06-28 07:25:16 +00006680#endif
6681 }
6682 }
6683 }
6684 }
6685 INTON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006686 if (!rootshell || !iflag || (job && thisjob == job)) {
Eric Andersencb57d552001-06-28 07:25:16 +00006687 core = WCOREDUMP(status);
Eric Andersend35c5df2002-01-09 15:37:36 +00006688#ifdef CONFIG_ASH_JOB_CONTROL
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006689 if (WIFSTOPPED(status))
6690 sig = WSTOPSIG(status);
Eric Andersencb57d552001-06-28 07:25:16 +00006691 else
6692#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006693 if (WIFEXITED(status))
6694 sig = 0;
6695 else
6696 sig = WTERMSIG(status);
Eric Andersencb57d552001-06-28 07:25:16 +00006697
6698 if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
6699 if (thisjob != job)
Eric Andersen3102ac42001-07-06 04:26:23 +00006700 out2fmt("%d: ", pid);
Eric Andersend35c5df2002-01-09 15:37:36 +00006701#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +00006702 if (sig == SIGTSTP && rootshell && iflag)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006703 out2fmt("%%%ld ", (long) (job - jobtab + 1));
Eric Andersencb57d552001-06-28 07:25:16 +00006704#endif
6705 if (sig < NSIG && sys_siglist[sig])
6706 out2str(sys_siglist[sig]);
6707 else
Eric Andersen3102ac42001-07-06 04:26:23 +00006708 out2fmt("Signal %d", sig);
Eric Andersencb57d552001-06-28 07:25:16 +00006709 if (core)
6710 out2str(" - core dumped");
6711 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00006712 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006713 TRACE(("Not printing status: status=%d, sig=%d\n", status, sig));
Eric Andersencb57d552001-06-28 07:25:16 +00006714 }
6715 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006716 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell,
6717 job));
Eric Andersencb57d552001-06-28 07:25:16 +00006718 if (thisjob)
6719 thisjob->changed = 1;
6720 }
6721 return pid;
6722}
6723
6724
6725
Eric Andersencb57d552001-06-28 07:25:16 +00006726
6727/*
6728 * return 1 if there are stopped jobs, otherwise 0
6729 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006730static int stoppedjobs(void)
Eric Andersencb57d552001-06-28 07:25:16 +00006731{
6732 int jobno;
6733 struct job *jp;
6734
6735 if (job_warning)
6736 return (0);
6737 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
6738 if (jp->used == 0)
6739 continue;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00006740 if (jp->state == JOBSTOPPED) {
Eric Andersencb57d552001-06-28 07:25:16 +00006741 out2str("You have stopped jobs.\n");
6742 job_warning = 2;
6743 return (1);
6744 }
6745 }
6746
6747 return (0);
6748}
6749
6750/*
6751 * Return a string identifying a command (to be printed by the
6752 * jobs command.
6753 */
6754
6755static char *cmdnextc;
6756static int cmdnleft;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006757
Eric Andersen2870d962001-07-02 17:27:21 +00006758#define MAXCMDTEXT 200
Eric Andersencb57d552001-06-28 07:25:16 +00006759
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006760static void cmdputs(const char *s)
Eric Andersen2870d962001-07-02 17:27:21 +00006761{
6762 const char *p;
6763 char *q;
6764 char c;
6765 int subtype = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00006766
Eric Andersen2870d962001-07-02 17:27:21 +00006767 if (cmdnleft <= 0)
6768 return;
6769 p = s;
6770 q = cmdnextc;
6771 while ((c = *p++) != '\0') {
6772 if (c == CTLESC)
6773 *q++ = *p++;
6774 else if (c == CTLVAR) {
6775 *q++ = '$';
6776 if (--cmdnleft > 0)
6777 *q++ = '{';
6778 subtype = *p++;
6779 } else if (c == '=' && subtype != 0) {
6780 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
6781 subtype = 0;
6782 } else if (c == CTLENDVAR) {
6783 *q++ = '}';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006784 } else if (c == CTLBACKQ || c == CTLBACKQ + CTLQUOTE)
6785 cmdnleft++; /* ignore it */
Eric Andersen2870d962001-07-02 17:27:21 +00006786 else
6787 *q++ = c;
6788 if (--cmdnleft <= 0) {
6789 *q++ = '.';
6790 *q++ = '.';
6791 *q++ = '.';
6792 break;
6793 }
6794 }
6795 cmdnextc = q;
Eric Andersencb57d552001-06-28 07:25:16 +00006796}
6797
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00006798#define CMDTXT_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006799#ifdef CMDTXT_TABLE
6800/*
6801 * To collect a lot of redundant code in cmdtxt() case statements, we
6802 * implement a mini language here. Each type of node struct has an
6803 * associated instruction sequence that operates on its members via
6804 * their offsets. The instruction are pack in unsigned chars with
6805 * format IIDDDDDE where the bits are
6806 * I : part of the instruction opcode, which are
6807 * 00 : member is a pointer to another node -- process it recursively
6808 * 40 : member is a pointer to a char string -- output it
6809 * 80 : output the string whose index is stored in the data field
6810 * CC : flag signaling that this case needs external processing
6811 * D : data - either the (shifted) index of a fixed string to output or
6812 * the actual offset of the member to operate on in the struct
6813 * (since we assume bit 0 is set, the offset is not shifted)
6814 * E : flag signaling end of instruction sequence
6815 *
6816 * WARNING: In order to handle larger offsets for 64bit archs, this code
6817 * assumes that no offset can be an odd number and stores the
6818 * end-of-instructions flag in bit 0.
6819 */
Eric Andersencb57d552001-06-28 07:25:16 +00006820
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006821#define CMDTXT_NOMORE 0x01 /* NOTE: no offset should be odd */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006822#define CMDTXT_CHARPTR 0x40
6823#define CMDTXT_STRING 0x80
6824#define CMDTXT_SPECIAL 0xC0
6825#define CMDTXT_OFFSETMASK 0x3E
6826
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006827static const char *const cmdtxt_strings[] = {
6828 /* 0 1 2 3 4 5 6 7 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006829 "; ", "(", ")", " && ", " || ", "if ", "; then ", "...",
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006830 /* 8 9 10 11 12 13 */
6831 "while ", "; do ", "; done", "until ", "for ", " in ...",
6832 /* 14 15 16 17 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006833 "case ", "???", "() ...", "<<..."
6834};
6835
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006836static const char *const redir_strings[] = {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006837 ">", "<", "<>", ">>", ">|", ">&", "<&"
6838};
6839
6840static const unsigned char cmdtxt_ops[] = {
6841#define CMDTXT_NSEMI 0
6842 offsetof(union node, nbinary.ch1),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006843 0 | CMDTXT_STRING,
6844 offsetof(union node, nbinary.ch2) | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006845#define CMDTXT_NCMD (CMDTXT_NSEMI + 3)
6846#define CMDTXT_NPIPE (CMDTXT_NCMD)
6847#define CMDTXT_NCASE (CMDTXT_NCMD)
6848#define CMDTXT_NTO (CMDTXT_NCMD)
6849#define CMDTXT_NFROM (CMDTXT_NCMD)
6850#define CMDTXT_NFROMTO (CMDTXT_NCMD)
6851#define CMDTXT_NAPPEND (CMDTXT_NCMD)
6852#define CMDTXT_NTOOV (CMDTXT_NCMD)
6853#define CMDTXT_NTOFD (CMDTXT_NCMD)
6854#define CMDTXT_NFROMFD (CMDTXT_NCMD)
6855 CMDTXT_SPECIAL,
6856#define CMDTXT_NREDIR (CMDTXT_NPIPE + 1)
6857#define CMDTXT_NBACKGND (CMDTXT_NREDIR)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006858 offsetof(union node, nredir.n) | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006859#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006860 (1 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006861 offsetof(union node, nredir.n),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006862 (2 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006863#define CMDTXT_NAND (CMDTXT_NSUBSHELL + 3)
6864 offsetof(union node, nbinary.ch1),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006865 (3 * 2) | CMDTXT_STRING,
6866 offsetof(union node, nbinary.ch2) | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006867#define CMDTXT_NOR (CMDTXT_NAND + 3)
6868 offsetof(union node, nbinary.ch1),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006869 (4 * 2) | CMDTXT_STRING,
6870 offsetof(union node, nbinary.ch2) | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006871#define CMDTXT_NIF (CMDTXT_NOR + 3)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006872 (5 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006873 offsetof(union node, nif.test),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006874 (6 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006875 offsetof(union node, nif.ifpart),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006876 (7 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006877#define CMDTXT_NWHILE (CMDTXT_NIF + 5)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006878 (8 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006879 offsetof(union node, nbinary.ch1),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006880 (9 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006881 offsetof(union node, nbinary.ch2),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006882 (10 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006883#define CMDTXT_NUNTIL (CMDTXT_NWHILE + 5)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006884 (11 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006885 offsetof(union node, nbinary.ch1),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006886 (9 * 2) | CMDTXT_STRING,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006887 offsetof(union node, nbinary.ch2),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006888 (10 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006889#define CMDTXT_NFOR (CMDTXT_NUNTIL + 5)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006890 (12 * 2) | CMDTXT_STRING,
6891 offsetof(union node, nfor.var) | CMDTXT_CHARPTR,
6892 (13 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
6893#define CMDTXT_NCLIST (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */
6894#define CMDTXT_NNOT (CMDTXT_NCLIST) /* TODO: IS THIS CORRECT??? */
6895 (15 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006896#define CMDTXT_NDEFUN (CMDTXT_NCLIST + 1)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006897 offsetof(union node, narg.text) | CMDTXT_CHARPTR,
6898 (16 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006899#define CMDTXT_NARG (CMDTXT_NDEFUN + 2)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006900 offsetof(union node, narg.text) | CMDTXT_CHARPTR | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006901#define CMDTXT_NHERE (CMDTXT_NARG + 1)
6902#define CMDTXT_NXHERE (CMDTXT_NHERE)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006903 (17 * 2) | CMDTXT_STRING | CMDTXT_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006904};
6905
6906#if CMDTXT_NXHERE != 36
6907#error CMDTXT_NXHERE
6908#endif
6909
6910static const unsigned char cmdtxt_ops_index[26] = {
6911 CMDTXT_NSEMI,
6912 CMDTXT_NCMD,
6913 CMDTXT_NPIPE,
6914 CMDTXT_NREDIR,
6915 CMDTXT_NBACKGND,
6916 CMDTXT_NSUBSHELL,
6917 CMDTXT_NAND,
6918 CMDTXT_NOR,
6919 CMDTXT_NIF,
6920 CMDTXT_NWHILE,
6921 CMDTXT_NUNTIL,
6922 CMDTXT_NFOR,
6923 CMDTXT_NCASE,
6924 CMDTXT_NCLIST,
6925 CMDTXT_NDEFUN,
6926 CMDTXT_NARG,
6927 CMDTXT_NTO,
6928 CMDTXT_NFROM,
6929 CMDTXT_NFROMTO,
6930 CMDTXT_NAPPEND,
6931 CMDTXT_NTOOV,
6932 CMDTXT_NTOFD,
6933 CMDTXT_NFROMFD,
6934 CMDTXT_NHERE,
6935 CMDTXT_NXHERE,
6936 CMDTXT_NNOT,
6937};
6938
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006939static void cmdtxt(const union node *n)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006940{
6941 const char *p;
6942
6943 if (n == NULL)
6944 return;
6945
6946 p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006947 if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006948 do {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006949 if (*p & CMDTXT_STRING) { /* output fixed string */
6950 cmdputs(cmdtxt_strings
6951 [((int) (*p & CMDTXT_OFFSETMASK) >> 1)]);
Manuel Novoa III c639a352001-08-12 17:32:56 +00006952 } else {
6953 const char *pf = ((const char *) n)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006954 + ((int) (*p & CMDTXT_OFFSETMASK));
6955
6956 if (*p & CMDTXT_CHARPTR) { /* output dynamic string */
Manuel Novoa III c639a352001-08-12 17:32:56 +00006957 cmdputs(*((const char **) pf));
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006958 } else { /* output field */
Manuel Novoa III c639a352001-08-12 17:32:56 +00006959 cmdtxt(*((const union node **) pf));
6960 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006961 }
6962 } while (!(*p++ & CMDTXT_NOMORE));
6963 } else if (n->type == NCMD) {
6964 union node *np;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006965
6966 for (np = n->ncmd.args; np; np = np->narg.next) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006967 cmdtxt(np);
6968 if (np->narg.next)
6969 cmdputs(spcstr);
6970 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006971 for (np = n->ncmd.redirect; np; np = np->nfile.next) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006972 cmdputs(spcstr);
6973 cmdtxt(np);
6974 }
6975 } else if (n->type == NPIPE) {
6976 struct nodelist *lp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00006977
6978 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00006979 cmdtxt(lp->n);
6980 if (lp->next)
6981 cmdputs(" | ");
6982 }
6983 } else if (n->type == NCASE) {
6984 cmdputs(cmdtxt_strings[14]);
6985 cmdputs(n->ncase.expr->narg.text);
6986 cmdputs(cmdtxt_strings[13]);
6987 } else {
6988#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22)
6989#error Assumption violated regarding range and ordering of NTO ... NFROMFD!
6990#endif
6991 char s[2];
6992
6993#ifdef DEBUG
6994 assert((n->type >= NTO) && (n->type <= NFROMFD));
6995#endif
6996
6997 p = redir_strings[n->type - NTO];
6998 if (n->nfile.fd != ('>' == *p)) {
6999 s[0] = n->nfile.fd + '0';
7000 s[1] = '\0';
7001 cmdputs(s);
7002 }
7003 cmdputs(p);
7004 if (n->type >= NTOFD) {
7005 s[0] = n->ndup.dupfd + '0';
7006 s[1] = '\0';
7007 cmdputs(s);
7008 } else {
7009 cmdtxt(n->nfile.fname);
7010 }
7011 }
7012}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007013#else /* CMDTXT_TABLE */
7014static void cmdtxt(const union node *n)
Eric Andersen2870d962001-07-02 17:27:21 +00007015{
Eric Andersencb57d552001-06-28 07:25:16 +00007016 union node *np;
7017 struct nodelist *lp;
7018 const char *p;
7019 int i;
7020 char s[2];
7021
7022 if (n == NULL)
7023 return;
7024 switch (n->type) {
7025 case NSEMI:
7026 cmdtxt(n->nbinary.ch1);
7027 cmdputs("; ");
7028 cmdtxt(n->nbinary.ch2);
7029 break;
7030 case NAND:
7031 cmdtxt(n->nbinary.ch1);
7032 cmdputs(" && ");
7033 cmdtxt(n->nbinary.ch2);
7034 break;
7035 case NOR:
7036 cmdtxt(n->nbinary.ch1);
7037 cmdputs(" || ");
7038 cmdtxt(n->nbinary.ch2);
7039 break;
7040 case NPIPE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007041 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007042 cmdtxt(lp->n);
7043 if (lp->next)
7044 cmdputs(" | ");
7045 }
7046 break;
7047 case NSUBSHELL:
7048 cmdputs("(");
7049 cmdtxt(n->nredir.n);
7050 cmdputs(")");
7051 break;
7052 case NREDIR:
7053 case NBACKGND:
7054 cmdtxt(n->nredir.n);
7055 break;
7056 case NIF:
7057 cmdputs("if ");
7058 cmdtxt(n->nif.test);
7059 cmdputs("; then ");
7060 cmdtxt(n->nif.ifpart);
7061 cmdputs("...");
7062 break;
7063 case NWHILE:
7064 cmdputs("while ");
7065 goto until;
7066 case NUNTIL:
7067 cmdputs("until ");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007068 until:
Eric Andersencb57d552001-06-28 07:25:16 +00007069 cmdtxt(n->nbinary.ch1);
7070 cmdputs("; do ");
7071 cmdtxt(n->nbinary.ch2);
7072 cmdputs("; done");
7073 break;
7074 case NFOR:
7075 cmdputs("for ");
7076 cmdputs(n->nfor.var);
7077 cmdputs(" in ...");
7078 break;
7079 case NCASE:
7080 cmdputs("case ");
7081 cmdputs(n->ncase.expr->narg.text);
7082 cmdputs(" in ...");
7083 break;
7084 case NDEFUN:
7085 cmdputs(n->narg.text);
7086 cmdputs("() ...");
7087 break;
7088 case NCMD:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007089 for (np = n->ncmd.args; np; np = np->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007090 cmdtxt(np);
7091 if (np->narg.next)
7092 cmdputs(spcstr);
7093 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007094 for (np = n->ncmd.redirect; np; np = np->nfile.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007095 cmdputs(spcstr);
7096 cmdtxt(np);
7097 }
7098 break;
7099 case NARG:
7100 cmdputs(n->narg.text);
7101 break;
7102 case NTO:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007103 p = ">";
7104 i = 1;
7105 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007106 case NAPPEND:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007107 p = ">>";
7108 i = 1;
7109 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007110 case NTOFD:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007111 p = ">&";
7112 i = 1;
7113 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007114 case NTOOV:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007115 p = ">|";
7116 i = 1;
7117 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007118 case NFROM:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007119 p = "<";
7120 i = 0;
7121 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007122 case NFROMFD:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007123 p = "<&";
7124 i = 0;
7125 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00007126 case NFROMTO:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007127 p = "<>";
7128 i = 0;
7129 goto redir;
7130 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00007131 if (n->nfile.fd != i) {
7132 s[0] = n->nfile.fd + '0';
7133 s[1] = '\0';
7134 cmdputs(s);
7135 }
7136 cmdputs(p);
7137 if (n->type == NTOFD || n->type == NFROMFD) {
7138 s[0] = n->ndup.dupfd + '0';
7139 s[1] = '\0';
7140 cmdputs(s);
7141 } else {
7142 cmdtxt(n->nfile.fname);
7143 }
7144 break;
7145 case NHERE:
7146 case NXHERE:
7147 cmdputs("<<...");
7148 break;
7149 default:
7150 cmdputs("???");
7151 break;
7152 }
7153}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007154#endif /* CMDTXT_TABLE */
Eric Andersencb57d552001-06-28 07:25:16 +00007155
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007156static char *commandtext(const union node *n)
Eric Andersen2870d962001-07-02 17:27:21 +00007157{
7158 char *name;
Eric Andersencb57d552001-06-28 07:25:16 +00007159
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007160 cmdnextc = name = xmalloc(MAXCMDTEXT);
Eric Andersen2870d962001-07-02 17:27:21 +00007161 cmdnleft = MAXCMDTEXT - 4;
7162 cmdtxt(n);
7163 *cmdnextc = '\0';
7164 return name;
Eric Andersencb57d552001-06-28 07:25:16 +00007165}
7166
Eric Andersen2870d962001-07-02 17:27:21 +00007167
Eric Andersend35c5df2002-01-09 15:37:36 +00007168#ifdef CONFIG_ASH_MAIL
Eric Andersenec074692001-10-31 11:05:49 +00007169
Eric Andersencb57d552001-06-28 07:25:16 +00007170/*
Eric Andersenec074692001-10-31 11:05:49 +00007171 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00007172 */
7173
7174
7175#define MAXMBOXES 10
7176
7177
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007178static int nmboxes; /* number of mailboxes */
7179static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
Eric Andersencb57d552001-06-28 07:25:16 +00007180
7181
7182
7183/*
7184 * Print appropriate message(s) if mail has arrived. If the argument is
7185 * nozero, then the value of MAIL has changed, so we just update the
7186 * values.
7187 */
7188
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007189static void chkmail(int silent)
Eric Andersencb57d552001-06-28 07:25:16 +00007190{
7191 int i;
7192 const char *mpath;
7193 char *p;
7194 char *q;
7195 struct stackmark smark;
7196 struct stat statb;
7197
7198 if (silent)
7199 nmboxes = 10;
7200 if (nmboxes == 0)
7201 return;
7202 setstackmark(&smark);
7203 mpath = mpathset()? mpathval() : mailval();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007204 for (i = 0; i < nmboxes; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00007205 p = padvance(&mpath, nullstr);
7206 if (p == NULL)
7207 break;
7208 if (*p == '\0')
7209 continue;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007210 for (q = p; *q; q++);
Eric Andersencb57d552001-06-28 07:25:16 +00007211#ifdef DEBUG
7212 if (q[-1] != '/')
7213 abort();
7214#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007215 q[-1] = '\0'; /* delete trailing '/' */
Eric Andersencb57d552001-06-28 07:25:16 +00007216 if (stat(p, &statb) < 0)
7217 statb.st_size = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007218 if (statb.st_size > mailtime[i] && !silent) {
7219 out2fmt(snlfmt, pathopt ? pathopt : "you have mail");
Eric Andersencb57d552001-06-28 07:25:16 +00007220 }
7221 mailtime[i] = statb.st_size;
Eric Andersencb57d552001-06-28 07:25:16 +00007222 }
7223 nmboxes = i;
7224 popstackmark(&smark);
7225}
Eric Andersencb57d552001-06-28 07:25:16 +00007226
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007227#endif /* CONFIG_ASH_MAIL */
Eric Andersenec074692001-10-31 11:05:49 +00007228
Eric Andersencb57d552001-06-28 07:25:16 +00007229#define PROFILE 0
7230
Eric Andersencb57d552001-06-28 07:25:16 +00007231#if PROFILE
Eric Andersen2870d962001-07-02 17:27:21 +00007232static short profile_buf[16384];
Eric Andersencb57d552001-06-28 07:25:16 +00007233extern int etext();
7234#endif
7235
Robert Griebl64f70cc2002-05-14 23:22:06 +00007236static int isloginsh = 0;
7237
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007238static void read_profile(const char *);
7239static void cmdloop(int);
7240static void options(int);
7241static void setoption(int, int);
7242static void procargs(int, char **);
Eric Andersencb57d552001-06-28 07:25:16 +00007243
Eric Andersen2870d962001-07-02 17:27:21 +00007244
Eric Andersencb57d552001-06-28 07:25:16 +00007245/*
7246 * Main routine. We initialize things, parse the arguments, execute
7247 * profiles if we're a login shell, and then call cmdloop to execute
7248 * commands. The setjmp call sets up the location to jump to when an
7249 * exception occurs. When an exception occurs the variable "state"
7250 * is used to figure out how far we had gotten.
7251 */
7252
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007253int ash_main(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007254{
7255 struct jmploc jmploc;
7256 struct stackmark smark;
7257 volatile int state;
Eric Andersen62483552001-07-10 06:09:16 +00007258 const char *shinit;
Eric Andersencb57d552001-06-28 07:25:16 +00007259
Eric Andersencb57d552001-06-28 07:25:16 +00007260 BLTINCMD = find_builtin("builtin");
Eric Andersencb57d552001-06-28 07:25:16 +00007261 EXECCMD = find_builtin("exec");
7262 EVALCMD = find_builtin("eval");
7263
Eric Andersenbdfd0d72001-10-24 05:00:29 +00007264#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
Eric Andersen1c039232001-07-07 00:05:55 +00007265 unsetenv("PS1");
7266 unsetenv("PS2");
7267#endif
7268
Eric Andersencb57d552001-06-28 07:25:16 +00007269#if PROFILE
7270 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7271#endif
7272#if defined(linux) || defined(__GNU__)
7273 signal(SIGCHLD, SIG_DFL);
7274#endif
7275 state = 0;
7276 if (setjmp(jmploc.loc)) {
7277 INTOFF;
7278 /*
7279 * When a shell procedure is executed, we raise the
7280 * exception EXSHELLPROC to clean up before executing
7281 * the shell procedure.
7282 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007283 if (exception == EXSHELLPROC) {
Eric Andersencb57d552001-06-28 07:25:16 +00007284 rootpid = getpid();
7285 rootshell = 1;
7286 minusc = NULL;
7287 state = 3;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007288 } else {
7289 if (exception == EXEXEC) {
7290 exitstatus = exerrno;
7291 } else if (exception == EXERROR) {
7292 exitstatus = 2;
7293 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007294 if (state == 0 || iflag == 0 || !rootshell)
7295 exitshell(exitstatus);
Eric Andersencb57d552001-06-28 07:25:16 +00007296 }
7297 reset();
Eric Andersen2870d962001-07-02 17:27:21 +00007298 if (exception == EXINT) {
Eric Andersencb57d552001-06-28 07:25:16 +00007299 out2c('\n');
Eric Andersencb57d552001-06-28 07:25:16 +00007300 }
7301 popstackmark(&smark);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007302 FORCEINTON; /* enable interrupts */
Eric Andersencb57d552001-06-28 07:25:16 +00007303 if (state == 1)
7304 goto state1;
7305 else if (state == 2)
7306 goto state2;
7307 else if (state == 3)
7308 goto state3;
7309 else
7310 goto state4;
7311 }
7312 handler = &jmploc;
7313#ifdef DEBUG
7314 opentrace();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007315 trputs("Shell args: ");
7316 trargs(argv);
Eric Andersencb57d552001-06-28 07:25:16 +00007317#endif
7318 rootpid = getpid();
7319 rootshell = 1;
7320 init();
7321 setstackmark(&smark);
7322 procargs(argc, argv);
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00007323#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7324 if ( iflag ) {
7325 const char *hp = lookupvar("HISTFILE");
7326
7327 if(hp == NULL ) {
7328 hp = lookupvar("HOME");
7329 if(hp != NULL) {
7330 char *defhp = concat_path_file(hp, ".ash_history");
7331 setvar("HISTFILE", defhp, 0);
7332 free(defhp);
7333 }
7334 }
7335 }
7336#endif
Robert Griebl64f70cc2002-05-14 23:22:06 +00007337 if (argv[0] && argv[0][0] == '-')
7338 isloginsh = 1;
7339 if (isloginsh) {
Eric Andersencb57d552001-06-28 07:25:16 +00007340 state = 1;
7341 read_profile("/etc/profile");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007342 state1:
Eric Andersencb57d552001-06-28 07:25:16 +00007343 state = 2;
7344 read_profile(".profile");
7345 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007346 state2:
Eric Andersencb57d552001-06-28 07:25:16 +00007347 state = 3;
7348#ifndef linux
7349 if (getuid() == geteuid() && getgid() == getegid()) {
7350#endif
7351 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7352 state = 3;
7353 read_profile(shinit);
7354 }
7355#ifndef linux
7356 }
7357#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007358 state3:
Eric Andersencb57d552001-06-28 07:25:16 +00007359 state = 4;
7360 if (sflag == 0 || minusc) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007361 static const char sigs[] = {
7362 SIGINT, SIGQUIT, SIGHUP,
Eric Andersencb57d552001-06-28 07:25:16 +00007363#ifdef SIGTSTP
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007364 SIGTSTP,
Eric Andersencb57d552001-06-28 07:25:16 +00007365#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007366 SIGPIPE
Eric Andersencb57d552001-06-28 07:25:16 +00007367 };
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007368
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007369#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])))
Eric Andersencb57d552001-06-28 07:25:16 +00007370 int i;
7371
7372 for (i = 0; i < SIGSSIZE; i++)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007373 setsignal(sigs[i]);
Eric Andersencb57d552001-06-28 07:25:16 +00007374 }
7375
7376 if (minusc)
7377 evalstring(minusc, 0);
7378
7379 if (sflag || minusc == NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007380 state4: /* XXX ??? - why isn't this before the "if" statement */
Robert Griebl350d26b2002-12-03 22:45:46 +00007381#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00007382 if ( iflag ) {
7383 const char *hp = lookupvar("HISTFILE");
7384
7385 if(hp != NULL )
7386 load_history ( hp );
7387 }
Robert Griebl350d26b2002-12-03 22:45:46 +00007388#endif
Eric Andersencb57d552001-06-28 07:25:16 +00007389 cmdloop(1);
7390 }
7391#if PROFILE
7392 monitor(0);
7393#endif
7394 exitshell(exitstatus);
7395 /* NOTREACHED */
7396}
7397
7398
7399/*
7400 * Read and execute commands. "Top" is nonzero for the top level command
7401 * loop; it turns on prompting if the shell is interactive.
7402 */
7403
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007404static void cmdloop(int top)
Eric Andersencb57d552001-06-28 07:25:16 +00007405{
7406 union node *n;
7407 struct stackmark smark;
7408 int inter;
7409 int numeof = 0;
7410
7411 TRACE(("cmdloop(%d) called\n", top));
7412 setstackmark(&smark);
7413 for (;;) {
7414 if (pendingsigs)
7415 dotrap();
7416 inter = 0;
7417 if (iflag && top) {
7418 inter++;
7419 showjobs(1);
Eric Andersend35c5df2002-01-09 15:37:36 +00007420#ifdef CONFIG_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00007421 chkmail(0);
Eric Andersenec074692001-10-31 11:05:49 +00007422#endif
Eric Andersen3102ac42001-07-06 04:26:23 +00007423 flushall();
Eric Andersencb57d552001-06-28 07:25:16 +00007424 }
7425 n = parsecmd(inter);
7426 /* showtree(n); DEBUG */
7427 if (n == NEOF) {
7428 if (!top || numeof >= 50)
7429 break;
7430 if (!stoppedjobs()) {
7431 if (!Iflag)
7432 break;
7433 out2str("\nUse \"exit\" to leave shell.\n");
7434 }
7435 numeof++;
7436 } else if (n != NULL && nflag == 0) {
7437 job_warning = (job_warning == 2) ? 1 : 0;
7438 numeof = 0;
7439 evaltree(n, 0);
7440 }
7441 popstackmark(&smark);
7442 setstackmark(&smark);
7443 if (evalskip == SKIPFILE) {
7444 evalskip = 0;
7445 break;
7446 }
7447 }
7448 popstackmark(&smark);
7449}
7450
7451
7452
7453/*
7454 * Read /etc/profile or .profile. Return on error.
7455 */
7456
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007457static void read_profile(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00007458{
7459 int fd;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007460 int xflag_save;
7461 int vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007462
7463 INTOFF;
7464 if ((fd = open(name, O_RDONLY)) >= 0)
7465 setinputfd(fd, 1);
7466 INTON;
7467 if (fd < 0)
7468 return;
7469 /* -q turns off -x and -v just when executing init files */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007470 /* Note: Might do a little redundant work, but reduces code size. */
7471 xflag_save = xflag;
7472 vflag_save = vflag;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007473 if (qflag) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007474 vflag = xflag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00007475 }
7476 cmdloop(0);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007477 xflag = xflag_save;
7478 vflag = vflag_save;
Eric Andersencb57d552001-06-28 07:25:16 +00007479 popfile();
7480}
7481
7482
7483
7484/*
7485 * Read a file containing shell functions.
7486 */
7487
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007488static void readcmdfile(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +00007489{
7490 int fd;
7491
7492 INTOFF;
7493 if ((fd = open(name, O_RDONLY)) >= 0)
7494 setinputfd(fd, 1);
7495 else
7496 error("Can't open %s", name);
7497 INTON;
7498 cmdloop(0);
7499 popfile();
7500}
7501
7502
7503
7504/*
7505 * Take commands from a file. To be compatable we should do a path
7506 * search for the file, which is necessary to find sub-commands.
7507 */
7508
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007509static inline char *find_dot_file(char *mybasename)
Eric Andersencb57d552001-06-28 07:25:16 +00007510{
7511 char *fullname;
7512 const char *path = pathval();
7513 struct stat statb;
7514
7515 /* don't try this for absolute or relative paths */
7516 if (strchr(mybasename, '/'))
7517 return mybasename;
7518
7519 while ((fullname = padvance(&path, mybasename)) != NULL) {
7520 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
7521 /*
7522 * Don't bother freeing here, since it will
7523 * be freed by the caller.
7524 */
7525 return fullname;
7526 }
7527 stunalloc(fullname);
7528 }
7529
7530 /* not found in the PATH */
7531 error("%s: not found", mybasename);
7532 /* NOTREACHED */
7533}
7534
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007535static int dotcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007536{
7537 struct strlist *sp;
Eric Andersen69a20f02001-10-31 10:40:37 +00007538 volatile struct shparam saveparam;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007539
Eric Andersencb57d552001-06-28 07:25:16 +00007540 exitstatus = 0;
7541
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007542 for (sp = cmdenviron; sp; sp = sp->next)
7543 setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +00007544
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007545 if (argc >= 2) { /* That's what SVR2 does */
Eric Andersencb57d552001-06-28 07:25:16 +00007546 char *fullname;
7547 struct stackmark smark;
7548
7549 setstackmark(&smark);
7550 fullname = find_dot_file(argv[1]);
Eric Andersen69a20f02001-10-31 10:40:37 +00007551
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007552 if (argc > 2) {
Eric Andersen69a20f02001-10-31 10:40:37 +00007553 saveparam = shellparam;
7554 shellparam.malloc = 0;
7555 shellparam.nparam = argc - 2;
7556 shellparam.p = argv + 2;
7557 };
7558
Eric Andersencb57d552001-06-28 07:25:16 +00007559 setinputfile(fullname, 1);
7560 commandname = fullname;
7561 cmdloop(0);
7562 popfile();
Eric Andersen69a20f02001-10-31 10:40:37 +00007563
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007564 if (argc > 2) {
Eric Andersen69a20f02001-10-31 10:40:37 +00007565 freeparam(&shellparam);
7566 shellparam = saveparam;
7567 };
7568
Eric Andersencb57d552001-06-28 07:25:16 +00007569 popstackmark(&smark);
7570 }
7571 return exitstatus;
7572}
7573
7574
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007575static int exitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007576{
7577 if (stoppedjobs())
7578 return 0;
Robert Griebl350d26b2002-12-03 22:45:46 +00007579
Eric Andersencb57d552001-06-28 07:25:16 +00007580 if (argc > 1)
7581 exitstatus = number(argv[1]);
7582 else
7583 exitstatus = oexitstatus;
7584 exitshell(exitstatus);
7585 /* NOTREACHED */
7586}
Eric Andersen62483552001-07-10 06:09:16 +00007587
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007588static pointer stalloc(int nbytes)
Eric Andersencb57d552001-06-28 07:25:16 +00007589{
7590 char *p;
7591
7592 nbytes = ALIGN(nbytes);
7593 if (nbytes > stacknleft) {
7594 int blocksize;
7595 struct stack_block *sp;
7596
7597 blocksize = nbytes;
7598 if (blocksize < MINSIZE)
7599 blocksize = MINSIZE;
7600 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00007601 sp = xmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
Eric Andersencb57d552001-06-28 07:25:16 +00007602 sp->prev = stackp;
7603 stacknxt = sp->space;
7604 stacknleft = blocksize;
7605 stackp = sp;
7606 INTON;
7607 }
7608 p = stacknxt;
7609 stacknxt += nbytes;
7610 stacknleft -= nbytes;
7611 return p;
7612}
7613
7614
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007615static void stunalloc(pointer p)
Eric Andersen2870d962001-07-02 17:27:21 +00007616{
Eric Andersencb57d552001-06-28 07:25:16 +00007617#ifdef DEBUG
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007618 if (p == NULL) { /*DEBUG */
Eric Andersencb57d552001-06-28 07:25:16 +00007619 write(2, "stunalloc\n", 10);
7620 abort();
7621 }
7622#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007623 if (!(stacknxt >= (char *) p && (char *) p >= stackp->space)) {
Eric Andersencb57d552001-06-28 07:25:16 +00007624 p = stackp->space;
7625 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007626 stacknleft += stacknxt - (char *) p;
Eric Andersencb57d552001-06-28 07:25:16 +00007627 stacknxt = p;
7628}
7629
7630
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007631static void setstackmark(struct stackmark *mark)
Eric Andersen2870d962001-07-02 17:27:21 +00007632{
Eric Andersencb57d552001-06-28 07:25:16 +00007633 mark->stackp = stackp;
7634 mark->stacknxt = stacknxt;
7635 mark->stacknleft = stacknleft;
7636 mark->marknext = markp;
7637 markp = mark;
7638}
7639
7640
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007641static void popstackmark(struct stackmark *mark)
Eric Andersen2870d962001-07-02 17:27:21 +00007642{
Eric Andersencb57d552001-06-28 07:25:16 +00007643 struct stack_block *sp;
7644
7645 INTOFF;
7646 markp = mark->marknext;
7647 while (stackp != mark->stackp) {
7648 sp = stackp;
7649 stackp = sp->prev;
Aaron Lehmann49c024a2002-08-02 06:39:47 +00007650 free(sp);
Eric Andersencb57d552001-06-28 07:25:16 +00007651 }
7652 stacknxt = mark->stacknxt;
7653 stacknleft = mark->stacknleft;
7654 INTON;
7655}
7656
7657
7658/*
7659 * When the parser reads in a string, it wants to stick the string on the
7660 * stack and only adjust the stack pointer when it knows how big the
7661 * string is. Stackblock (defined in stack.h) returns a pointer to a block
7662 * of space on top of the stack and stackblocklen returns the length of
7663 * this block. Growstackblock will grow this space by at least one byte,
7664 * possibly moving it (like realloc). Grabstackblock actually allocates the
7665 * part of the block that has been used.
7666 */
7667
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007668static void growstackblock(void)
7669{
Eric Andersencb57d552001-06-28 07:25:16 +00007670 char *p;
7671 int newlen = ALIGN(stacknleft * 2 + 100);
7672 char *oldspace = stacknxt;
7673 int oldlen = stacknleft;
7674 struct stack_block *sp;
7675 struct stack_block *oldstackp;
7676
7677 if (stacknxt == stackp->space && stackp != &stackbase) {
7678 INTOFF;
7679 oldstackp = stackp;
7680 sp = stackp;
7681 stackp = sp->prev;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007682 sp = xrealloc((pointer) sp,
7683 sizeof(struct stack_block) - MINSIZE + newlen);
Eric Andersencb57d552001-06-28 07:25:16 +00007684 sp->prev = stackp;
7685 stackp = sp;
7686 stacknxt = sp->space;
7687 stacknleft = newlen;
7688 {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007689 /* Stack marks pointing to the start of the old block
7690 * must be relocated to point to the new block
7691 */
7692 struct stackmark *xmark;
7693
7694 xmark = markp;
7695 while (xmark != NULL && xmark->stackp == oldstackp) {
7696 xmark->stackp = stackp;
7697 xmark->stacknxt = stacknxt;
7698 xmark->stacknleft = stacknleft;
7699 xmark = xmark->marknext;
7700 }
Eric Andersencb57d552001-06-28 07:25:16 +00007701 }
7702 INTON;
7703 } else {
7704 p = stalloc(newlen);
7705 memcpy(p, oldspace, oldlen);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007706 stacknxt = p; /* free the space */
7707 stacknleft += newlen; /* we just allocated */
Eric Andersencb57d552001-06-28 07:25:16 +00007708 }
7709}
7710
7711
7712
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007713static inline void grabstackblock(int len)
Eric Andersencb57d552001-06-28 07:25:16 +00007714{
7715 len = ALIGN(len);
7716 stacknxt += len;
7717 stacknleft -= len;
7718}
7719
7720
7721
7722/*
7723 * The following routines are somewhat easier to use that the above.
7724 * The user declares a variable of type STACKSTR, which may be declared
7725 * to be a register. The macro STARTSTACKSTR initializes things. Then
7726 * the user uses the macro STPUTC to add characters to the string. In
7727 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
7728 * grown as necessary. When the user is done, she can just leave the
7729 * string there and refer to it using stackblock(). Or she can allocate
7730 * the space for it using grabstackstr(). If it is necessary to allow
7731 * someone else to use the stack temporarily and then continue to grow
7732 * the string, the user should use grabstack to allocate the space, and
7733 * then call ungrabstr(p) to return to the previous mode of operation.
7734 *
7735 * USTPUTC is like STPUTC except that it doesn't check for overflow.
7736 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
7737 * is space for at least one character.
7738 */
7739
7740
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007741static char *growstackstr(void)
7742{
Eric Andersencb57d552001-06-28 07:25:16 +00007743 int len = stackblocksize();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007744
Eric Andersencb57d552001-06-28 07:25:16 +00007745 if (herefd >= 0 && len >= 1024) {
7746 xwrite(herefd, stackblock(), len);
7747 sstrnleft = len - 1;
7748 return stackblock();
7749 }
7750 growstackblock();
7751 sstrnleft = stackblocksize() - len - 1;
7752 return stackblock() + len;
7753}
7754
7755
7756/*
7757 * Called from CHECKSTRSPACE.
7758 */
7759
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007760static char *makestrspace(size_t newlen)
7761{
Eric Andersencb57d552001-06-28 07:25:16 +00007762 int len = stackblocksize() - sstrnleft;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007763
Eric Andersencb57d552001-06-28 07:25:16 +00007764 do {
7765 growstackblock();
7766 sstrnleft = stackblocksize() - len;
7767 } while (sstrnleft < newlen);
7768 return stackblock() + len;
7769}
7770
7771
7772
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007773static void ungrabstackstr(char *s, char *p)
Eric Andersen2870d962001-07-02 17:27:21 +00007774{
Eric Andersencb57d552001-06-28 07:25:16 +00007775 stacknleft += stacknxt - s;
7776 stacknxt = s;
7777 sstrnleft = stacknleft - (p - s);
7778}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007779
Eric Andersencb57d552001-06-28 07:25:16 +00007780/*
7781 * Miscelaneous builtins.
7782 */
7783
7784
7785#undef rflag
7786
Eric Andersencb57d552001-06-28 07:25:16 +00007787#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersen62483552001-07-10 06:09:16 +00007788typedef long rlim_t;
Eric Andersencb57d552001-06-28 07:25:16 +00007789#endif
7790
7791
7792
7793/*
7794 * The read builtin. The -e option causes backslashes to escape the
7795 * following character.
7796 *
7797 * This uses unbuffered input, which may be avoidable in some cases.
7798 */
7799
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007800static int readcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007801{
7802 char **ap;
7803 int backslash;
7804 char c;
7805 int rflag;
7806 char *prompt;
7807 const char *ifs;
7808 char *p;
7809 int startword;
7810 int status;
7811 int i;
7812
7813 rflag = 0;
7814 prompt = NULL;
7815 while ((i = nextopt("p:r")) != '\0') {
7816 if (i == 'p')
7817 prompt = optionarg;
7818 else
7819 rflag = 1;
7820 }
7821 if (prompt && isatty(0)) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007822 out2str(prompt); /* read without cmdedit */
Eric Andersencb57d552001-06-28 07:25:16 +00007823 flushall();
7824 }
7825 if (*(ap = argptr) == NULL)
7826 error("arg count");
7827 if ((ifs = bltinlookup("IFS")) == NULL)
7828 ifs = defifs;
7829 status = 0;
7830 startword = 1;
7831 backslash = 0;
7832 STARTSTACKSTR(p);
7833 for (;;) {
7834 if (read(0, &c, 1) != 1) {
7835 status = 1;
7836 break;
7837 }
7838 if (c == '\0')
7839 continue;
7840 if (backslash) {
7841 backslash = 0;
7842 if (c != '\n')
7843 STPUTC(c, p);
7844 continue;
7845 }
7846 if (!rflag && c == '\\') {
7847 backslash++;
7848 continue;
7849 }
7850 if (c == '\n')
7851 break;
7852 if (startword && *ifs == ' ' && strchr(ifs, c)) {
7853 continue;
7854 }
7855 startword = 0;
7856 if (backslash && c == '\\') {
7857 if (read(0, &c, 1) != 1) {
7858 status = 1;
7859 break;
7860 }
7861 STPUTC(c, p);
7862 } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
7863 STACKSTRNUL(p);
7864 setvar(*ap, stackblock(), 0);
7865 ap++;
7866 startword = 1;
7867 STARTSTACKSTR(p);
7868 } else {
7869 STPUTC(c, p);
7870 }
7871 }
7872 STACKSTRNUL(p);
7873 /* Remove trailing blanks */
7874 while (stackblock() <= --p && strchr(ifs, *p) != NULL)
7875 *p = '\0';
7876 setvar(*ap, stackblock(), 0);
7877 while (*++ap != NULL)
7878 setvar(*ap, nullstr, 0);
7879 return status;
7880}
7881
7882
7883
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007884static int umaskcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00007885{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007886 static const char permuser[3] = "ugo";
7887 static const char permmode[3] = "rwx";
7888 static const short int permmask[] = {
7889 S_IRUSR, S_IWUSR, S_IXUSR,
7890 S_IRGRP, S_IWGRP, S_IXGRP,
7891 S_IROTH, S_IWOTH, S_IXOTH
7892 };
7893
Eric Andersencb57d552001-06-28 07:25:16 +00007894 char *ap;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007895 mode_t mask;
Eric Andersencb57d552001-06-28 07:25:16 +00007896 int i;
7897 int symbolic_mode = 0;
7898
Eric Andersen62483552001-07-10 06:09:16 +00007899 while (nextopt("S") != '\0') {
Eric Andersencb57d552001-06-28 07:25:16 +00007900 symbolic_mode = 1;
7901 }
7902
7903 INTOFF;
7904 mask = umask(0);
7905 umask(mask);
7906 INTON;
7907
7908 if ((ap = *argptr) == NULL) {
7909 if (symbolic_mode) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007910 char buf[18];
7911 char *p = buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007912
7913 for (i = 0; i < 3; i++) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007914 int j;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007915
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007916 *p++ = permuser[i];
7917 *p++ = '=';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007918 for (j = 0; j < 3; j++) {
7919 if ((mask & permmask[3 * i + j]) == 0) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007920 *p++ = permmode[j];
7921 }
7922 }
7923 *p++ = ',';
7924 }
7925 *--p = 0;
7926 puts(buf);
Eric Andersencb57d552001-06-28 07:25:16 +00007927 } else {
Eric Andersen62483552001-07-10 06:09:16 +00007928 printf("%.4o\n", mask);
Eric Andersencb57d552001-06-28 07:25:16 +00007929 }
7930 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007931 if (is_digit((unsigned char) *ap)) {
Eric Andersencb57d552001-06-28 07:25:16 +00007932 mask = 0;
7933 do {
7934 if (*ap >= '8' || *ap < '0')
7935 error("Illegal number: %s", argv[1]);
7936 mask = (mask << 3) + (*ap - '0');
7937 } while (*++ap != '\0');
7938 umask(mask);
7939 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007940 mask = ~mask & 0777;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007941 if (!parse_mode(ap, &mask)) {
Eric Andersencb57d552001-06-28 07:25:16 +00007942 error("Illegal mode: %s", ap);
Manuel Novoa III 16815d42001-08-10 19:36:07 +00007943 }
Eric Andersencb57d552001-06-28 07:25:16 +00007944 umask(~mask & 0777);
7945 }
7946 }
7947 return 0;
7948}
7949
7950/*
7951 * ulimit builtin
7952 *
7953 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
7954 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
7955 * ash by J.T. Conklin.
7956 *
7957 * Public domain.
7958 */
7959
7960struct limits {
7961 const char *name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007962 short cmd;
7963 short factor; /* multiply by to get rlim_{cur,max} values */
Eric Andersencb57d552001-06-28 07:25:16 +00007964};
7965
7966static const struct limits limits[] = {
7967#ifdef RLIMIT_CPU
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007968 {"time(seconds)", RLIMIT_CPU, 1},
Eric Andersencb57d552001-06-28 07:25:16 +00007969#endif
7970#ifdef RLIMIT_FSIZE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007971 {"file(blocks)", RLIMIT_FSIZE, 512},
Eric Andersencb57d552001-06-28 07:25:16 +00007972#endif
7973#ifdef RLIMIT_DATA
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007974 {"data(kbytes)", RLIMIT_DATA, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007975#endif
7976#ifdef RLIMIT_STACK
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007977 {"stack(kbytes)", RLIMIT_STACK, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007978#endif
7979#ifdef RLIMIT_CORE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007980 {"coredump(blocks)", RLIMIT_CORE, 512},
Eric Andersencb57d552001-06-28 07:25:16 +00007981#endif
7982#ifdef RLIMIT_RSS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007983 {"memory(kbytes)", RLIMIT_RSS, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007984#endif
7985#ifdef RLIMIT_MEMLOCK
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007986 {"locked memory(kbytes)", RLIMIT_MEMLOCK, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007987#endif
7988#ifdef RLIMIT_NPROC
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007989 {"process(processes)", RLIMIT_NPROC, 1},
Eric Andersencb57d552001-06-28 07:25:16 +00007990#endif
7991#ifdef RLIMIT_NOFILE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007992 {"nofiles(descriptors)", RLIMIT_NOFILE, 1},
Eric Andersencb57d552001-06-28 07:25:16 +00007993#endif
7994#ifdef RLIMIT_VMEM
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007995 {"vmemory(kbytes)", RLIMIT_VMEM, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007996#endif
7997#ifdef RLIMIT_SWAP
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007998 {"swap(kbytes)", RLIMIT_SWAP, 1024},
Eric Andersencb57d552001-06-28 07:25:16 +00007999#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008000 {NULL, 0, 0}
Eric Andersencb57d552001-06-28 07:25:16 +00008001};
8002
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008003static int ulimitcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008004{
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008005 static const char unlimited_string[] = "unlimited";
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008006 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008007 rlim_t val = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008008 enum { SOFT = 0x1, HARD = 0x2 } how = SOFT | HARD;
8009 const struct limits *l;
8010 int set, all = 0;
8011 int optc, what;
8012 struct rlimit limit;
Eric Andersencb57d552001-06-28 07:25:16 +00008013
8014 what = 'f';
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008015
8016 while ((optc = nextopt("HSa"
8017#ifdef RLIMIT_CPU
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008018 "t"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008019#endif
8020#ifdef RLIMIT_FSIZE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008021 "f"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008022#endif
8023#ifdef RLIMIT_DATA
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008024 "d"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008025#endif
8026#ifdef RLIMIT_STACK
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008027 "s"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008028#endif
8029#ifdef RLIMIT_CORE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008030 "c"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008031#endif
8032#ifdef RLIMIT_RSS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008033 "m"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008034#endif
8035#ifdef RLIMIT_MEMLOCK
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008036 "l"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008037#endif
8038#ifdef RLIMIT_NPROC
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008039 "p"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008040#endif
8041#ifdef RLIMIT_NOFILE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008042 "n"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008043#endif
8044#ifdef RLIMIT_VMEM
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008045 "v"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008046#endif
8047#ifdef RLIMIT_SWAP
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008048 "w"
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008049#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008050 )) != '\0') {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008051 if (optc == 'H') {
Eric Andersencb57d552001-06-28 07:25:16 +00008052 how = HARD;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008053 } else if (optc == 'S') {
Eric Andersencb57d552001-06-28 07:25:16 +00008054 how = SOFT;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008055 } else if (optc == 'a') {
Eric Andersencb57d552001-06-28 07:25:16 +00008056 all = 1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008057 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00008058 what = optc;
8059 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008060 }
Eric Andersencb57d552001-06-28 07:25:16 +00008061
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008062 for (l = limits; l->name; l++) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008063 if (l->name[0] == what)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008064 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008065 if (l->name[1] == 'w' && what == 'w')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008066 break;
8067 }
Eric Andersencb57d552001-06-28 07:25:16 +00008068
8069 set = *argptr ? 1 : 0;
8070 if (set) {
8071 char *p = *argptr;
8072
8073 if (all || argptr[1])
8074 error("too many arguments");
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008075 if (strcmp(p, unlimited_string) == 0)
Eric Andersencb57d552001-06-28 07:25:16 +00008076 val = RLIM_INFINITY;
8077 else {
8078 val = (rlim_t) 0;
8079
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008080 while ((c = *p++) >= '0' && c <= '9') {
8081 val = (val * 10) + (long) (c - '0');
Eric Andersencb57d552001-06-28 07:25:16 +00008082 if (val < (rlim_t) 0)
8083 break;
8084 }
8085 if (c)
8086 error("bad number");
8087 val *= l->factor;
8088 }
8089 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008090
Eric Andersencb57d552001-06-28 07:25:16 +00008091 if (all) {
8092 for (l = limits; l->name; l++) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008093 printf("%-20s ", l->name);
Eric Andersencb57d552001-06-28 07:25:16 +00008094 getrlimit(l->cmd, &limit);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008095 OUTPUT_LIMIT:
Eric Andersencb57d552001-06-28 07:25:16 +00008096 if (how & SOFT)
8097 val = limit.rlim_cur;
8098 else if (how & HARD)
8099 val = limit.rlim_max;
8100
Eric Andersencb57d552001-06-28 07:25:16 +00008101 if (val == RLIM_INFINITY)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008102 puts(unlimited_string);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008103 else {
Eric Andersencb57d552001-06-28 07:25:16 +00008104 val /= l->factor;
Eric Andersen62483552001-07-10 06:09:16 +00008105 printf("%lld\n", (long long) val);
Eric Andersencb57d552001-06-28 07:25:16 +00008106 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008107 if (!all) {
8108 break;
8109 }
Eric Andersencb57d552001-06-28 07:25:16 +00008110 }
8111 return 0;
8112 }
8113
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008114 if (!set) {
8115 goto OUTPUT_LIMIT;
Eric Andersencb57d552001-06-28 07:25:16 +00008116 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008117
8118 getrlimit(l->cmd, &limit);
8119 if (how & HARD)
8120 limit.rlim_max = val;
8121 if (how & SOFT)
8122 limit.rlim_cur = val;
8123 if (setrlimit(l->cmd, &limit) < 0)
8124 error("error setting limit (%m)");
Eric Andersencb57d552001-06-28 07:25:16 +00008125 return 0;
8126}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008127
Eric Andersencb57d552001-06-28 07:25:16 +00008128/*
8129 * prefix -- see if pfx is a prefix of string.
8130 */
8131
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008132static int prefix(char const *pfx, char const *string)
Eric Andersen62483552001-07-10 06:09:16 +00008133{
Eric Andersencb57d552001-06-28 07:25:16 +00008134 while (*pfx) {
8135 if (*pfx++ != *string++)
8136 return 0;
8137 }
8138 return 1;
8139}
8140
Eric Andersen2870d962001-07-02 17:27:21 +00008141/*
8142 * Return true if s is a string of digits, and save munber in intptr
8143 * nagative is bad
8144 */
8145
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008146static int is_number(const char *p, int *intptr)
Eric Andersen2870d962001-07-02 17:27:21 +00008147{
8148 int ret = 0;
8149
8150 do {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008151 if (!is_digit(*p))
Eric Andersen2870d962001-07-02 17:27:21 +00008152 return 0;
8153 ret *= 10;
8154 ret += digit_val(*p);
8155 p++;
8156 } while (*p != '\0');
8157
8158 *intptr = ret;
8159 return 1;
8160}
Eric Andersencb57d552001-06-28 07:25:16 +00008161
8162/*
8163 * Convert a string of digits to an integer, printing an error message on
8164 * failure.
8165 */
8166
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008167static int number(const char *s)
Eric Andersen2870d962001-07-02 17:27:21 +00008168{
8169 int i;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008170
8171 if (!is_number(s, &i))
Eric Andersencb57d552001-06-28 07:25:16 +00008172 error("Illegal number: %s", s);
Eric Andersen2870d962001-07-02 17:27:21 +00008173 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00008174}
8175
Eric Andersencb57d552001-06-28 07:25:16 +00008176/*
8177 * Produce a possibly single quoted string suitable as input to the shell.
8178 * The return string is allocated on the stack.
8179 */
8180
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008181static char *single_quote(const char *s)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008182{
Eric Andersencb57d552001-06-28 07:25:16 +00008183 char *p;
8184
8185 STARTSTACKSTR(p);
8186
8187 do {
8188 char *q = p;
8189 size_t len1, len1p, len2, len2p;
8190
8191 len1 = strcspn(s, "'");
8192 len2 = strspn(s + len1, "'");
8193
8194 len1p = len1 ? len1 + 2 : len1;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008195 len2p = len2 + ((len2 < 2) ? len2 : 2);
Eric Andersencb57d552001-06-28 07:25:16 +00008196
8197 CHECKSTRSPACE(len1p + len2p + 1, p);
8198
8199 if (len1) {
8200 *p = '\'';
Eric Andersencb57d552001-06-28 07:25:16 +00008201 q = p + 1 + len1;
8202 memcpy(p + 1, s, len1);
Eric Andersencb57d552001-06-28 07:25:16 +00008203 *q++ = '\'';
8204 s += len1;
8205 }
8206
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008207 if (len2 > 1) {
Eric Andersencb57d552001-06-28 07:25:16 +00008208 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008209 q += 1 + len2;
8210 memcpy(q + 1, s, len2);
8211 *q = '"';
Eric Andersencb57d552001-06-28 07:25:16 +00008212 s += len2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008213 } else if (len2 == 1) {
8214 *q++ = '\\';
8215 *q = '\'';
8216 s++;
Eric Andersencb57d552001-06-28 07:25:16 +00008217 }
8218
8219 STADJUST(len1p + len2p, p);
8220 } while (*s);
8221
8222 USTPUTC(0, p);
8223
8224 return grabstackstr(p);
8225}
8226
8227/*
Eric Andersencb57d552001-06-28 07:25:16 +00008228 * Routine for dealing with parsed shell commands.
8229 */
8230
8231
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008232static void sizenodelist(const struct nodelist *);
8233static struct nodelist *copynodelist(const struct nodelist *);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008234static char *nodesavestr(const char *);
Eric Andersencb57d552001-06-28 07:25:16 +00008235
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00008236#define CALCSIZE_TABLE
8237#define COPYNODE_TABLE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008238#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE)
8239/*
8240 * To collect a lot of redundant code in case statements for copynode()
8241 * and calcsize(), we implement a mini language here. Each type of node
8242 * struct has an associated instruction sequence that operates on its
8243 * members via their offsets. The instruction are pack in unsigned chars
8244 * with format IIDDDDDE where the bits are
8245 * I : part of the instruction opcode, which are
8246 * 00 : member is a pointer to another node
8247 * 40 : member is an integer
8248 * 80 : member is a pointer to a nodelist
8249 * CC : member is a pointer to a char string
8250 * D : data - the actual offset of the member to operate on in the struct
8251 * (since we assume bit 0 is set, it is not shifted)
8252 * E : flag signaling end of instruction sequence
8253 *
8254 * WARNING: In order to handle larger offsets for 64bit archs, this code
8255 * assumes that no offset can be an odd number and stores the
8256 * end-of-instructions flag in bit 0.
8257 */
8258
8259#define NODE_INTEGER 0x40
8260#define NODE_NODELIST 0x80
8261#define NODE_CHARPTR 0xC0
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008262#define NODE_NOMORE 0x01 /* Note: no offset should be odd (aligned) */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008263#define NODE_MBRMASK 0xC0
8264#define NODE_OFFSETMASK 0x3E
8265
8266static const unsigned char copynode_ops[35] = {
8267#define COPYNODE_OPS0 0
8268 offsetof(union node, nbinary.ch2),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008269 offsetof(union node, nbinary.ch1) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008270#define COPYNODE_OPS1 (COPYNODE_OPS0 + 2)
8271 offsetof(union node, ncmd.redirect),
8272 offsetof(union node, ncmd.args),
8273 offsetof(union node, ncmd.assign),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008274 offsetof(union node, ncmd.backgnd) | NODE_INTEGER | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008275#define COPYNODE_OPS2 (COPYNODE_OPS1 + 4)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008276 offsetof(union node, npipe.cmdlist) | NODE_NODELIST,
8277 offsetof(union node, npipe.backgnd) | NODE_INTEGER | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008278#define COPYNODE_OPS3 (COPYNODE_OPS2 + 2)
8279 offsetof(union node, nredir.redirect),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008280 offsetof(union node, nredir.n) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008281#define COPYNODE_OPS4 (COPYNODE_OPS3 + 2)
8282 offsetof(union node, nif.elsepart),
8283 offsetof(union node, nif.ifpart),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008284 offsetof(union node, nif.test) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008285#define COPYNODE_OPS5 (COPYNODE_OPS4 + 3)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008286 offsetof(union node, nfor.var) | NODE_CHARPTR,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008287 offsetof(union node, nfor.body),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008288 offsetof(union node, nfor.args) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008289#define COPYNODE_OPS6 (COPYNODE_OPS5 + 3)
8290 offsetof(union node, ncase.cases),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008291 offsetof(union node, ncase.expr) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008292#define COPYNODE_OPS7 (COPYNODE_OPS6 + 2)
8293 offsetof(union node, nclist.body),
8294 offsetof(union node, nclist.pattern),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008295 offsetof(union node, nclist.next) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008296#define COPYNODE_OPS8 (COPYNODE_OPS7 + 3)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008297 offsetof(union node, narg.backquote) | NODE_NODELIST,
8298 offsetof(union node, narg.text) | NODE_CHARPTR,
8299 offsetof(union node, narg.next) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008300#define COPYNODE_OPS9 (COPYNODE_OPS8 + 3)
8301 offsetof(union node, nfile.fname),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008302 offsetof(union node, nfile.fd) | NODE_INTEGER,
8303 offsetof(union node, nfile.next) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008304#define COPYNODE_OPS10 (COPYNODE_OPS9 + 3)
8305 offsetof(union node, ndup.vname),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008306 offsetof(union node, ndup.dupfd) | NODE_INTEGER,
8307 offsetof(union node, ndup.fd) | NODE_INTEGER,
8308 offsetof(union node, ndup.next) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008309#define COPYNODE_OPS11 (COPYNODE_OPS10 + 4)
8310 offsetof(union node, nhere.doc),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008311 offsetof(union node, nhere.fd) | NODE_INTEGER,
8312 offsetof(union node, nhere.next) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008313#define COPYNODE_OPS12 (COPYNODE_OPS11 + 3)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008314 offsetof(union node, nnot.com) | NODE_NOMORE,
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008315};
8316
8317#if COPYNODE_OPS12 != 34
8318#error COPYNODE_OPS12 is incorrect
8319#endif
8320
8321static const unsigned char copynode_ops_index[26] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008322 COPYNODE_OPS0, /* NSEMI */
8323 COPYNODE_OPS1, /* NCMD */
8324 COPYNODE_OPS2, /* NPIPE */
8325 COPYNODE_OPS3, /* NREDIR */
8326 COPYNODE_OPS3, /* NBACKGND */
8327 COPYNODE_OPS3, /* NSUBSHELL */
8328 COPYNODE_OPS0, /* NAND */
8329 COPYNODE_OPS0, /* NOR */
8330 COPYNODE_OPS4, /* NIF */
8331 COPYNODE_OPS0, /* NWHILE */
8332 COPYNODE_OPS0, /* NUNTIL */
8333 COPYNODE_OPS5, /* NFOR */
8334 COPYNODE_OPS6, /* NCASE */
8335 COPYNODE_OPS7, /* NCLIST */
8336 COPYNODE_OPS8, /* NDEFUN */
8337 COPYNODE_OPS8, /* NARG */
8338 COPYNODE_OPS9, /* NTO */
8339 COPYNODE_OPS9, /* NFROM */
8340 COPYNODE_OPS9, /* NFROMTO */
8341 COPYNODE_OPS9, /* NAPPEND */
8342 COPYNODE_OPS9, /* NTOOV */
8343 COPYNODE_OPS10, /* NTOFD */
8344 COPYNODE_OPS10, /* NFROMFD */
8345 COPYNODE_OPS11, /* NHERE */
8346 COPYNODE_OPS11, /* NXHERE */
8347 COPYNODE_OPS12, /* NNOT */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008348};
8349
8350#if NODE_CHARPTR != NODE_MBRMASK
8351#error NODE_CHARPTR != NODE_MBRMASK!!!
8352#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008353#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008354
8355#ifdef COPYNODE_TABLE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008356static union node *copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008357{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008358 union node *new;
8359 const unsigned char *p;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008360
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008361 if (n == NULL) {
8362 return NULL;
8363 }
8364 new = funcblock;
8365 new->type = n->type;
8366 funcblock = (char *) funcblock + (int) nodesize[n->type];
8367 p = copynode_ops + (int) copynode_ops_index[n->type];
8368 do {
8369 char *nn = ((char *) new) + ((int) (*p & NODE_OFFSETMASK));
8370 const char *no = ((const char *) n) + ((int) (*p & NODE_OFFSETMASK));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008371
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008372 if (!(*p & NODE_MBRMASK)) { /* standard node */
8373 *((union node **) nn) = copynode(*((const union node **) no));
8374 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008375 *((const char **) nn) = nodesavestr(*((const char **) no));
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008376 } else if (*p & NODE_NODELIST) { /* nodelist */
8377 *((struct nodelist **) nn)
8378 = copynodelist(*((const struct nodelist **) no));
8379 } else { /* integer */
8380 *((int *) nn) = *((int *) no);
8381 }
8382 } while (!(*p++ & NODE_NOMORE));
8383 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008384}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008385#else /* COPYNODE_TABLE */
8386static union node *copynode(const union node *n)
Eric Andersencb57d552001-06-28 07:25:16 +00008387{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008388 union node *new;
Eric Andersencb57d552001-06-28 07:25:16 +00008389
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008390 if (n == NULL)
8391 return NULL;
8392 new = funcblock;
8393 funcblock = (char *) funcblock + nodesize[n->type];
8394 switch (n->type) {
8395 case NSEMI:
8396 case NAND:
8397 case NOR:
8398 case NWHILE:
8399 case NUNTIL:
8400 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8401 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8402 break;
8403 case NCMD:
8404 new->ncmd.redirect = copynode(n->ncmd.redirect);
8405 new->ncmd.args = copynode(n->ncmd.args);
8406 new->ncmd.assign = copynode(n->ncmd.assign);
8407 new->ncmd.backgnd = n->ncmd.backgnd;
8408 break;
8409 case NPIPE:
8410 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8411 new->npipe.backgnd = n->npipe.backgnd;
8412 break;
8413 case NREDIR:
8414 case NBACKGND:
8415 case NSUBSHELL:
8416 new->nredir.redirect = copynode(n->nredir.redirect);
8417 new->nredir.n = copynode(n->nredir.n);
8418 break;
8419 case NIF:
8420 new->nif.elsepart = copynode(n->nif.elsepart);
8421 new->nif.ifpart = copynode(n->nif.ifpart);
8422 new->nif.test = copynode(n->nif.test);
8423 break;
8424 case NFOR:
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008425 new->nfor.var = nodesavestr(n->nfor.var);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008426 new->nfor.body = copynode(n->nfor.body);
8427 new->nfor.args = copynode(n->nfor.args);
8428 break;
8429 case NCASE:
8430 new->ncase.cases = copynode(n->ncase.cases);
8431 new->ncase.expr = copynode(n->ncase.expr);
8432 break;
8433 case NCLIST:
8434 new->nclist.body = copynode(n->nclist.body);
8435 new->nclist.pattern = copynode(n->nclist.pattern);
8436 new->nclist.next = copynode(n->nclist.next);
8437 break;
8438 case NDEFUN:
8439 case NARG:
8440 new->narg.backquote = copynodelist(n->narg.backquote);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008441 new->narg.text = nodesavestr(n->narg.text);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008442 new->narg.next = copynode(n->narg.next);
8443 break;
8444 case NTO:
8445 case NFROM:
8446 case NFROMTO:
8447 case NAPPEND:
8448 case NTOOV:
8449 new->nfile.fname = copynode(n->nfile.fname);
8450 new->nfile.fd = n->nfile.fd;
8451 new->nfile.next = copynode(n->nfile.next);
8452 break;
8453 case NTOFD:
8454 case NFROMFD:
8455 new->ndup.vname = copynode(n->ndup.vname);
8456 new->ndup.dupfd = n->ndup.dupfd;
8457 new->ndup.fd = n->ndup.fd;
8458 new->ndup.next = copynode(n->ndup.next);
8459 break;
8460 case NHERE:
8461 case NXHERE:
8462 new->nhere.doc = copynode(n->nhere.doc);
8463 new->nhere.fd = n->nhere.fd;
8464 new->nhere.next = copynode(n->nhere.next);
8465 break;
8466 case NNOT:
8467 new->nnot.com = copynode(n->nnot.com);
8468 break;
8469 };
8470 new->type = n->type;
8471 return new;
Eric Andersencb57d552001-06-28 07:25:16 +00008472}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008473#endif /* COPYNODE_TABLE */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008474
8475#ifdef CALCSIZE_TABLE
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008476static void calcsize(const union node *n)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008477{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008478 const unsigned char *p;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008479
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008480 if (n == NULL)
8481 return;
8482 funcblocksize += (int) nodesize[n->type];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008483
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008484 p = copynode_ops + (int) copynode_ops_index[n->type];
8485 do {
8486 const char *no = ((const char *) n) + ((int) (*p & NODE_OFFSETMASK));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008487
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008488 if (!(*p & NODE_MBRMASK)) { /* standard node */
8489 calcsize(*((const union node **) no));
8490 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
8491 funcstringsize += strlen(*((const char **) no)) + 1;
8492 } else if (*p & NODE_NODELIST) { /* nodelist */
8493 sizenodelist(*((const struct nodelist **) no));
8494 } /* else integer -- ignore */
8495 } while (!(*p++ & NODE_NOMORE));
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008496}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008497#else /* CALCSIZE_TABLE */
8498static void calcsize(const union node *n)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008499{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008500 if (n == NULL)
8501 return;
8502 funcblocksize += nodesize[n->type];
8503 switch (n->type) {
8504 case NSEMI:
8505 case NAND:
8506 case NOR:
8507 case NWHILE:
8508 case NUNTIL:
8509 calcsize(n->nbinary.ch2);
8510 calcsize(n->nbinary.ch1);
8511 break;
8512 case NCMD:
8513 calcsize(n->ncmd.redirect);
8514 calcsize(n->ncmd.args);
8515 calcsize(n->ncmd.assign);
8516 break;
8517 case NPIPE:
8518 sizenodelist(n->npipe.cmdlist);
8519 break;
8520 case NREDIR:
8521 case NBACKGND:
8522 case NSUBSHELL:
8523 calcsize(n->nredir.redirect);
8524 calcsize(n->nredir.n);
8525 break;
8526 case NIF:
8527 calcsize(n->nif.elsepart);
8528 calcsize(n->nif.ifpart);
8529 calcsize(n->nif.test);
8530 break;
8531 case NFOR:
8532 funcstringsize += strlen(n->nfor.var) + 1;
8533 calcsize(n->nfor.body);
8534 calcsize(n->nfor.args);
8535 break;
8536 case NCASE:
8537 calcsize(n->ncase.cases);
8538 calcsize(n->ncase.expr);
8539 break;
8540 case NCLIST:
8541 calcsize(n->nclist.body);
8542 calcsize(n->nclist.pattern);
8543 calcsize(n->nclist.next);
8544 break;
8545 case NDEFUN:
8546 case NARG:
8547 sizenodelist(n->narg.backquote);
8548 funcstringsize += strlen(n->narg.text) + 1;
8549 calcsize(n->narg.next);
8550 break;
8551 case NTO:
8552 case NFROM:
8553 case NFROMTO:
8554 case NAPPEND:
8555 case NTOOV:
8556 calcsize(n->nfile.fname);
8557 calcsize(n->nfile.next);
8558 break;
8559 case NTOFD:
8560 case NFROMFD:
8561 calcsize(n->ndup.vname);
8562 calcsize(n->ndup.next);
8563 break;
8564 case NHERE:
8565 case NXHERE:
8566 calcsize(n->nhere.doc);
8567 calcsize(n->nhere.next);
8568 break;
8569 case NNOT:
8570 calcsize(n->nnot.com);
8571 break;
8572 };
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008573}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008574#endif /* CALCSIZE_TABLE */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008575
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008576static void sizenodelist(const struct nodelist *lp)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00008577{
8578 while (lp) {
8579 funcblocksize += ALIGN(sizeof(struct nodelist));
8580 calcsize(lp->n);
8581 lp = lp->next;
8582 }
8583}
Eric Andersencb57d552001-06-28 07:25:16 +00008584
8585
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008586static struct nodelist *copynodelist(const struct nodelist *lp)
Eric Andersencb57d552001-06-28 07:25:16 +00008587{
8588 struct nodelist *start;
8589 struct nodelist **lpp;
8590
8591 lpp = &start;
8592 while (lp) {
8593 *lpp = funcblock;
8594 funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist));
8595 (*lpp)->n = copynode(lp->n);
8596 lp = lp->next;
8597 lpp = &(*lpp)->next;
8598 }
8599 *lpp = NULL;
8600 return start;
8601}
8602
8603
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008604static char *nodesavestr(const char *s)
Eric Andersencb57d552001-06-28 07:25:16 +00008605{
Eric Andersen62483552001-07-10 06:09:16 +00008606 const char *p = s;
8607 char *q = funcstring;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008608 char *rtn = funcstring;
Eric Andersencb57d552001-06-28 07:25:16 +00008609
8610 while ((*q++ = *p++) != '\0')
8611 continue;
8612 funcstring = q;
8613 return rtn;
Eric Andersencb57d552001-06-28 07:25:16 +00008614}
8615
Eric Andersend35c5df2002-01-09 15:37:36 +00008616#ifdef CONFIG_ASH_GETOPTS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008617static int getopts(char *, char *, char **, int *, int *);
Eric Andersencb57d552001-06-28 07:25:16 +00008618#endif
8619
Eric Andersencb57d552001-06-28 07:25:16 +00008620/*
8621 * Process the shell command line arguments.
8622 */
8623
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008624static void procargs(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008625{
8626 int i;
8627
8628 argptr = argv;
8629 if (argc > 0)
8630 argptr++;
8631 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008632 optent_val(i) = 2;
Eric Andersencb57d552001-06-28 07:25:16 +00008633 options(1);
8634 if (*argptr == NULL && minusc == NULL)
8635 sflag = 1;
8636 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8637 iflag = 1;
8638 if (mflag == 2)
8639 mflag = iflag;
8640 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008641 if (optent_val(i) == 2)
8642 optent_val(i) = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00008643 arg0 = argv[0];
8644 if (sflag == 0 && minusc == NULL) {
8645 commandname = argv[0];
8646 arg0 = *argptr++;
8647 setinputfile(arg0, 0);
8648 commandname = arg0;
8649 }
8650 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8651 if (argptr && minusc && *argptr)
Eric Andersen2870d962001-07-02 17:27:21 +00008652 arg0 = *argptr++;
Eric Andersencb57d552001-06-28 07:25:16 +00008653
8654 shellparam.p = argptr;
8655 shellparam.optind = 1;
8656 shellparam.optoff = -1;
8657 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8658 while (*argptr) {
8659 shellparam.nparam++;
8660 argptr++;
8661 }
8662 optschanged();
8663}
8664
8665
Eric Andersencb57d552001-06-28 07:25:16 +00008666
8667/*
8668 * Process shell options. The global variable argptr contains a pointer
8669 * to the argument list; we advance it past the options.
8670 */
8671
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008672static inline void minus_o(const char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00008673{
8674 int i;
8675
8676 if (name == NULL) {
8677 out1str("Current option settings\n");
8678 for (i = 0; i < NOPTS; i++)
8679 printf("%-16s%s\n", optent_name(optlist[i]),
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008680 optent_val(i) ? "on" : "off");
Eric Andersen62483552001-07-10 06:09:16 +00008681 } else {
8682 for (i = 0; i < NOPTS; i++)
8683 if (equal(name, optent_name(optlist[i]))) {
8684 setoption(optent_letter(optlist[i]), val);
8685 return;
8686 }
8687 error("Illegal option -o %s", name);
8688 }
8689}
8690
8691
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008692static void options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00008693{
8694 char *p;
8695 int val;
8696 int c;
8697
8698 if (cmdline)
8699 minusc = NULL;
8700 while ((p = *argptr) != NULL) {
8701 argptr++;
8702 if ((c = *p++) == '-') {
8703 val = 1;
Eric Andersen2870d962001-07-02 17:27:21 +00008704 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8705 if (!cmdline) {
8706 /* "-" means turn off -x and -v */
8707 if (p[0] == '\0')
8708 xflag = vflag = 0;
8709 /* "--" means reset params */
8710 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00008711 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00008712 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008713 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00008714 }
8715 } else if (c == '+') {
8716 val = 0;
8717 } else {
8718 argptr--;
8719 break;
8720 }
8721 while ((c = *p++) != '\0') {
8722 if (c == 'c' && cmdline) {
8723 char *q;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008724
8725#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */
Eric Andersencb57d552001-06-28 07:25:16 +00008726 if (*p == '\0')
8727#endif
8728 q = *argptr++;
8729 if (q == NULL || minusc != NULL)
8730 error("Bad -c option");
8731 minusc = q;
8732#ifdef NOHACK
8733 break;
8734#endif
8735 } else if (c == 'o') {
8736 minus_o(*argptr, val);
8737 if (*argptr)
8738 argptr++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008739 } else if (cmdline && (c == '-')) { // long options
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008740 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00008741 isloginsh = 1;
8742 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008743 } else {
8744 setoption(c, val);
8745 }
8746 }
8747 }
8748}
8749
Eric Andersencb57d552001-06-28 07:25:16 +00008750
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008751static void setoption(int flag, int val)
Eric Andersen2870d962001-07-02 17:27:21 +00008752{
Eric Andersencb57d552001-06-28 07:25:16 +00008753 int i;
8754
8755 for (i = 0; i < NOPTS; i++)
Eric Andersen2870d962001-07-02 17:27:21 +00008756 if (optent_letter(optlist[i]) == flag) {
8757 optent_val(i) = val;
Eric Andersencb57d552001-06-28 07:25:16 +00008758 if (val) {
8759 /* #%$ hack for ksh semantics */
8760 if (flag == 'V')
8761 Eflag = 0;
8762 else if (flag == 'E')
8763 Vflag = 0;
8764 }
8765 return;
8766 }
8767 error("Illegal option -%c", flag);
8768 /* NOTREACHED */
8769}
8770
8771
8772
Eric Andersencb57d552001-06-28 07:25:16 +00008773/*
8774 * Set the shell parameters.
8775 */
8776
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008777static void setparam(char **argv)
Eric Andersen2870d962001-07-02 17:27:21 +00008778{
Eric Andersencb57d552001-06-28 07:25:16 +00008779 char **newparam;
8780 char **ap;
8781 int nparam;
8782
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008783 for (nparam = 0; argv[nparam]; nparam++);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008784 ap = newparam = xmalloc((nparam + 1) * sizeof *ap);
Eric Andersencb57d552001-06-28 07:25:16 +00008785 while (*argv) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +00008786 *ap++ = xstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00008787 }
8788 *ap = NULL;
8789 freeparam(&shellparam);
8790 shellparam.malloc = 1;
8791 shellparam.nparam = nparam;
8792 shellparam.p = newparam;
8793 shellparam.optind = 1;
8794 shellparam.optoff = -1;
8795}
8796
8797
8798/*
8799 * Free the list of positional parameters.
8800 */
8801
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008802static void freeparam(volatile struct shparam *param)
Eric Andersen2870d962001-07-02 17:27:21 +00008803{
Eric Andersencb57d552001-06-28 07:25:16 +00008804 char **ap;
8805
8806 if (param->malloc) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008807 for (ap = param->p; *ap; ap++)
Aaron Lehmann49c024a2002-08-02 06:39:47 +00008808 free(*ap);
8809 free(param->p);
Eric Andersencb57d552001-06-28 07:25:16 +00008810 }
8811}
8812
8813
8814
8815/*
8816 * The shift builtin command.
8817 */
8818
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008819static int shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008820{
8821 int n;
8822 char **ap1, **ap2;
8823
8824 n = 1;
8825 if (argc > 1)
8826 n = number(argv[1]);
8827 if (n > shellparam.nparam)
8828 error("can't shift that many");
8829 INTOFF;
8830 shellparam.nparam -= n;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008831 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Eric Andersencb57d552001-06-28 07:25:16 +00008832 if (shellparam.malloc)
Aaron Lehmann49c024a2002-08-02 06:39:47 +00008833 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00008834 }
8835 ap2 = shellparam.p;
8836 while ((*ap2++ = *ap1++) != NULL);
8837 shellparam.optind = 1;
8838 shellparam.optoff = -1;
8839 INTON;
8840 return 0;
8841}
8842
8843
8844
8845/*
8846 * The set command builtin.
8847 */
8848
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008849static int setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008850{
8851 if (argc == 1)
8852 return showvarscmd(argc, argv);
8853 INTOFF;
8854 options(0);
8855 optschanged();
8856 if (*argptr != NULL) {
8857 setparam(argptr);
8858 }
8859 INTON;
8860 return 0;
8861}
8862
8863
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008864static void getoptsreset(const char *value)
Eric Andersencb57d552001-06-28 07:25:16 +00008865{
8866 shellparam.optind = number(value);
8867 shellparam.optoff = -1;
8868}
8869
Eric Andersenbdfd0d72001-10-24 05:00:29 +00008870#ifdef CONFIG_LOCALE_SUPPORT
Eric Andersen2870d962001-07-02 17:27:21 +00008871static void change_lc_all(const char *value)
8872{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008873 if (value != 0 && *value != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00008874 setlocale(LC_ALL, value);
8875}
8876
8877static void change_lc_ctype(const char *value)
8878{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008879 if (value != 0 && *value != 0)
Eric Andersen2870d962001-07-02 17:27:21 +00008880 setlocale(LC_CTYPE, value);
8881}
8882
8883#endif
8884
Eric Andersend35c5df2002-01-09 15:37:36 +00008885#ifdef CONFIG_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00008886/*
8887 * The getopts builtin. Shellparam.optnext points to the next argument
8888 * to be processed. Shellparam.optptr points to the next character to
8889 * be processed in the current argument. If shellparam.optnext is NULL,
8890 * then it's the first time getopts has been called.
8891 */
8892
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008893static int getoptscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008894{
8895 char **optbase;
8896
8897 if (argc < 3)
8898 error("Usage: getopts optstring var [arg]");
8899 else if (argc == 3) {
8900 optbase = shellparam.p;
8901 if (shellparam.optind > shellparam.nparam + 1) {
8902 shellparam.optind = 1;
8903 shellparam.optoff = -1;
8904 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008905 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00008906 optbase = &argv[3];
8907 if (shellparam.optind > argc - 2) {
8908 shellparam.optind = 1;
8909 shellparam.optoff = -1;
8910 }
8911 }
8912
8913 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008914 &shellparam.optoff);
Eric Andersencb57d552001-06-28 07:25:16 +00008915}
8916
8917/*
8918 * Safe version of setvar, returns 1 on success 0 on failure.
8919 */
8920
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008921static int setvarsafe(const char *name, const char *val, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008922{
8923 struct jmploc jmploc;
8924 struct jmploc *volatile savehandler = handler;
8925 int err = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008926
Eric Andersencb57d552001-06-28 07:25:16 +00008927#ifdef __GNUC__
8928 (void) &err;
8929#endif
8930
8931 if (setjmp(jmploc.loc))
8932 err = 1;
8933 else {
8934 handler = &jmploc;
8935 setvar(name, val, flags);
8936 }
8937 handler = savehandler;
8938 return err;
8939}
8940
8941static int
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008942getopts(char *optstr, char *optvar, char **optfirst, int *myoptind,
8943 int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00008944{
8945 char *p, *q;
8946 char c = '?';
8947 int done = 0;
8948 int err = 0;
8949 char s[10];
8950 char **optnext = optfirst + *myoptind - 1;
8951
8952 if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008953 strlen(*(optnext - 1)) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00008954 p = NULL;
8955 else
8956 p = *(optnext - 1) + *optoff;
8957 if (p == NULL || *p == '\0') {
8958 /* Current word is done, advance */
8959 if (optnext == NULL)
8960 return 1;
8961 p = *optnext;
8962 if (p == NULL || *p != '-' || *++p == '\0') {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008963 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00008964 *myoptind = optnext - optfirst + 1;
8965 p = NULL;
8966 done = 1;
8967 goto out;
8968 }
8969 optnext++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008970 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00008971 goto atend;
8972 }
8973
8974 c = *p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008975 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00008976 if (*q == '\0') {
8977 if (optstr[0] == ':') {
8978 s[0] = c;
8979 s[1] = '\0';
8980 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008981 } else {
Eric Andersen3102ac42001-07-06 04:26:23 +00008982 out2fmt("Illegal option -%c\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00008983 (void) unsetvar("OPTARG");
8984 }
8985 c = '?';
8986 goto bad;
8987 }
8988 if (*++q == ':')
8989 q++;
8990 }
8991
8992 if (*++q == ':') {
8993 if (*p == '\0' && (p = *optnext) == NULL) {
8994 if (optstr[0] == ':') {
8995 s[0] = c;
8996 s[1] = '\0';
8997 err |= setvarsafe("OPTARG", s, 0);
8998 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008999 } else {
Eric Andersen3102ac42001-07-06 04:26:23 +00009000 out2fmt("No arg for -%c option\n", c);
Eric Andersencb57d552001-06-28 07:25:16 +00009001 (void) unsetvar("OPTARG");
9002 c = '?';
9003 }
9004 goto bad;
9005 }
9006
9007 if (p == *optnext)
9008 optnext++;
9009 setvarsafe("OPTARG", p, 0);
9010 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009011 } else
Eric Andersencb57d552001-06-28 07:25:16 +00009012 setvarsafe("OPTARG", "", 0);
9013 *myoptind = optnext - optfirst + 1;
9014 goto out;
9015
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009016 bad:
Eric Andersencb57d552001-06-28 07:25:16 +00009017 *myoptind = 1;
9018 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009019 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009020 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersen3102ac42001-07-06 04:26:23 +00009021 snprintf(s, sizeof(s), "%d", *myoptind);
Eric Andersencb57d552001-06-28 07:25:16 +00009022 err |= setvarsafe("OPTIND", s, VNOFUNC);
9023 s[0] = c;
9024 s[1] = '\0';
9025 err |= setvarsafe(optvar, s, 0);
9026 if (err) {
9027 *myoptind = 1;
9028 *optoff = -1;
Eric Andersencb57d552001-06-28 07:25:16 +00009029 exraise(EXERROR);
9030 }
9031 return done;
9032}
Eric Andersen2870d962001-07-02 17:27:21 +00009033#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009034
9035/*
9036 * XXX - should get rid of. have all builtins use getopt(3). the
9037 * library getopt must have the BSD extension static variable "optreset"
9038 * otherwise it can't be used within the shell safely.
9039 *
9040 * Standard option processing (a la getopt) for builtin routines. The
9041 * only argument that is passed to nextopt is the option string; the
9042 * other arguments are unnecessary. It return the character, or '\0' on
9043 * end of input.
9044 */
9045
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009046static int nextopt(const char *optstring)
Eric Andersen62483552001-07-10 06:09:16 +00009047{
Eric Andersencb57d552001-06-28 07:25:16 +00009048 char *p;
9049 const char *q;
9050 char c;
9051
9052 if ((p = optptr) == NULL || *p == '\0') {
9053 p = *argptr;
9054 if (p == NULL || *p != '-' || *++p == '\0')
9055 return '\0';
9056 argptr++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009057 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009058 return '\0';
9059 }
9060 c = *p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009061 for (q = optstring; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +00009062 if (*q == '\0')
9063 error("Illegal option -%c", c);
9064 if (*++q == ':')
9065 q++;
9066 }
9067 if (*++q == ':') {
9068 if (*p == '\0' && (p = *argptr++) == NULL)
9069 error("No arg for -%c option", c);
9070 optionarg = p;
9071 p = NULL;
9072 }
9073 optptr = p;
9074 return c;
9075}
9076
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009077static void flushall()
9078{
Eric Andersencb57d552001-06-28 07:25:16 +00009079 INTOFF;
Eric Andersen3102ac42001-07-06 04:26:23 +00009080 fflush(stdout);
Eric Andersencb57d552001-06-28 07:25:16 +00009081 INTON;
Eric Andersencb57d552001-06-28 07:25:16 +00009082}
9083
9084
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009085static void out2fmt(const char *fmt, ...)
Eric Andersencb57d552001-06-28 07:25:16 +00009086{
9087 va_list ap;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009088
Eric Andersencb57d552001-06-28 07:25:16 +00009089 va_start(ap, fmt);
Eric Andersen3102ac42001-07-06 04:26:23 +00009090 vfprintf(stderr, fmt, ap);
Eric Andersencb57d552001-06-28 07:25:16 +00009091 va_end(ap);
9092}
9093
Eric Andersencb57d552001-06-28 07:25:16 +00009094/*
9095 * Version of write which resumes after a signal is caught.
9096 */
9097
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009098static int xwrite(int fd, const char *buf, int nbytes)
Eric Andersen2870d962001-07-02 17:27:21 +00009099{
Eric Andersencb57d552001-06-28 07:25:16 +00009100 int ntry;
9101 int i;
9102 int n;
9103
9104 n = nbytes;
9105 ntry = 0;
9106 for (;;) {
9107 i = write(fd, buf, n);
9108 if (i > 0) {
9109 if ((n -= i) <= 0)
9110 return nbytes;
9111 buf += i;
9112 ntry = 0;
9113 } else if (i == 0) {
9114 if (++ntry > 10)
9115 return nbytes - n;
9116 } else if (errno != EINTR) {
9117 return -1;
9118 }
9119 }
9120}
9121
9122
Eric Andersencb57d552001-06-28 07:25:16 +00009123/*
9124 * Shell command parser.
9125 */
9126
9127#define EOFMARKLEN 79
9128
9129
9130
9131struct heredoc {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009132 struct heredoc *next; /* next here document in list */
9133 union node *here; /* redirection node */
9134 char *eofmark; /* string indicating end of input */
9135 int striptabs; /* if set, strip leading tabs */
Eric Andersencb57d552001-06-28 07:25:16 +00009136};
9137
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009138static struct heredoc *heredoclist; /* list of here documents to read */
9139static int parsebackquote; /* nonzero if we are inside backquotes */
9140static int doprompt; /* if set, prompt the user */
9141static int needprompt; /* true if interactive and at start of line */
9142static int lasttoken; /* last token read */
Eric Andersencb57d552001-06-28 07:25:16 +00009143
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009144static char *wordtext; /* text of last word returned by readtoken */
Eric Andersencb57d552001-06-28 07:25:16 +00009145
Eric Andersen2870d962001-07-02 17:27:21 +00009146static struct nodelist *backquotelist;
9147static union node *redirnode;
Eric Andersen044228d2001-07-17 01:12:36 +00009148static struct heredoc *heredoc;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009149static int quoteflag; /* set if (part of) last token was quoted */
9150static int startlinno; /* line # where last token started */
Eric Andersencb57d552001-06-28 07:25:16 +00009151
9152
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009153static union node *list(int);
9154static union node *andor(void);
9155static union node *pipeline(void);
9156static union node *command(void);
Eric Andersena3483db2001-10-24 08:01:06 +00009157static union node *simplecmd(union node **rpp, union node *redir);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009158static void parsefname(void);
9159static void parseheredoc(void);
9160static char peektoken(void);
9161static int readtoken(void);
9162static int xxreadtoken(void);
9163static int readtoken1(int, int, const char *, int);
9164static int noexpand(char *);
9165static void synexpect(int) __attribute__ ((noreturn));
9166static void synerror(const char *) __attribute__ ((noreturn));
9167static void setprompt(int);
Eric Andersencb57d552001-06-28 07:25:16 +00009168
9169
9170/*
9171 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9172 * valid parse tree indicating a blank line.)
9173 */
9174
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009175static union node *parsecmd(int interact)
Eric Andersencb57d552001-06-28 07:25:16 +00009176{
9177 int t;
9178
9179 tokpushback = 0;
9180 doprompt = interact;
9181 if (doprompt)
9182 setprompt(1);
9183 else
9184 setprompt(0);
9185 needprompt = 0;
9186 t = readtoken();
9187 if (t == TEOF)
9188 return NEOF;
9189 if (t == TNL)
9190 return NULL;
9191 tokpushback++;
9192 return list(1);
9193}
9194
9195
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009196static union node *list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009197{
9198 union node *n1, *n2, *n3;
9199 int tok;
9200
9201 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009202 if (nlflag == 0 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009203 return NULL;
9204 n1 = NULL;
9205 for (;;) {
9206 n2 = andor();
9207 tok = readtoken();
9208 if (tok == TBACKGND) {
9209 if (n2->type == NCMD || n2->type == NPIPE) {
9210 n2->ncmd.backgnd = 1;
9211 } else if (n2->type == NREDIR) {
9212 n2->type = NBACKGND;
9213 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009214 n3 = (union node *) stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009215 n3->type = NBACKGND;
9216 n3->nredir.n = n2;
9217 n3->nredir.redirect = NULL;
9218 n2 = n3;
9219 }
9220 }
9221 if (n1 == NULL) {
9222 n1 = n2;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009223 } else {
9224 n3 = (union node *) stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009225 n3->type = NSEMI;
9226 n3->nbinary.ch1 = n1;
9227 n3->nbinary.ch2 = n2;
9228 n1 = n3;
9229 }
9230 switch (tok) {
9231 case TBACKGND:
9232 case TSEMI:
9233 tok = readtoken();
9234 /* fall through */
9235 case TNL:
9236 if (tok == TNL) {
9237 parseheredoc();
9238 if (nlflag)
9239 return n1;
9240 } else {
9241 tokpushback++;
9242 }
9243 checkkwd = 2;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009244 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009245 return n1;
9246 break;
9247 case TEOF:
9248 if (heredoclist)
9249 parseheredoc();
9250 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009251 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009252 return n1;
9253 default:
9254 if (nlflag)
9255 synexpect(-1);
9256 tokpushback++;
9257 return n1;
9258 }
9259 }
9260}
9261
9262
9263
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009264static union node *andor()
9265{
Eric Andersencb57d552001-06-28 07:25:16 +00009266 union node *n1, *n2, *n3;
9267 int t;
9268
9269 checkkwd = 1;
9270 n1 = pipeline();
9271 for (;;) {
9272 if ((t = readtoken()) == TAND) {
9273 t = NAND;
9274 } else if (t == TOR) {
9275 t = NOR;
9276 } else {
9277 tokpushback++;
9278 return n1;
9279 }
9280 checkkwd = 2;
9281 n2 = pipeline();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009282 n3 = (union node *) stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009283 n3->type = t;
9284 n3->nbinary.ch1 = n1;
9285 n3->nbinary.ch2 = n2;
9286 n1 = n3;
9287 }
9288}
9289
9290
9291
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009292static union node *pipeline()
9293{
Eric Andersencb57d552001-06-28 07:25:16 +00009294 union node *n1, *n2, *pipenode;
9295 struct nodelist *lp, *prev;
9296 int negate;
9297
9298 negate = 0;
9299 TRACE(("pipeline: entered\n"));
9300 if (readtoken() == TNOT) {
9301 negate = !negate;
9302 checkkwd = 1;
9303 } else
9304 tokpushback++;
9305 n1 = command();
9306 if (readtoken() == TPIPE) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009307 pipenode = (union node *) stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009308 pipenode->type = NPIPE;
9309 pipenode->npipe.backgnd = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009310 lp = (struct nodelist *) stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009311 pipenode->npipe.cmdlist = lp;
9312 lp->n = n1;
9313 do {
9314 prev = lp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009315 lp = (struct nodelist *) stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009316 checkkwd = 2;
9317 lp->n = command();
9318 prev->next = lp;
9319 } while (readtoken() == TPIPE);
9320 lp->next = NULL;
9321 n1 = pipenode;
9322 }
9323 tokpushback++;
9324 if (negate) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009325 n2 = (union node *) stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009326 n2->type = NNOT;
9327 n2->nnot.com = n1;
9328 return n2;
9329 } else
9330 return n1;
9331}
9332
9333
9334
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009335static union node *command(void)
9336{
Eric Andersencb57d552001-06-28 07:25:16 +00009337 union node *n1, *n2;
9338 union node *ap, **app;
9339 union node *cp, **cpp;
9340 union node *redir, **rpp;
9341 int t;
9342
9343 redir = NULL;
9344 n1 = NULL;
9345 rpp = &redir;
9346
Eric Andersen88cec252001-09-06 17:35:20 +00009347 /* Check for redirection which may precede command */
9348 while (readtoken() == TREDIR) {
9349 *rpp = n2 = redirnode;
9350 rpp = &n2->nfile.next;
9351 parsefname();
9352 }
9353 tokpushback++;
9354
Eric Andersencb57d552001-06-28 07:25:16 +00009355 switch (readtoken()) {
9356 case TIF:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009357 n1 = (union node *) stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009358 n1->type = NIF;
9359 n1->nif.test = list(0);
9360 if (readtoken() != TTHEN)
9361 synexpect(TTHEN);
9362 n1->nif.ifpart = list(0);
9363 n2 = n1;
9364 while (readtoken() == TELIF) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009365 n2->nif.elsepart = (union node *) stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009366 n2 = n2->nif.elsepart;
9367 n2->type = NIF;
9368 n2->nif.test = list(0);
9369 if (readtoken() != TTHEN)
9370 synexpect(TTHEN);
9371 n2->nif.ifpart = list(0);
9372 }
9373 if (lasttoken == TELSE)
9374 n2->nif.elsepart = list(0);
9375 else {
9376 n2->nif.elsepart = NULL;
9377 tokpushback++;
9378 }
9379 if (readtoken() != TFI)
9380 synexpect(TFI);
9381 checkkwd = 1;
9382 break;
9383 case TWHILE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009384 case TUNTIL:{
Eric Andersencb57d552001-06-28 07:25:16 +00009385 int got;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009386 n1 = (union node *) stalloc(sizeof(struct nbinary));
9387 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009388 n1->nbinary.ch1 = list(0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009389 if ((got = readtoken()) != TDO) {
9390 TRACE(("expecting DO got %s %s\n", tokname(got),
9391 got == TWORD ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00009392 synexpect(TDO);
9393 }
9394 n1->nbinary.ch2 = list(0);
9395 if (readtoken() != TDONE)
9396 synexpect(TDONE);
9397 checkkwd = 1;
9398 break;
9399 }
9400 case TFOR:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009401 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Eric Andersencb57d552001-06-28 07:25:16 +00009402 synerror("Bad for loop variable");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009403 n1 = (union node *) stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009404 n1->type = NFOR;
9405 n1->nfor.var = wordtext;
9406 checkkwd = 1;
9407 if (readtoken() == TIN) {
9408 app = &ap;
9409 while (readtoken() == TWORD) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009410 n2 = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009411 n2->type = NARG;
9412 n2->narg.text = wordtext;
9413 n2->narg.backquote = backquotelist;
9414 *app = n2;
9415 app = &n2->narg.next;
9416 }
9417 *app = NULL;
9418 n1->nfor.args = ap;
9419 if (lasttoken != TNL && lasttoken != TSEMI)
9420 synexpect(-1);
9421 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009422 static char argvars[5] = { CTLVAR, VSNORMAL | VSQUOTE,
9423 '@', '=', '\0'
9424 };
9425 n2 = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009426 n2->type = NARG;
9427 n2->narg.text = argvars;
9428 n2->narg.backquote = NULL;
9429 n2->narg.next = NULL;
9430 n1->nfor.args = n2;
9431 /*
9432 * Newline or semicolon here is optional (but note
9433 * that the original Bourne shell only allowed NL).
9434 */
9435 if (lasttoken != TNL && lasttoken != TSEMI)
9436 tokpushback++;
9437 }
9438 checkkwd = 2;
9439 if (readtoken() != TDO)
9440 synexpect(TDO);
9441 n1->nfor.body = list(0);
9442 if (readtoken() != TDONE)
9443 synexpect(TDONE);
9444 checkkwd = 1;
9445 break;
9446 case TCASE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009447 n1 = (union node *) stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009448 n1->type = NCASE;
9449 if (readtoken() != TWORD)
9450 synexpect(TWORD);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009451 n1->ncase.expr = n2 = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009452 n2->type = NARG;
9453 n2->narg.text = wordtext;
9454 n2->narg.backquote = backquotelist;
9455 n2->narg.next = NULL;
9456 do {
9457 checkkwd = 1;
9458 } while (readtoken() == TNL);
9459 if (lasttoken != TIN)
9460 synerror("expecting \"in\"");
9461 cpp = &n1->ncase.cases;
9462 checkkwd = 2, readtoken();
9463 do {
9464 if (lasttoken == TLP)
9465 readtoken();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009466 *cpp = cp = (union node *) stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009467 cp->type = NCLIST;
9468 app = &cp->nclist.pattern;
9469 for (;;) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009470 *app = ap = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009471 ap->type = NARG;
9472 ap->narg.text = wordtext;
9473 ap->narg.backquote = backquotelist;
9474 if (checkkwd = 2, readtoken() != TPIPE)
9475 break;
9476 app = &ap->narg.next;
9477 readtoken();
9478 }
9479 ap->narg.next = NULL;
9480 if (lasttoken != TRP)
9481 synexpect(TRP);
9482 cp->nclist.body = list(0);
9483
9484 checkkwd = 2;
9485 if ((t = readtoken()) != TESAC) {
9486 if (t != TENDCASE)
9487 synexpect(TENDCASE);
9488 else
9489 checkkwd = 2, readtoken();
9490 }
9491 cpp = &cp->nclist.next;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009492 } while (lasttoken != TESAC);
Eric Andersencb57d552001-06-28 07:25:16 +00009493 *cpp = NULL;
9494 checkkwd = 1;
9495 break;
9496 case TLP:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009497 n1 = (union node *) stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009498 n1->type = NSUBSHELL;
9499 n1->nredir.n = list(0);
9500 n1->nredir.redirect = NULL;
9501 if (readtoken() != TRP)
9502 synexpect(TRP);
9503 checkkwd = 1;
9504 break;
9505 case TBEGIN:
9506 n1 = list(0);
9507 if (readtoken() != TEND)
9508 synexpect(TEND);
9509 checkkwd = 1;
9510 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009511 /* Handle an empty command like other simple commands. */
Eric Andersencb57d552001-06-28 07:25:16 +00009512 case TSEMI:
9513 case TAND:
9514 case TOR:
9515 case TNL:
9516 case TEOF:
9517 case TRP:
9518 case TBACKGND:
9519 /*
9520 * An empty command before a ; doesn't make much sense, and
9521 * should certainly be disallowed in the case of `if ;'.
9522 */
9523 if (!redir)
9524 synexpect(-1);
9525 case TWORD:
Eric Andersencb57d552001-06-28 07:25:16 +00009526 tokpushback++;
Eric Andersena3483db2001-10-24 08:01:06 +00009527 n1 = simplecmd(rpp, redir);
Eric Andersencb57d552001-06-28 07:25:16 +00009528 return n1;
9529 default:
9530 synexpect(-1);
9531 /* NOTREACHED */
9532 }
9533
9534 /* Now check for redirection which may follow command */
9535 while (readtoken() == TREDIR) {
9536 *rpp = n2 = redirnode;
9537 rpp = &n2->nfile.next;
9538 parsefname();
9539 }
9540 tokpushback++;
9541 *rpp = NULL;
9542 if (redir) {
9543 if (n1->type != NSUBSHELL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009544 n2 = (union node *) stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009545 n2->type = NREDIR;
9546 n2->nredir.n = n1;
9547 n1 = n2;
9548 }
9549 n1->nredir.redirect = redir;
9550 }
9551
9552 return n1;
9553}
9554
9555
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009556static union node *simplecmd(union node **rpp, union node *redir)
9557{
Eric Andersencb57d552001-06-28 07:25:16 +00009558 union node *args, **app;
9559 union node *n = NULL;
9560 union node *vars, **vpp;
Eric Andersena3483db2001-10-24 08:01:06 +00009561 union node **orig_rpp;
Eric Andersencb57d552001-06-28 07:25:16 +00009562
9563 args = NULL;
9564 app = &args;
9565 vars = NULL;
9566 vpp = &vars;
Eric Andersena3483db2001-10-24 08:01:06 +00009567
9568 /* If we don't have any redirections already, then we must reset
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009569 rpp to be the address of the local redir variable. */
Eric Andersena3483db2001-10-24 08:01:06 +00009570 if (redir == 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009571 rpp = &redir;
Eric Andersena3483db2001-10-24 08:01:06 +00009572 /* We save the incoming value, because we need this for shell
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009573 functions. There can not be a redirect or an argument between
9574 the function name and the open parenthesis. */
Eric Andersena3483db2001-10-24 08:01:06 +00009575 orig_rpp = rpp;
Eric Andersencb57d552001-06-28 07:25:16 +00009576
9577 checkalias = 2;
9578 for (;;) {
9579 switch (readtoken()) {
9580 case TWORD:
9581 case TASSIGN:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009582 n = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009583 n->type = NARG;
9584 n->narg.text = wordtext;
9585 n->narg.backquote = backquotelist;
9586 if (lasttoken == TWORD) {
9587 *app = n;
9588 app = &n->narg.next;
9589 } else {
9590 *vpp = n;
9591 vpp = &n->narg.next;
9592 }
9593 break;
9594 case TREDIR:
9595 *rpp = n = redirnode;
9596 rpp = &n->nfile.next;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009597 parsefname(); /* read name of redirection file */
Eric Andersencb57d552001-06-28 07:25:16 +00009598 break;
9599 case TLP:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009600 if (args && app == &args->narg.next && !vars && rpp == orig_rpp) {
Eric Andersencb57d552001-06-28 07:25:16 +00009601 /* We have a function */
9602 if (readtoken() != TRP)
9603 synexpect(TRP);
Eric Andersencb57d552001-06-28 07:25:16 +00009604 n->type = NDEFUN;
9605 checkkwd = 2;
9606 n->narg.next = command();
9607 return n;
9608 }
9609 /* fall through */
9610 default:
9611 tokpushback++;
9612 goto out;
9613 }
9614 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009615 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009616 *app = NULL;
9617 *vpp = NULL;
9618 *rpp = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009619 n = (union node *) stalloc(sizeof(struct ncmd));
Eric Andersencb57d552001-06-28 07:25:16 +00009620 n->type = NCMD;
9621 n->ncmd.backgnd = 0;
9622 n->ncmd.args = args;
9623 n->ncmd.assign = vars;
9624 n->ncmd.redirect = redir;
9625 return n;
9626}
9627
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009628static union node *makename(void)
9629{
Eric Andersencb57d552001-06-28 07:25:16 +00009630 union node *n;
9631
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009632 n = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009633 n->type = NARG;
9634 n->narg.next = NULL;
9635 n->narg.text = wordtext;
9636 n->narg.backquote = backquotelist;
9637 return n;
9638}
9639
9640static void fixredir(union node *n, const char *text, int err)
Eric Andersen2870d962001-07-02 17:27:21 +00009641{
Eric Andersencb57d552001-06-28 07:25:16 +00009642 TRACE(("Fix redir %s %d\n", text, err));
9643 if (!err)
9644 n->ndup.vname = NULL;
9645
9646 if (is_digit(text[0]) && text[1] == '\0')
9647 n->ndup.dupfd = digit_val(text[0]);
9648 else if (text[0] == '-' && text[1] == '\0')
9649 n->ndup.dupfd = -1;
9650 else {
9651
9652 if (err)
9653 synerror("Bad fd number");
9654 else
9655 n->ndup.vname = makename();
9656 }
9657}
9658
9659
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009660static void parsefname(void)
9661{
Eric Andersencb57d552001-06-28 07:25:16 +00009662 union node *n = redirnode;
9663
9664 if (readtoken() != TWORD)
9665 synexpect(-1);
9666 if (n->type == NHERE) {
9667 struct heredoc *here = heredoc;
9668 struct heredoc *p;
9669 int i;
9670
9671 if (quoteflag == 0)
9672 n->type = NXHERE;
9673 TRACE(("Here document %d\n", n->type));
9674 if (here->striptabs) {
9675 while (*wordtext == '\t')
9676 wordtext++;
9677 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009678 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0
9679 || i > EOFMARKLEN)
Eric Andersencb57d552001-06-28 07:25:16 +00009680 synerror("Illegal eof marker for << redirection");
9681 rmescapes(wordtext);
9682 here->eofmark = wordtext;
9683 here->next = NULL;
9684 if (heredoclist == NULL)
9685 heredoclist = here;
9686 else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009687 for (p = heredoclist; p->next; p = p->next);
Eric Andersencb57d552001-06-28 07:25:16 +00009688 p->next = here;
9689 }
9690 } else if (n->type == NTOFD || n->type == NFROMFD) {
9691 fixredir(n, wordtext, 0);
9692 } else {
9693 n->nfile.fname = makename();
9694 }
9695}
9696
9697
9698/*
9699 * Input any here documents.
9700 */
9701
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009702static void parseheredoc()
9703{
Eric Andersencb57d552001-06-28 07:25:16 +00009704 struct heredoc *here;
9705 union node *n;
9706
9707 while (heredoclist) {
9708 here = heredoclist;
9709 heredoclist = here->next;
9710 if (needprompt) {
9711 setprompt(2);
9712 needprompt = 0;
9713 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009714 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
9715 here->eofmark, here->striptabs);
9716 n = (union node *) stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009717 n->narg.type = NARG;
9718 n->narg.next = NULL;
9719 n->narg.text = wordtext;
9720 n->narg.backquote = backquotelist;
9721 here->here->nhere.doc = n;
9722 }
9723}
9724
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009725static char peektoken()
9726{
Eric Andersencb57d552001-06-28 07:25:16 +00009727 int t;
9728
9729 t = readtoken();
9730 tokpushback++;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009731 return tokname_array[t][0];
Eric Andersencb57d552001-06-28 07:25:16 +00009732}
9733
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009734static int readtoken()
9735{
Eric Andersencb57d552001-06-28 07:25:16 +00009736 int t;
Eric Andersen7467c8d2001-07-12 20:26:32 +00009737
Eric Andersencb57d552001-06-28 07:25:16 +00009738 int savecheckalias = checkalias;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009739
Eric Andersen8e139872002-07-04 00:19:46 +00009740#ifdef CONFIG_ASH_ALIAS
Eric Andersen7467c8d2001-07-12 20:26:32 +00009741 int savecheckkwd = checkkwd;
Eric Andersencb57d552001-06-28 07:25:16 +00009742 struct alias *ap;
Eric Andersen2870d962001-07-02 17:27:21 +00009743#endif
9744
Eric Andersencb57d552001-06-28 07:25:16 +00009745#ifdef DEBUG
9746 int alreadyseen = tokpushback;
9747#endif
9748
Eric Andersend35c5df2002-01-09 15:37:36 +00009749#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009750 top:
Eric Andersen2870d962001-07-02 17:27:21 +00009751#endif
9752
Eric Andersencb57d552001-06-28 07:25:16 +00009753 t = xxreadtoken();
Eric Andersen2870d962001-07-02 17:27:21 +00009754
Eric Andersencb57d552001-06-28 07:25:16 +00009755 checkalias = savecheckalias;
9756
9757 if (checkkwd) {
9758 /*
9759 * eat newlines
9760 */
9761 if (checkkwd == 2) {
9762 checkkwd = 0;
9763 while (t == TNL) {
9764 parseheredoc();
9765 t = xxreadtoken();
9766 }
9767 }
9768 checkkwd = 0;
9769 /*
9770 * check for keywords
9771 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009772 if (t == TWORD && !quoteflag) {
Eric Andersencb57d552001-06-28 07:25:16 +00009773 const char *const *pp;
9774
9775 if ((pp = findkwd(wordtext))) {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009776 lasttoken = t = pp - tokname_array;
9777 TRACE(("keyword %s recognized\n", tokname(t)));
Eric Andersencb57d552001-06-28 07:25:16 +00009778 goto out;
9779 }
9780 }
9781 }
9782
Eric Andersen7467c8d2001-07-12 20:26:32 +00009783
Eric Andersencb57d552001-06-28 07:25:16 +00009784 if (t != TWORD) {
9785 if (t != TREDIR) {
9786 checkalias = 0;
9787 }
9788 } else if (checkalias == 2 && isassignment(wordtext)) {
9789 lasttoken = t = TASSIGN;
9790 } else if (checkalias) {
Eric Andersen8e139872002-07-04 00:19:46 +00009791#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009792 if (!quoteflag && (ap = *__lookupalias(wordtext)) != NULL
9793 && !(ap->flag & ALIASINUSE)) {
Eric Andersencb57d552001-06-28 07:25:16 +00009794 if (*ap->val) {
9795 pushstring(ap->val, strlen(ap->val), ap);
9796 }
9797 checkkwd = savecheckkwd;
9798 goto top;
9799 }
Eric Andersen2870d962001-07-02 17:27:21 +00009800#endif
Eric Andersen8e139872002-07-04 00:19:46 +00009801 checkalias = 0;
Eric Andersen7467c8d2001-07-12 20:26:32 +00009802 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009803 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009804#ifdef DEBUG
9805 if (!alreadyseen)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009806 TRACE(("token %s %s\n", tokname(t), t == TWORD
9807 || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00009808 else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009809 TRACE(("reread token %s %s\n", tokname(t), t == TWORD
9810 || t == TASSIGN ? wordtext : ""));
Eric Andersencb57d552001-06-28 07:25:16 +00009811#endif
9812 return (t);
9813}
9814
9815
9816/*
9817 * Read the next input token.
9818 * If the token is a word, we set backquotelist to the list of cmds in
Eric Andersen2870d962001-07-02 17:27:21 +00009819 * backquotes. We set quoteflag to true if any part of the word was
9820 * quoted.
Eric Andersencb57d552001-06-28 07:25:16 +00009821 * If the token is TREDIR, then we set redirnode to a structure containing
Eric Andersen2870d962001-07-02 17:27:21 +00009822 * the redirection.
Eric Andersencb57d552001-06-28 07:25:16 +00009823 * In all cases, the variable startlinno is set to the number of the line
Eric Andersen2870d962001-07-02 17:27:21 +00009824 * on which the token starts.
Eric Andersencb57d552001-06-28 07:25:16 +00009825 *
9826 * [Change comment: here documents and internal procedures]
9827 * [Readtoken shouldn't have any arguments. Perhaps we should make the
9828 * word parsing code into a separate routine. In this case, readtoken
9829 * doesn't need to have any internal procedures, but parseword does.
9830 * We could also make parseoperator in essence the main routine, and
9831 * have parseword (readtoken1?) handle both words and redirection.]
9832 */
9833
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009834#define NEW_xxreadtoken
9835#ifdef NEW_xxreadtoken
9836
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009837static const char xxreadtoken_chars[] = "\n()&|;"; /* singles must be first! */
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009838static const char xxreadtoken_tokens[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009839 TNL, TLP, TRP, /* only single occurrence allowed */
9840 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
9841 TEOF, /* corresponds to trailing nul */
9842 TAND, TOR, TENDCASE, /* if double occurrence */
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009843};
9844
9845#define xxreadtoken_doubles \
9846 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
9847#define xxreadtoken_singles \
9848 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
9849
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009850static int xxreadtoken()
9851{
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009852 int c;
9853
9854 if (tokpushback) {
9855 tokpushback = 0;
9856 return lasttoken;
9857 }
9858 if (needprompt) {
9859 setprompt(2);
9860 needprompt = 0;
9861 }
9862 startlinno = plinno;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009863 for (;;) { /* until token or start of word found */
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009864 c = pgetc_macro();
9865
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009866 if ((c != ' ') && (c != '\t')
Eric Andersend35c5df2002-01-09 15:37:36 +00009867#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009868 && (c != PEOA)
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009869#endif
9870 ) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009871 if (c == '#') {
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009872 while ((c = pgetc()) != '\n' && c != PEOF);
9873 pungetc();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009874 } else if (c == '\\') {
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009875 if (pgetc() != '\n') {
9876 pungetc();
9877 goto READTOKEN1;
9878 }
9879 startlinno = ++plinno;
9880 setprompt(doprompt ? 2 : 0);
9881 } else {
9882 const char *p
9883 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
9884
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009885 if (c != PEOF) {
9886 if (c == '\n') {
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009887 plinno++;
9888 needprompt = doprompt;
9889 }
9890
9891 p = strchr(xxreadtoken_chars, c);
9892 if (p == NULL) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009893 READTOKEN1:
9894 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009895 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009896
9897 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
9898 if (pgetc() == *p) { /* double occurrence? */
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009899 p += xxreadtoken_doubles + 1;
9900 } else {
9901 pungetc();
9902 }
9903 }
9904 }
9905
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009906 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009907 }
9908 }
9909 }
9910}
9911
9912
9913#else
Eric Andersen2870d962001-07-02 17:27:21 +00009914#define RETURN(token) return lasttoken = token
Eric Andersencb57d552001-06-28 07:25:16 +00009915
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009916static int xxreadtoken()
9917{
Eric Andersencb57d552001-06-28 07:25:16 +00009918 int c;
9919
9920 if (tokpushback) {
9921 tokpushback = 0;
9922 return lasttoken;
9923 }
9924 if (needprompt) {
9925 setprompt(2);
9926 needprompt = 0;
9927 }
9928 startlinno = plinno;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009929 for (;;) { /* until token or start of word found */
Eric Andersencb57d552001-06-28 07:25:16 +00009930 c = pgetc_macro();
9931 switch (c) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009932 case ' ':
9933 case '\t':
Eric Andersend35c5df2002-01-09 15:37:36 +00009934#ifdef CONFIG_ASH_ALIAS
Eric Andersencb57d552001-06-28 07:25:16 +00009935 case PEOA:
Eric Andersen3102ac42001-07-06 04:26:23 +00009936#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009937 continue;
9938 case '#':
9939 while ((c = pgetc()) != '\n' && c != PEOF);
9940 pungetc();
9941 continue;
9942 case '\\':
9943 if (pgetc() == '\n') {
9944 startlinno = ++plinno;
9945 if (doprompt)
9946 setprompt(2);
9947 else
9948 setprompt(0);
9949 continue;
9950 }
9951 pungetc();
9952 goto breakloop;
9953 case '\n':
9954 plinno++;
9955 needprompt = doprompt;
9956 RETURN(TNL);
9957 case PEOF:
9958 RETURN(TEOF);
9959 case '&':
9960 if (pgetc() == '&')
9961 RETURN(TAND);
9962 pungetc();
9963 RETURN(TBACKGND);
9964 case '|':
9965 if (pgetc() == '|')
9966 RETURN(TOR);
9967 pungetc();
9968 RETURN(TPIPE);
9969 case ';':
9970 if (pgetc() == ';')
9971 RETURN(TENDCASE);
9972 pungetc();
9973 RETURN(TSEMI);
9974 case '(':
9975 RETURN(TLP);
9976 case ')':
9977 RETURN(TRP);
9978 default:
9979 goto breakloop;
9980 }
9981 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009982 breakloop:
9983 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009984#undef RETURN
9985}
Manuel Novoa III 8d0afde2001-09-11 01:14:02 +00009986#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009987
Eric Andersencb57d552001-06-28 07:25:16 +00009988/*
9989 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
9990 * is not NULL, read a here document. In the latter case, eofmark is the
9991 * word which marks the end of the document and striptabs is true if
9992 * leading tabs should be stripped from the document. The argument firstc
9993 * is the first character of the input token or document.
9994 *
9995 * Because C does not have internal subroutines, I have simulated them
9996 * using goto's to implement the subroutine linkage. The following macros
9997 * will run code that appears at the end of readtoken1.
9998 */
9999
Eric Andersen2870d962001-07-02 17:27:21 +000010000#define CHECKEND() {goto checkend; checkend_return:;}
10001#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10002#define PARSESUB() {goto parsesub; parsesub_return:;}
10003#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10004#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10005#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010006
10007static int
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010008readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
10009{
Eric Andersencb57d552001-06-28 07:25:16 +000010010 int c = firstc;
10011 char *out;
10012 int len;
10013 char line[EOFMARKLEN + 1];
10014 struct nodelist *bqlist;
10015 int quotef;
10016 int dblquote;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010017 int varnest; /* levels of variables expansion */
10018 int arinest; /* levels of arithmetic expansion */
10019 int parenlevel; /* levels of parens in arithmetic */
10020 int dqvarnest; /* levels of variables expansion within double quotes */
Eric Andersencb57d552001-06-28 07:25:16 +000010021 int oldstyle;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010022 int prevsyntax; /* syntax before arithmetic */
10023
Eric Andersencb57d552001-06-28 07:25:16 +000010024#if __GNUC__
10025 /* Avoid longjmp clobbering */
10026 (void) &out;
10027 (void) &quotef;
10028 (void) &dblquote;
10029 (void) &varnest;
10030 (void) &arinest;
10031 (void) &parenlevel;
10032 (void) &dqvarnest;
10033 (void) &oldstyle;
10034 (void) &prevsyntax;
10035 (void) &syntax;
10036#endif
10037
10038 startlinno = plinno;
10039 dblquote = 0;
10040 if (syntax == DQSYNTAX)
10041 dblquote = 1;
10042 quotef = 0;
10043 bqlist = NULL;
10044 varnest = 0;
10045 arinest = 0;
10046 parenlevel = 0;
10047 dqvarnest = 0;
10048
10049 STARTSTACKSTR(out);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010050 loop:{ /* for each line, until end of word */
10051 CHECKEND(); /* set c to PEOF if at end of here document */
10052 for (;;) { /* until end of line or end of word */
10053 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
10054 switch (SIT(c, syntax)) {
10055 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010056 if (syntax == BASESYNTAX)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010057 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010058 USTPUTC(c, out);
10059 plinno++;
10060 if (doprompt)
10061 setprompt(2);
10062 else
10063 setprompt(0);
10064 c = pgetc();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010065 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010066 case CWORD:
10067 USTPUTC(c, out);
10068 break;
10069 case CCTL:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010070 if ((eofmark == NULL || dblquote) && dqvarnest == 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010071 USTPUTC(CTLESC, out);
10072 USTPUTC(c, out);
10073 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010074 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010075 c = pgetc2();
10076 if (c == PEOF) {
10077 USTPUTC('\\', out);
10078 pungetc();
10079 } else if (c == '\n') {
10080 if (doprompt)
10081 setprompt(2);
10082 else
10083 setprompt(0);
10084 } else {
10085 if (dblquote && c != '\\' && c != '`' && c != '$'
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010086 && (c != '"' || eofmark != NULL))
Eric Andersencb57d552001-06-28 07:25:16 +000010087 USTPUTC('\\', out);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010088 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010089 USTPUTC(CTLESC, out);
10090 else if (eofmark == NULL)
10091 USTPUTC(CTLQUOTEMARK, out);
10092 USTPUTC(c, out);
10093 quotef++;
10094 }
10095 break;
10096 case CSQUOTE:
10097 if (eofmark == NULL)
10098 USTPUTC(CTLQUOTEMARK, out);
10099 syntax = SQSYNTAX;
10100 break;
10101 case CDQUOTE:
10102 if (eofmark == NULL)
10103 USTPUTC(CTLQUOTEMARK, out);
10104 syntax = DQSYNTAX;
10105 dblquote = 1;
10106 break;
10107 case CENDQUOTE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010108 if (eofmark != NULL && arinest == 0 && varnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010109 USTPUTC(c, out);
10110 } else {
10111 if (arinest) {
10112 syntax = ARISYNTAX;
10113 dblquote = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010114 } else if (eofmark == NULL && dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010115 syntax = BASESYNTAX;
10116 dblquote = 0;
10117 }
10118 quotef++;
10119 }
10120 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010121 case CVAR: /* '$' */
10122 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010123 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010124 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010125 if (varnest > 0) {
10126 varnest--;
10127 if (dqvarnest > 0) {
10128 dqvarnest--;
10129 }
10130 USTPUTC(CTLENDVAR, out);
10131 } else {
10132 USTPUTC(c, out);
10133 }
10134 break;
Eric Andersend35c5df2002-01-09 15:37:36 +000010135#ifdef CONFIG_ASH_MATH_SUPPORT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010136 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010137 parenlevel++;
10138 USTPUTC(c, out);
10139 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010140 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010141 if (parenlevel > 0) {
10142 USTPUTC(c, out);
10143 --parenlevel;
10144 } else {
10145 if (pgetc() == ')') {
10146 if (--arinest == 0) {
10147 USTPUTC(CTLENDARI, out);
10148 syntax = prevsyntax;
10149 if (syntax == DQSYNTAX)
10150 dblquote = 1;
10151 else
10152 dblquote = 0;
10153 } else
10154 USTPUTC(')', out);
10155 } else {
10156 /*
10157 * unbalanced parens
10158 * (don't 2nd guess - no error)
10159 */
10160 pungetc();
10161 USTPUTC(')', out);
10162 }
10163 }
10164 break;
10165#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010166 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010167 PARSEBACKQOLD();
10168 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010169 case CENDFILE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010170 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010171 case CIGN:
10172 break;
10173 default:
10174 if (varnest == 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010175 goto endword; /* exit outer loop */
Eric Andersend35c5df2002-01-09 15:37:36 +000010176#ifdef CONFIG_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010177 if (c != PEOA)
10178#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010179 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010180
Eric Andersencb57d552001-06-28 07:25:16 +000010181 }
10182 c = pgetc_macro();
10183 }
10184 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010185 endword:
Eric Andersencb57d552001-06-28 07:25:16 +000010186 if (syntax == ARISYNTAX)
10187 synerror("Missing '))'");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010188 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010189 synerror("Unterminated quoted string");
10190 if (varnest != 0) {
10191 startlinno = plinno;
10192 synerror("Missing '}'");
10193 }
10194 USTPUTC('\0', out);
10195 len = out - stackblock();
10196 out = stackblock();
10197 if (eofmark == NULL) {
10198 if ((c == '>' || c == '<')
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010199 && quotef == 0 && len <= 2 && (*out == '\0' || is_digit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010200 PARSEREDIR();
10201 return lasttoken = TREDIR;
10202 } else {
10203 pungetc();
10204 }
10205 }
10206 quoteflag = quotef;
10207 backquotelist = bqlist;
10208 grabstackblock(len);
10209 wordtext = out;
10210 return lasttoken = TWORD;
10211/* end of readtoken routine */
10212
10213
10214
10215/*
10216 * Check to see whether we are at the end of the here document. When this
10217 * is called, c is set to the first character of the next input line. If
10218 * we are at the end of the here document, this routine sets the c to PEOF.
10219 */
10220
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010221 checkend:{
10222 if (eofmark) {
Eric Andersend35c5df2002-01-09 15:37:36 +000010223#ifdef CONFIG_ASH_ALIAS
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010224 if (c == PEOA) {
Eric Andersencb57d552001-06-28 07:25:16 +000010225 c = pgetc2();
10226 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010227#endif
10228 if (striptabs) {
10229 while (c == '\t') {
10230 c = pgetc2();
10231 }
10232 }
10233 if (c == *eofmark) {
10234 if (pfgets(line, sizeof line) != NULL) {
10235 const char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010236
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010237 p = line;
10238 for (q = eofmark + 1; *q && *p == *q; p++, q++);
10239 if (*p == '\n' && *q == '\0') {
10240 c = PEOF;
10241 plinno++;
10242 needprompt = doprompt;
10243 } else {
10244 pushstring(line, strlen(line), NULL);
10245 }
Eric Andersencb57d552001-06-28 07:25:16 +000010246 }
10247 }
10248 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010249 goto checkend_return;
Eric Andersencb57d552001-06-28 07:25:16 +000010250 }
Eric Andersencb57d552001-06-28 07:25:16 +000010251
10252
10253/*
10254 * Parse a redirection operator. The variable "out" points to a string
10255 * specifying the fd to be redirected. The variable "c" contains the
10256 * first character of the redirection operator.
10257 */
10258
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010259 parseredir:{
10260 char fd = *out;
10261 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010262
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010263 np = (union node *) stalloc(sizeof(struct nfile));
10264 if (c == '>') {
10265 np->nfile.fd = 1;
10266 c = pgetc();
10267 if (c == '>')
10268 np->type = NAPPEND;
10269 else if (c == '&')
10270 np->type = NTOFD;
10271 else if (c == '|')
10272 np->type = NTOOV;
10273 else {
10274 np->type = NTO;
Eric Andersencb57d552001-06-28 07:25:16 +000010275 pungetc();
10276 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010277 } else { /* c == '<' */
10278 np->nfile.fd = 0;
10279 switch (c = pgetc()) {
10280 case '<':
10281 if (sizeof(struct nfile) != sizeof(struct nhere)) {
10282 np = (union node *) stalloc(sizeof(struct nhere));
10283 np->nfile.fd = 0;
10284 }
10285 np->type = NHERE;
10286 heredoc = (struct heredoc *) stalloc(sizeof(struct heredoc));
10287 heredoc->here = np;
10288 if ((c = pgetc()) == '-') {
10289 heredoc->striptabs = 1;
10290 } else {
10291 heredoc->striptabs = 0;
10292 pungetc();
10293 }
10294 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010295
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010296 case '&':
10297 np->type = NFROMFD;
10298 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010299
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010300 case '>':
10301 np->type = NFROMTO;
10302 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010303
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010304 default:
10305 np->type = NFROM;
10306 pungetc();
10307 break;
10308 }
Eric Andersencb57d552001-06-28 07:25:16 +000010309 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010310 if (fd != '\0')
10311 np->nfile.fd = digit_val(fd);
10312 redirnode = np;
10313 goto parseredir_return;
Eric Andersencb57d552001-06-28 07:25:16 +000010314 }
Eric Andersencb57d552001-06-28 07:25:16 +000010315
10316
10317/*
10318 * Parse a substitution. At this point, we have read the dollar sign
10319 * and nothing else.
10320 */
10321
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010322 parsesub:{
10323 int subtype;
10324 int typeloc;
10325 int flags;
10326 char *p;
10327 static const char types[] = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010328
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010329 c = pgetc();
10330 if (c <= PEOA ||
10331 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10332 ) {
10333 USTPUTC('$', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010334 pungetc();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010335 } else if (c == '(') { /* $(command) or $((arith)) */
10336 if (pgetc() == '(') {
10337 PARSEARITH();
10338 } else {
10339 pungetc();
10340 PARSEBACKQNEW();
Eric Andersencb57d552001-06-28 07:25:16 +000010341 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010342 } else {
10343 USTPUTC(CTLVAR, out);
10344 typeloc = out - stackblock();
10345 USTPUTC(VSNORMAL, out);
10346 subtype = VSNORMAL;
10347 if (c == '{') {
Eric Andersencb57d552001-06-28 07:25:16 +000010348 c = pgetc();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010349 if (c == '#') {
10350 if ((c = pgetc()) == '}')
10351 c = '#';
10352 else
10353 subtype = VSLENGTH;
10354 } else
10355 subtype = 0;
10356 }
10357 if (c > PEOA && is_name(c)) {
10358 do {
10359 STPUTC(c, out);
10360 c = pgetc();
10361 } while (c > PEOA && is_in_name(c));
10362 } else if (is_digit(c)) {
10363 do {
10364 USTPUTC(c, out);
10365 c = pgetc();
10366 } while (is_digit(c));
10367 } else if (is_special(c)) {
Eric Andersencb57d552001-06-28 07:25:16 +000010368 USTPUTC(c, out);
10369 c = pgetc();
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010370 } else
10371 badsub:synerror("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010372
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010373 STPUTC('=', out);
10374 flags = 0;
10375 if (subtype == 0) {
10376 switch (c) {
10377 case ':':
10378 flags = VSNUL;
10379 c = pgetc();
10380 /*FALLTHROUGH*/ default:
10381 p = strchr(types, c);
10382 if (p == NULL)
10383 goto badsub;
10384 subtype = p - types + VSNORMAL;
10385 break;
10386 case '%':
10387 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010388 {
10389 int cc = c;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010390
10391 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010392 c = pgetc();
10393 if (c == cc)
10394 subtype++;
10395 else
10396 pungetc();
10397 break;
10398 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010399 }
10400 } else {
10401 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010402 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010403 if (dblquote || arinest)
10404 flags |= VSQUOTE;
10405 *(stackblock() + typeloc) = subtype | flags;
10406 if (subtype != VSNORMAL) {
10407 varnest++;
10408 if (dblquote) {
10409 dqvarnest++;
10410 }
Eric Andersencb57d552001-06-28 07:25:16 +000010411 }
10412 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010413 goto parsesub_return;
Eric Andersencb57d552001-06-28 07:25:16 +000010414 }
Eric Andersencb57d552001-06-28 07:25:16 +000010415
10416
10417/*
10418 * Called to parse command substitutions. Newstyle is set if the command
10419 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10420 * list of commands (passed by reference), and savelen is the number of
10421 * characters on the top of the stack which must be preserved.
10422 */
10423
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010424 parsebackq:{
10425 struct nodelist **nlpp;
10426 int savepbq;
10427 union node *n;
10428 char *volatile str;
10429 struct jmploc jmploc;
10430 struct jmploc *volatile savehandler;
10431 int savelen;
10432 int saveprompt;
10433
Eric Andersencb57d552001-06-28 07:25:16 +000010434#ifdef __GNUC__
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010435 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010436#endif
10437
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010438 savepbq = parsebackquote;
10439 if (setjmp(jmploc.loc)) {
10440 free(str);
10441 parsebackquote = 0;
10442 handler = savehandler;
10443 longjmp(handler->loc, 1);
10444 }
10445 INTOFF;
10446 str = NULL;
10447 savelen = out - stackblock();
10448 if (savelen > 0) {
10449 str = xmalloc(savelen);
10450 memcpy(str, stackblock(), savelen);
10451 }
10452 savehandler = handler;
10453 handler = &jmploc;
10454 INTON;
10455 if (oldstyle) {
10456 /* We must read until the closing backquote, giving special
10457 treatment to some slashes, and then push the string and
10458 reread it as input, interpreting it normally. */
10459 char *pout;
10460 int pc;
10461 int psavelen;
10462 char *pstr;
Eric Andersencb57d552001-06-28 07:25:16 +000010463
10464
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010465 STARTSTACKSTR(pout);
10466 for (;;) {
10467 if (needprompt) {
10468 setprompt(2);
10469 needprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010470 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010471 switch (pc = pgetc()) {
10472 case '`':
10473 goto done;
10474
10475 case '\\':
10476 if ((pc = pgetc()) == '\n') {
10477 plinno++;
10478 if (doprompt)
10479 setprompt(2);
10480 else
10481 setprompt(0);
10482 /*
10483 * If eating a newline, avoid putting
10484 * the newline into the new character
10485 * stream (via the STPUTC after the
10486 * switch).
10487 */
10488 continue;
10489 }
10490 if (pc != '\\' && pc != '`' && pc != '$'
10491 && (!dblquote || pc != '"'))
10492 STPUTC('\\', pout);
10493 if (pc > PEOA) {
10494 break;
10495 }
10496 /* fall through */
10497
10498 case PEOF:
10499#ifdef CONFIG_ASH_ALIAS
10500 case PEOA:
10501#endif
10502 startlinno = plinno;
10503 synerror("EOF in backquote substitution");
10504
10505 case '\n':
10506 plinno++;
10507 needprompt = doprompt;
10508 break;
10509
10510 default:
Eric Andersencb57d552001-06-28 07:25:16 +000010511 break;
10512 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010513 STPUTC(pc, pout);
Eric Andersencb57d552001-06-28 07:25:16 +000010514 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010515 done:
10516 STPUTC('\0', pout);
10517 psavelen = pout - stackblock();
10518 if (psavelen > 0) {
10519 pstr = grabstackstr(pout);
10520 setinputstring(pstr);
10521 }
Eric Andersen2870d962001-07-02 17:27:21 +000010522 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010523 nlpp = &bqlist;
10524 while (*nlpp)
10525 nlpp = &(*nlpp)->next;
10526 *nlpp = (struct nodelist *) stalloc(sizeof(struct nodelist));
10527 (*nlpp)->next = NULL;
10528 parsebackquote = oldstyle;
10529
10530 if (oldstyle) {
10531 saveprompt = doprompt;
10532 doprompt = 0;
Eric Andersen2870d962001-07-02 17:27:21 +000010533 }
Eric Andersencb57d552001-06-28 07:25:16 +000010534
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010535 n = list(0);
Eric Andersencb57d552001-06-28 07:25:16 +000010536
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010537 if (oldstyle)
10538 doprompt = saveprompt;
10539 else {
10540 if (readtoken() != TRP)
10541 synexpect(TRP);
10542 }
Eric Andersencb57d552001-06-28 07:25:16 +000010543
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010544 (*nlpp)->n = n;
10545 if (oldstyle) {
10546 /*
10547 * Start reading from old file again, ignoring any pushed back
10548 * tokens left from the backquote parsing
10549 */
10550 popfile();
10551 tokpushback = 0;
10552 }
10553 while (stackblocksize() <= savelen)
10554 growstackblock();
10555 STARTSTACKSTR(out);
10556 if (str) {
10557 memcpy(out, str, savelen);
10558 STADJUST(savelen, out);
10559 INTOFF;
10560 free(str);
10561 str = NULL;
10562 INTON;
10563 }
10564 parsebackquote = savepbq;
10565 handler = savehandler;
10566 if (arinest || dblquote)
10567 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10568 else
10569 USTPUTC(CTLBACKQ, out);
10570 if (oldstyle)
10571 goto parsebackq_oldreturn;
10572 else
10573 goto parsebackq_newreturn;
Eric Andersencb57d552001-06-28 07:25:16 +000010574 }
10575
Eric Andersencb57d552001-06-28 07:25:16 +000010576/*
10577 * Parse an arithmetic expansion (indicate start of one and set state)
10578 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010579 parsearith:{
Eric Andersencb57d552001-06-28 07:25:16 +000010580
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010581 if (++arinest == 1) {
10582 prevsyntax = syntax;
10583 syntax = ARISYNTAX;
10584 USTPUTC(CTLARI, out);
10585 if (dblquote)
10586 USTPUTC('"', out);
10587 else
10588 USTPUTC(' ', out);
10589 } else {
10590 /*
10591 * we collapse embedded arithmetic expansion to
10592 * parenthesis, which should be equivalent
10593 */
10594 USTPUTC('(', out);
10595 }
10596 goto parsearith_return;
Eric Andersencb57d552001-06-28 07:25:16 +000010597 }
Eric Andersencb57d552001-06-28 07:25:16 +000010598
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010599} /* end of readtoken */
Eric Andersencb57d552001-06-28 07:25:16 +000010600
10601
Eric Andersencb57d552001-06-28 07:25:16 +000010602/*
10603 * Returns true if the text contains nothing to expand (no dollar signs
10604 * or backquotes).
10605 */
10606
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010607static int noexpand(char *text)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000010608{
Eric Andersencb57d552001-06-28 07:25:16 +000010609 char *p;
10610 char c;
10611
10612 p = text;
10613 while ((c = *p++) != '\0') {
10614 if (c == CTLQUOTEMARK)
10615 continue;
10616 if (c == CTLESC)
10617 p++;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010618 else if (SIT(c, BASESYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010619 return 0;
10620 }
10621 return 1;
10622}
10623
10624
10625/*
10626 * Return true if the argument is a legal variable name (a letter or
10627 * underscore followed by zero or more letters, underscores, and digits).
10628 */
10629
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010630static int goodname(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +000010631{
10632 const char *p;
Eric Andersencb57d552001-06-28 07:25:16 +000010633
10634 p = name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010635 if (!is_name(*p))
Eric Andersencb57d552001-06-28 07:25:16 +000010636 return 0;
10637 while (*++p) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010638 if (!is_in_name(*p))
Eric Andersencb57d552001-06-28 07:25:16 +000010639 return 0;
10640 }
10641 return 1;
10642}
10643
10644
10645/*
10646 * Called when an unexpected token is read during the parse. The argument
10647 * is the token that is expected, or -1 if more than one type of token can
10648 * occur at this point.
10649 */
10650
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010651static void synexpect(int token)
Eric Andersencb57d552001-06-28 07:25:16 +000010652{
10653 char msg[64];
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010654 int l;
Eric Andersencb57d552001-06-28 07:25:16 +000010655
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010656 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10657 if (token >= 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010658 sprintf(msg + l, " (expecting %s)", tokname(token));
Eric Andersencb57d552001-06-28 07:25:16 +000010659 synerror(msg);
10660 /* NOTREACHED */
10661}
10662
10663
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010664static void synerror(const char *msg)
Eric Andersen2870d962001-07-02 17:27:21 +000010665{
Eric Andersencb57d552001-06-28 07:25:16 +000010666 if (commandname)
Eric Andersen3102ac42001-07-06 04:26:23 +000010667 out2fmt("%s: %d: ", commandname, startlinno);
10668 out2fmt("Syntax error: %s\n", msg);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010669 error((char *) NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010670 /* NOTREACHED */
10671}
10672
Eric Andersencb57d552001-06-28 07:25:16 +000010673
10674/*
10675 * called by editline -- any expansions to the prompt
10676 * should be added here.
10677 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010678static void setprompt(int whichprompt)
Eric Andersen2870d962001-07-02 17:27:21 +000010679{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010680 char *prompt;
10681
10682 switch (whichprompt) {
Eric Andersen62483552001-07-10 06:09:16 +000010683 case 1:
10684 prompt = ps1val();
10685 break;
10686 case 2:
10687 prompt = ps2val();
10688 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010689 default: /* 0 */
Eric Andersen62483552001-07-10 06:09:16 +000010690 prompt = "";
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010691 }
10692 putprompt(prompt);
Eric Andersencb57d552001-06-28 07:25:16 +000010693}
10694
Eric Andersencb57d552001-06-28 07:25:16 +000010695
Eric Andersencb57d552001-06-28 07:25:16 +000010696/*
10697 * Code for dealing with input/output redirection.
10698 */
10699
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010700#define EMPTY -2 /* marks an unused slot in redirtab */
Eric Andersencb57d552001-06-28 07:25:16 +000010701#ifndef PIPE_BUF
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010702# define PIPESIZE 4096 /* amount of buffering in a pipe */
Eric Andersencb57d552001-06-28 07:25:16 +000010703#else
10704# define PIPESIZE PIPE_BUF
10705#endif
10706
10707
Eric Andersen62483552001-07-10 06:09:16 +000010708/*
10709 * Open a file in noclobber mode.
10710 * The code was copied from bash.
10711 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010712static inline int noclobberopen(const char *fname)
Eric Andersen62483552001-07-10 06:09:16 +000010713{
10714 int r, fd;
10715 struct stat finfo, finfo2;
10716
10717 /*
10718 * If the file exists and is a regular file, return an error
10719 * immediately.
10720 */
10721 r = stat(fname, &finfo);
10722 if (r == 0 && S_ISREG(finfo.st_mode)) {
10723 errno = EEXIST;
10724 return -1;
10725 }
10726
10727 /*
10728 * If the file was not present (r != 0), make sure we open it
10729 * exclusively so that if it is created before we open it, our open
10730 * will fail. Make sure that we do not truncate an existing file.
10731 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10732 * file was not a regular file, we leave O_EXCL off.
10733 */
10734 if (r != 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010735 return open(fname, O_WRONLY | O_CREAT | O_EXCL, 0666);
10736 fd = open(fname, O_WRONLY | O_CREAT, 0666);
Eric Andersen62483552001-07-10 06:09:16 +000010737
10738 /* If the open failed, return the file descriptor right away. */
10739 if (fd < 0)
10740 return fd;
10741
10742 /*
10743 * OK, the open succeeded, but the file may have been changed from a
10744 * non-regular file to a regular file between the stat and the open.
10745 * We are assuming that the O_EXCL open handles the case where FILENAME
10746 * did not exist and is symlinked to an existing file between the stat
10747 * and open.
10748 */
10749
10750 /*
10751 * If we can open it and fstat the file descriptor, and neither check
10752 * revealed that it was a regular file, and the file has not been
10753 * replaced, return the file descriptor.
10754 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010755 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
10756 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
Eric Andersen62483552001-07-10 06:09:16 +000010757 return fd;
10758
10759 /* The file has been replaced. badness. */
10760 close(fd);
10761 errno = EEXIST;
10762 return -1;
10763}
Eric Andersencb57d552001-06-28 07:25:16 +000010764
10765/*
Eric Andersen62483552001-07-10 06:09:16 +000010766 * Handle here documents. Normally we fork off a process to write the
10767 * data to a pipe. If the document is short, we can stuff the data in
10768 * the pipe without forking.
Eric Andersencb57d552001-06-28 07:25:16 +000010769 */
10770
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010771static inline int openhere(const union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010772{
10773 int pip[2];
10774 int len = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010775
Eric Andersen62483552001-07-10 06:09:16 +000010776 if (pipe(pip) < 0)
10777 error("Pipe call failed");
10778 if (redir->type == NHERE) {
10779 len = strlen(redir->nhere.doc->narg.text);
10780 if (len <= PIPESIZE) {
10781 xwrite(pip[1], redir->nhere.doc->narg.text, len);
10782 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010783 }
Eric Andersencb57d552001-06-28 07:25:16 +000010784 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010785 if (forkshell((struct job *) NULL, (union node *) NULL, FORK_NOJOB) == 0) {
Eric Andersen62483552001-07-10 06:09:16 +000010786 close(pip[0]);
10787 signal(SIGINT, SIG_IGN);
10788 signal(SIGQUIT, SIG_IGN);
10789 signal(SIGHUP, SIG_IGN);
10790#ifdef SIGTSTP
10791 signal(SIGTSTP, SIG_IGN);
10792#endif
10793 signal(SIGPIPE, SIG_DFL);
10794 if (redir->type == NHERE)
10795 xwrite(pip[1], redir->nhere.doc->narg.text, len);
10796 else
10797 expandhere(redir->nhere.doc, pip[1]);
10798 _exit(0);
10799 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010800 out:
Eric Andersen62483552001-07-10 06:09:16 +000010801 close(pip[1]);
10802 return pip[0];
Eric Andersencb57d552001-06-28 07:25:16 +000010803}
10804
10805
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010806static inline int openredirect(const union node *redir)
Eric Andersen62483552001-07-10 06:09:16 +000010807{
Eric Andersencb57d552001-06-28 07:25:16 +000010808 char *fname;
10809 int f;
10810
10811 switch (redir->nfile.type) {
10812 case NFROM:
10813 fname = redir->nfile.expfname;
10814 if ((f = open(fname, O_RDONLY)) < 0)
10815 goto eopen;
10816 break;
10817 case NFROMTO:
10818 fname = redir->nfile.expfname;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010819 if ((f = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010820 goto ecreate;
10821 break;
10822 case NTO:
10823 /* Take care of noclobber mode. */
10824 if (Cflag) {
10825 fname = redir->nfile.expfname;
10826 if ((f = noclobberopen(fname)) < 0)
10827 goto ecreate;
10828 break;
10829 }
10830 case NTOOV:
10831 fname = redir->nfile.expfname;
10832#ifdef O_CREAT
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010833 if ((f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010834 goto ecreate;
10835#else
10836 if ((f = creat(fname, 0666)) < 0)
10837 goto ecreate;
10838#endif
10839 break;
10840 case NAPPEND:
10841 fname = redir->nfile.expfname;
10842#ifdef O_APPEND
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010843 if ((f = open(fname, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010844 goto ecreate;
10845#else
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010846 if ((f = open(fname, O_WRONLY)) < 0 && (f = creat(fname, 0666)) < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010847 goto ecreate;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010848 lseek(f, (off_t) 0, 2);
Eric Andersencb57d552001-06-28 07:25:16 +000010849#endif
10850 break;
10851 default:
10852#ifdef DEBUG
10853 abort();
10854#endif
10855 /* Fall through to eliminate warning. */
10856 case NTOFD:
10857 case NFROMFD:
10858 f = -1;
10859 break;
10860 case NHERE:
10861 case NXHERE:
10862 f = openhere(redir);
10863 break;
10864 }
10865
10866 return f;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010867 ecreate:
Eric Andersencb57d552001-06-28 07:25:16 +000010868 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010869 eopen:
Eric Andersencb57d552001-06-28 07:25:16 +000010870 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
10871}
10872
10873
Eric Andersen62483552001-07-10 06:09:16 +000010874/*
10875 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
10876 * old file descriptors are stashed away so that the redirection can be
10877 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
10878 * standard output, and the standard error if it becomes a duplicate of
10879 * stdout.
10880 */
10881
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010882static void redirect(union node *redir, int flags)
Eric Andersen62483552001-07-10 06:09:16 +000010883{
10884 union node *n;
10885 struct redirtab *sv = NULL;
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010886 int i;
Eric Andersen62483552001-07-10 06:09:16 +000010887 int fd;
10888 int newfd;
10889 int try;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010890 int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
Eric Andersen62483552001-07-10 06:09:16 +000010891
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010892 TRACE(("redirect(%s) called\n",
10893 flags & REDIR_PUSH ? "REDIR_PUSH" : "NO_REDIR_PUSH"));
Eric Andersen62483552001-07-10 06:09:16 +000010894 if (flags & REDIR_PUSH) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010895 sv = xmalloc(sizeof(struct redirtab));
10896 for (i = 0; i < 10; i++)
Eric Andersen62483552001-07-10 06:09:16 +000010897 sv->renamed[i] = EMPTY;
10898 sv->next = redirlist;
10899 redirlist = sv;
10900 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010901 for (n = redir; n; n = n->nfile.next) {
Eric Andersen62483552001-07-10 06:09:16 +000010902 fd = n->nfile.fd;
10903 try = 0;
10904 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010905 n->ndup.dupfd == fd)
10906 continue; /* redirect from/to same file descriptor */
Eric Andersen62483552001-07-10 06:09:16 +000010907
10908 INTOFF;
10909 newfd = openredirect(n);
10910 if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
Eric Andersen09da6272002-10-22 22:15:33 +000010911 i = fd;
Eric Andersen62483552001-07-10 06:09:16 +000010912 if (newfd == fd) {
10913 try++;
10914 } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
10915 switch (errno) {
10916 case EBADF:
10917 if (!try) {
10918 dupredirect(n, newfd, fd1dup);
10919 try++;
10920 break;
10921 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010922 /* FALLTHROUGH */
Eric Andersen62483552001-07-10 06:09:16 +000010923 default:
10924 if (newfd >= 0) {
10925 close(newfd);
10926 }
10927 INTON;
10928 error("%d: %m", fd);
10929 /* NOTREACHED */
10930 }
10931 }
10932 if (!try) {
10933 close(fd);
10934 if (flags & REDIR_PUSH) {
10935 sv->renamed[fd] = i;
10936 }
10937 }
10938 } else if (fd != newfd) {
10939 close(fd);
10940 }
10941 if (fd == 0)
10942 fd0_redirected++;
10943 if (!try)
10944 dupredirect(n, newfd, fd1dup);
10945 INTON;
10946 }
10947}
10948
10949
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010950static void dupredirect(const union node *redir, int f, int fd1dup)
Eric Andersen2870d962001-07-02 17:27:21 +000010951{
Eric Andersencb57d552001-06-28 07:25:16 +000010952 int fd = redir->nfile.fd;
10953
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010954 if (fd == 1)
Eric Andersen62483552001-07-10 06:09:16 +000010955 fd1dup = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010956 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010957 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
10958 if (redir->ndup.dupfd != 1 || fd1dup != 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010959 dup_as_newfd(redir->ndup.dupfd, fd);
10960 }
10961 return;
10962 }
10963
10964 if (f != fd) {
10965 dup_as_newfd(f, fd);
10966 close(f);
10967 }
10968 return;
10969}
10970
10971
Eric Andersencb57d552001-06-28 07:25:16 +000010972
Eric Andersencb57d552001-06-28 07:25:16 +000010973/*
10974 * Undo the effects of the last redirection.
10975 */
10976
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010977static void popredir(void)
Eric Andersen2870d962001-07-02 17:27:21 +000010978{
Eric Andersencb57d552001-06-28 07:25:16 +000010979 struct redirtab *rp = redirlist;
10980 int i;
10981
10982 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010983 for (i = 0; i < 10; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000010984 if (rp->renamed[i] != EMPTY) {
Eric Andersen2870d962001-07-02 17:27:21 +000010985 if (i == 0)
10986 fd0_redirected--;
Eric Andersencb57d552001-06-28 07:25:16 +000010987 close(i);
10988 if (rp->renamed[i] >= 0) {
10989 dup_as_newfd(rp->renamed[i], i);
10990 close(rp->renamed[i]);
10991 }
Eric Andersencb57d552001-06-28 07:25:16 +000010992 }
10993 }
10994 redirlist = rp->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +000010995 free(rp);
Eric Andersencb57d552001-06-28 07:25:16 +000010996 INTON;
10997}
10998
10999/*
Eric Andersencb57d552001-06-28 07:25:16 +000011000 * Discard all saved file descriptors.
11001 */
11002
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011003static void clearredir(void)
11004{
Eric Andersencb57d552001-06-28 07:25:16 +000011005 struct redirtab *rp;
11006 int i;
11007
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011008 for (rp = redirlist; rp; rp = rp->next) {
11009 for (i = 0; i < 10; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011010 if (rp->renamed[i] >= 0) {
11011 close(rp->renamed[i]);
Eric Andersencb57d552001-06-28 07:25:16 +000011012 }
11013 rp->renamed[i] = EMPTY;
11014 }
11015 }
Eric Andersencb57d552001-06-28 07:25:16 +000011016}
11017
11018
Eric Andersencb57d552001-06-28 07:25:16 +000011019/*
11020 * Copy a file descriptor to be >= to. Returns -1
11021 * if the source file descriptor is closed, EMPTY if there are no unused
11022 * file descriptors left.
11023 */
11024
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011025static int dup_as_newfd(int from, int to)
Eric Andersencb57d552001-06-28 07:25:16 +000011026{
11027 int newfd;
11028
11029 newfd = fcntl(from, F_DUPFD, to);
11030 if (newfd < 0) {
11031 if (errno == EMFILE)
11032 return EMPTY;
11033 else
Eric Andersen2870d962001-07-02 17:27:21 +000011034 error("%d: %m", from);
Eric Andersencb57d552001-06-28 07:25:16 +000011035 }
11036 return newfd;
11037}
11038
Eric Andersencb57d552001-06-28 07:25:16 +000011039#ifdef DEBUG
Eric Andersenec074692001-10-31 11:05:49 +000011040/*
11041 * Debugging stuff.
11042 */
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011043
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011044static void shtree(union node *, int, char *, FILE *);
11045static void shcmd(union node *, FILE *);
11046static void sharg(union node *, FILE *);
11047static void indent(int, char *, FILE *);
11048static void trstring(char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011049
11050
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011051#if 0
11052static void showtree(node * n)
Eric Andersencb57d552001-06-28 07:25:16 +000011053{
11054 trputs("showtree called\n");
11055 shtree(n, 1, NULL, stdout);
11056}
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011057#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011058
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011059static void shtree(union node *n, int ind, char *pfx, FILE * fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011060{
11061 struct nodelist *lp;
11062 const char *s;
11063
11064 if (n == NULL)
11065 return;
11066
11067 indent(ind, pfx, fp);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011068 switch (n->type) {
Eric Andersencb57d552001-06-28 07:25:16 +000011069 case NSEMI:
11070 s = "; ";
11071 goto binop;
11072 case NAND:
11073 s = " && ";
11074 goto binop;
11075 case NOR:
11076 s = " || ";
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011077 binop:
Eric Andersencb57d552001-06-28 07:25:16 +000011078 shtree(n->nbinary.ch1, ind, NULL, fp);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011079 /* if (ind < 0) */
11080 fputs(s, fp);
Eric Andersencb57d552001-06-28 07:25:16 +000011081 shtree(n->nbinary.ch2, ind, NULL, fp);
11082 break;
11083 case NCMD:
11084 shcmd(n, fp);
11085 if (ind >= 0)
11086 putc('\n', fp);
11087 break;
11088 case NPIPE:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011089 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +000011090 shcmd(lp->n, fp);
11091 if (lp->next)
11092 fputs(" | ", fp);
11093 }
11094 if (n->npipe.backgnd)
11095 fputs(" &", fp);
11096 if (ind >= 0)
11097 putc('\n', fp);
11098 break;
11099 default:
11100 fprintf(fp, "<node type %d>", n->type);
11101 if (ind >= 0)
11102 putc('\n', fp);
11103 break;
11104 }
11105}
11106
11107
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011108static void shcmd(union node *cmd, FILE * fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011109{
11110 union node *np;
11111 int first;
11112 const char *s;
11113 int dftfd;
11114
11115 first = 1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011116 for (np = cmd->ncmd.args; np; np = np->narg.next) {
11117 if (!first)
Eric Andersencb57d552001-06-28 07:25:16 +000011118 putchar(' ');
11119 sharg(np, fp);
11120 first = 0;
11121 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011122 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
11123 if (!first)
Eric Andersencb57d552001-06-28 07:25:16 +000011124 putchar(' ');
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011125#if 1
11126 s = "*error*";
11127 dftfd = 0;
11128 if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) {
11129 s = redir_strings[np->nfile.type - NTO];
11130 if (*s == '>') {
11131 dftfd = 1;
11132 }
11133 }
11134#else
Eric Andersencb57d552001-06-28 07:25:16 +000011135 switch (np->nfile.type) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011136 case NTO:
11137 s = ">";
11138 dftfd = 1;
11139 break;
11140 case NAPPEND:
11141 s = ">>";
11142 dftfd = 1;
11143 break;
11144 case NTOFD:
11145 s = ">&";
11146 dftfd = 1;
11147 break;
11148 case NTOOV:
11149 s = ">|";
11150 dftfd = 1;
11151 break;
11152 case NFROM:
11153 s = "<";
11154 dftfd = 0;
11155 break;
11156 case NFROMFD:
11157 s = "<&";
11158 dftfd = 0;
11159 break;
11160 case NFROMTO:
11161 s = "<>";
11162 dftfd = 0;
11163 break;
11164 default:
11165 s = "*error*";
11166 dftfd = 0;
11167 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011168 }
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011169#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011170 if (np->nfile.fd != dftfd)
11171 fprintf(fp, "%d", np->nfile.fd);
11172 fputs(s, fp);
11173 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11174 fprintf(fp, "%d", np->ndup.dupfd);
11175 } else {
11176 sharg(np->nfile.fname, fp);
11177 }
11178 first = 0;
11179 }
11180}
11181
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011182
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011183static void sharg(union node *arg, FILE * fp)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011184{
Eric Andersencb57d552001-06-28 07:25:16 +000011185 char *p;
11186 struct nodelist *bqlist;
11187 int subtype;
11188
11189 if (arg->type != NARG) {
11190 printf("<node type %d>\n", arg->type);
11191 fflush(stdout);
11192 abort();
11193 }
11194 bqlist = arg->narg.backquote;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011195 for (p = arg->narg.text; *p; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011196 switch (*p) {
11197 case CTLESC:
11198 putc(*++p, fp);
11199 break;
11200 case CTLVAR:
11201 putc('$', fp);
11202 putc('{', fp);
11203 subtype = *++p;
11204 if (subtype == VSLENGTH)
11205 putc('#', fp);
11206
11207 while (*p != '=')
11208 putc(*p++, fp);
11209
11210 if (subtype & VSNUL)
11211 putc(':', fp);
11212
11213 switch (subtype & VSTYPE) {
11214 case VSNORMAL:
11215 putc('}', fp);
11216 break;
11217 case VSMINUS:
11218 putc('-', fp);
11219 break;
11220 case VSPLUS:
11221 putc('+', fp);
11222 break;
11223 case VSQUESTION:
11224 putc('?', fp);
11225 break;
11226 case VSASSIGN:
11227 putc('=', fp);
11228 break;
11229 case VSTRIMLEFT:
11230 putc('#', fp);
11231 break;
11232 case VSTRIMLEFTMAX:
11233 putc('#', fp);
11234 putc('#', fp);
11235 break;
11236 case VSTRIMRIGHT:
11237 putc('%', fp);
11238 break;
11239 case VSTRIMRIGHTMAX:
11240 putc('%', fp);
11241 putc('%', fp);
11242 break;
11243 case VSLENGTH:
11244 break;
11245 default:
11246 printf("<subtype %d>", subtype);
11247 }
11248 break;
11249 case CTLENDVAR:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011250 putc('}', fp);
11251 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011252 case CTLBACKQ:
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011253 case CTLBACKQ | CTLQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000011254 putc('$', fp);
11255 putc('(', fp);
11256 shtree(bqlist->n, -1, NULL, fp);
11257 putc(')', fp);
11258 break;
11259 default:
11260 putc(*p, fp);
11261 break;
11262 }
11263 }
11264}
11265
11266
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011267static void indent(int amount, char *pfx, FILE * fp)
Eric Andersencb57d552001-06-28 07:25:16 +000011268{
11269 int i;
11270
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011271 for (i = 0; i < amount; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011272 if (pfx && i == amount - 1)
11273 fputs(pfx, fp);
11274 putc('\t', fp);
11275 }
11276}
Eric Andersencb57d552001-06-28 07:25:16 +000011277
Eric Andersencb57d552001-06-28 07:25:16 +000011278FILE *tracefile;
11279
11280#if DEBUG == 2
11281static int debug = 1;
11282#else
11283static int debug = 0;
11284#endif
11285
11286
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011287static void trputc(int c)
Eric Andersencb57d552001-06-28 07:25:16 +000011288{
11289 if (tracefile == NULL)
11290 return;
11291 putc(c, tracefile);
11292 if (c == '\n')
11293 fflush(tracefile);
11294}
11295
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011296static void trace(const char *fmt, ...)
Eric Andersencb57d552001-06-28 07:25:16 +000011297{
11298 va_list va;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011299
Eric Andersencb57d552001-06-28 07:25:16 +000011300 va_start(va, fmt);
Eric Andersencb57d552001-06-28 07:25:16 +000011301 if (tracefile != NULL) {
11302 (void) vfprintf(tracefile, fmt, va);
11303 if (strchr(fmt, '\n'))
11304 (void) fflush(tracefile);
11305 }
11306 va_end(va);
11307}
11308
11309
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011310static void trputs(const char *s)
Eric Andersencb57d552001-06-28 07:25:16 +000011311{
11312 if (tracefile == NULL)
11313 return;
11314 fputs(s, tracefile);
11315 if (strchr(s, '\n'))
11316 fflush(tracefile);
11317}
11318
11319
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011320static void trstring(char *s)
Eric Andersencb57d552001-06-28 07:25:16 +000011321{
11322 char *p;
11323 char c;
11324
11325 if (tracefile == NULL)
11326 return;
11327 putc('"', tracefile);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011328 for (p = s; *p; p++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011329 switch (*p) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011330 case '\n':
11331 c = 'n';
11332 goto backslash;
11333 case '\t':
11334 c = 't';
11335 goto backslash;
11336 case '\r':
11337 c = 'r';
11338 goto backslash;
11339 case '"':
11340 c = '"';
11341 goto backslash;
11342 case '\\':
11343 c = '\\';
11344 goto backslash;
11345 case CTLESC:
11346 c = 'e';
11347 goto backslash;
11348 case CTLVAR:
11349 c = 'v';
11350 goto backslash;
11351 case CTLVAR + CTLQUOTE:
11352 c = 'V';
11353 goto backslash;
11354 case CTLBACKQ:
11355 c = 'q';
11356 goto backslash;
11357 case CTLBACKQ + CTLQUOTE:
11358 c = 'Q';
11359 goto backslash;
11360 backslash:putc('\\', tracefile);
Eric Andersencb57d552001-06-28 07:25:16 +000011361 putc(c, tracefile);
11362 break;
11363 default:
11364 if (*p >= ' ' && *p <= '~')
11365 putc(*p, tracefile);
11366 else {
11367 putc('\\', tracefile);
11368 putc(*p >> 6 & 03, tracefile);
11369 putc(*p >> 3 & 07, tracefile);
11370 putc(*p & 07, tracefile);
11371 }
11372 break;
11373 }
11374 }
11375 putc('"', tracefile);
11376}
11377
11378
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011379static void trargs(char **ap)
Eric Andersencb57d552001-06-28 07:25:16 +000011380{
11381 if (tracefile == NULL)
11382 return;
11383 while (*ap) {
11384 trstring(*ap++);
11385 if (*ap)
11386 putc(' ', tracefile);
11387 else
11388 putc('\n', tracefile);
11389 }
11390 fflush(tracefile);
11391}
11392
11393
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011394static void opentrace()
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011395{
Eric Andersencb57d552001-06-28 07:25:16 +000011396 char s[100];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011397
Eric Andersencb57d552001-06-28 07:25:16 +000011398#ifdef O_APPEND
11399 int flags;
11400#endif
11401
11402 if (!debug)
11403 return;
11404#ifdef not_this_way
11405 {
11406 char *p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011407
Eric Andersencb57d552001-06-28 07:25:16 +000011408 if ((p = getenv("HOME")) == NULL) {
11409 if (geteuid() == 0)
11410 p = "/";
11411 else
11412 p = "/tmp";
11413 }
Eric Andersen2870d962001-07-02 17:27:21 +000011414 strcpy(s, p);
Eric Andersencb57d552001-06-28 07:25:16 +000011415 strcat(s, "/trace");
11416 }
11417#else
Eric Andersen2870d962001-07-02 17:27:21 +000011418 strcpy(s, "./trace");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011419#endif /* not_this_way */
Matt Kraaia5f09c62001-11-12 16:44:55 +000011420 if ((tracefile = wfopen(s, "a")) == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011421 return;
Eric Andersencb57d552001-06-28 07:25:16 +000011422#ifdef O_APPEND
11423 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11424 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11425#endif
11426 fputs("\nTracing started.\n", tracefile);
11427 fflush(tracefile);
11428}
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011429#endif /* DEBUG */
Eric Andersencb57d552001-06-28 07:25:16 +000011430
11431
11432/*
Eric Andersencb57d552001-06-28 07:25:16 +000011433 * The trap builtin.
11434 */
11435
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011436static int trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011437{
11438 char *action;
11439 char **ap;
11440 int signo;
11441
11442 if (argc <= 1) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011443 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011444 if (trap[signo] != NULL) {
11445 char *p;
Eric Andersen34506362001-08-02 05:02:46 +000011446 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011447
11448 p = single_quote(trap[signo]);
Eric Andersen34506362001-08-02 05:02:46 +000011449 sn = sys_siglist[signo];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011450 if (sn == NULL)
Eric Andersen34506362001-08-02 05:02:46 +000011451 sn = u_signal_names(0, &signo, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011452 if (sn == NULL)
Eric Andersen34506362001-08-02 05:02:46 +000011453 sn = "???";
11454 printf("trap -- %s %s\n", p, sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011455 stunalloc(p);
11456 }
11457 }
11458 return 0;
11459 }
11460 ap = argv + 1;
11461 if (argc == 2)
11462 action = NULL;
11463 else
11464 action = *ap++;
11465 while (*ap) {
11466 if ((signo = decode_signal(*ap, 0)) < 0)
11467 error("%s: bad trap", *ap);
11468 INTOFF;
11469 if (action) {
11470 if (action[0] == '-' && action[1] == '\0')
11471 action = NULL;
11472 else
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011473 action = xstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011474 }
Aaron Lehmann49c024a2002-08-02 06:39:47 +000011475 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011476 trap[signo] = action;
11477 if (signo != 0)
11478 setsignal(signo);
11479 INTON;
11480 ap++;
11481 }
11482 return 0;
11483}
11484
11485
Eric Andersencb57d552001-06-28 07:25:16 +000011486/*
11487 * Set the signal handler for the specified signal. The routine figures
11488 * out what it should be set to.
11489 */
11490
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011491static void setsignal(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011492{
11493 int action;
11494 char *t;
11495 struct sigaction act;
11496
11497 if ((t = trap[signo]) == NULL)
11498 action = S_DFL;
11499 else if (*t != '\0')
11500 action = S_CATCH;
11501 else
11502 action = S_IGN;
11503 if (rootshell && action == S_DFL) {
11504 switch (signo) {
11505 case SIGINT:
11506 if (iflag || minusc || sflag == 0)
11507 action = S_CATCH;
11508 break;
11509 case SIGQUIT:
11510#ifdef DEBUG
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011511 {
Eric Andersencb57d552001-06-28 07:25:16 +000011512
11513 if (debug)
11514 break;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011515 }
Eric Andersencb57d552001-06-28 07:25:16 +000011516#endif
11517 /* FALLTHROUGH */
11518 case SIGTERM:
11519 if (iflag)
11520 action = S_IGN;
11521 break;
Eric Andersend35c5df2002-01-09 15:37:36 +000011522#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000011523 case SIGTSTP:
11524 case SIGTTOU:
11525 if (mflag)
11526 action = S_IGN;
11527 break;
11528#endif
11529 }
11530 }
11531
11532 t = &sigmode[signo - 1];
11533 if (*t == 0) {
11534 /*
11535 * current setting unknown
11536 */
11537 if (sigaction(signo, 0, &act) == -1) {
11538 /*
11539 * Pretend it worked; maybe we should give a warning
11540 * here, but other shells don't. We don't alter
11541 * sigmode, so that we retry every time.
11542 */
11543 return;
11544 }
11545 if (act.sa_handler == SIG_IGN) {
11546 if (mflag && (signo == SIGTSTP ||
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011547 signo == SIGTTIN || signo == SIGTTOU)) {
11548 *t = S_IGN; /* don't hard ignore these */
Eric Andersencb57d552001-06-28 07:25:16 +000011549 } else
11550 *t = S_HARD_IGN;
11551 } else {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011552 *t = S_RESET; /* force to be set */
Eric Andersencb57d552001-06-28 07:25:16 +000011553 }
11554 }
11555 if (*t == S_HARD_IGN || *t == action)
11556 return;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011557 act.sa_handler = ((action == S_CATCH) ? onsig
11558 : ((action == S_IGN) ? SIG_IGN : SIG_DFL));
Eric Andersencb57d552001-06-28 07:25:16 +000011559 *t = action;
11560 act.sa_flags = 0;
11561 sigemptyset(&act.sa_mask);
11562 sigaction(signo, &act, 0);
11563}
11564
11565/*
11566 * Ignore a signal.
11567 */
11568
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011569static void ignoresig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011570{
11571 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11572 signal(signo, SIG_IGN);
11573 }
11574 sigmode[signo - 1] = S_HARD_IGN;
11575}
11576
11577
Eric Andersencb57d552001-06-28 07:25:16 +000011578/*
11579 * Signal handler.
11580 */
11581
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011582static void onsig(int signo)
Eric Andersencb57d552001-06-28 07:25:16 +000011583{
11584 if (signo == SIGINT && trap[SIGINT] == NULL) {
11585 onint();
11586 return;
11587 }
11588 gotsig[signo - 1] = 1;
11589 pendingsigs++;
11590}
11591
11592
Eric Andersencb57d552001-06-28 07:25:16 +000011593/*
11594 * Called to execute a trap. Perhaps we should avoid entering new trap
11595 * handlers while we are executing a trap handler.
11596 */
11597
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011598static void dotrap(void)
Eric Andersen2870d962001-07-02 17:27:21 +000011599{
Eric Andersencb57d552001-06-28 07:25:16 +000011600 int i;
11601 int savestatus;
11602
11603 for (;;) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011604 for (i = 1;; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011605 if (gotsig[i - 1])
11606 break;
11607 if (i >= NSIG - 1)
11608 goto done;
11609 }
11610 gotsig[i - 1] = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011611 savestatus = exitstatus;
Eric Andersencb57d552001-06-28 07:25:16 +000011612 evalstring(trap[i], 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011613 exitstatus = savestatus;
Eric Andersencb57d552001-06-28 07:25:16 +000011614 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011615 done:
Eric Andersencb57d552001-06-28 07:25:16 +000011616 pendingsigs = 0;
11617}
11618
Eric Andersencb57d552001-06-28 07:25:16 +000011619/*
11620 * Called to exit the shell.
11621 */
11622
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011623static void exitshell(int status)
Eric Andersencb57d552001-06-28 07:25:16 +000011624{
11625 struct jmploc loc1, loc2;
11626 char *p;
11627
11628 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
11629 if (setjmp(loc1.loc)) {
11630 goto l1;
11631 }
11632 if (setjmp(loc2.loc)) {
11633 goto l2;
11634 }
11635 handler = &loc1;
11636 if ((p = trap[0]) != NULL && *p != '\0') {
11637 trap[0] = NULL;
11638 evalstring(p, 0);
11639 }
Glenn L McGrathfdbbb042002-12-09 11:10:40 +000011640l1:
11641 handler = &loc2; /* probably unnecessary */
Eric Andersencb57d552001-06-28 07:25:16 +000011642 flushall();
Eric Andersend35c5df2002-01-09 15:37:36 +000011643#ifdef CONFIG_ASH_JOB_CONTROL
Eric Andersencb57d552001-06-28 07:25:16 +000011644 setjobctl(0);
11645#endif
Glenn L McGrathfdbbb042002-12-09 11:10:40 +000011646#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
11647 if (iflag && rootshell) {
11648 const char *hp = lookupvar("HISTFILE");
11649
11650 if(hp != NULL )
11651 save_history ( hp );
11652 }
11653#endif
11654l2:
11655 _exit(status);
Eric Andersencb57d552001-06-28 07:25:16 +000011656 /* NOTREACHED */
11657}
11658
11659static int decode_signal(const char *string, int minsig)
11660{
11661 int signo;
Eric Andersen34506362001-08-02 05:02:46 +000011662 const char *name = u_signal_names(string, &signo, minsig);
Eric Andersencb57d552001-06-28 07:25:16 +000011663
Eric Andersen34506362001-08-02 05:02:46 +000011664 return name ? signo : -1;
Eric Andersencb57d552001-06-28 07:25:16 +000011665}
Eric Andersen34506362001-08-02 05:02:46 +000011666
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011667static struct var **hashvar(const char *);
11668static void showvars(const char *, int, int);
11669static struct var **findvar(struct var **, const char *);
Eric Andersencb57d552001-06-28 07:25:16 +000011670
11671/*
11672 * Initialize the varable symbol tables and import the environment
11673 */
11674
Eric Andersencb57d552001-06-28 07:25:16 +000011675/*
11676 * This routine initializes the builtin variables. It is called when the
11677 * shell is initialized and again when a shell procedure is spawned.
11678 */
11679
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011680static void initvar()
11681{
Eric Andersencb57d552001-06-28 07:25:16 +000011682 const struct varinit *ip;
11683 struct var *vp;
11684 struct var **vpp;
11685
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011686 for (ip = varinit; (vp = ip->var) != NULL; ip++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011687 if ((vp->flags & VEXPORT) == 0) {
11688 vpp = hashvar(ip->text);
11689 vp->next = *vpp;
11690 *vpp = vp;
Matt Kraaic8227632001-11-12 16:57:27 +000011691 vp->text = xstrdup(ip->text);
Eric Andersencb57d552001-06-28 07:25:16 +000011692 vp->flags = ip->flags;
11693 vp->func = ip->func;
11694 }
11695 }
Tim Riker497a8852002-04-13 05:37:10 +000011696#if !defined(CONFIG_FEATURE_COMMAND_EDITING) || !defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
Eric Andersencb57d552001-06-28 07:25:16 +000011697 /*
11698 * PS1 depends on uid
11699 */
11700 if ((vps1.flags & VEXPORT) == 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +000011701 vpp = hashvar("PS1=$ ");
Eric Andersencb57d552001-06-28 07:25:16 +000011702 vps1.next = *vpp;
11703 *vpp = &vps1;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011704 vps1.text = xstrdup(geteuid()? "PS1=$ " : "PS1=# ");
11705 vps1.flags = VSTRFIXED | VTEXTFIXED;
Eric Andersencb57d552001-06-28 07:25:16 +000011706 }
Tim Riker497a8852002-04-13 05:37:10 +000011707#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011708}
11709
11710/*
11711 * Set the value of a variable. The flags argument is ored with the
11712 * flags of the variable. If val is NULL, the variable is unset.
11713 */
11714
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011715static void setvar(const char *name, const char *val, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +000011716{
11717 const char *p;
11718 int len;
11719 int namelen;
11720 char *nameeq;
11721 int isbad;
11722 int vallen = 0;
11723
11724 isbad = 0;
11725 p = name;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011726 if (!is_name(*p))
Eric Andersencb57d552001-06-28 07:25:16 +000011727 isbad = 1;
11728 p++;
11729 for (;;) {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011730 if (!is_in_name(*p)) {
Eric Andersencb57d552001-06-28 07:25:16 +000011731 if (*p == '\0' || *p == '=')
11732 break;
11733 isbad = 1;
11734 }
11735 p++;
11736 }
11737 namelen = p - name;
11738 if (isbad)
11739 error("%.*s: bad variable name", namelen, name);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011740 len = namelen + 2; /* 2 is space for '=' and '\0' */
Eric Andersencb57d552001-06-28 07:25:16 +000011741 if (val == NULL) {
11742 flags |= VUNSET;
11743 } else {
11744 len += vallen = strlen(val);
11745 }
11746 INTOFF;
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011747 nameeq = xmalloc(len);
Eric Andersencb57d552001-06-28 07:25:16 +000011748 memcpy(nameeq, name, namelen);
11749 nameeq[namelen] = '=';
11750 if (val) {
11751 memcpy(nameeq + namelen + 1, val, vallen + 1);
11752 } else {
11753 nameeq[namelen + 1] = '\0';
11754 }
11755 setvareq(nameeq, flags);
11756 INTON;
11757}
11758
11759
11760
11761/*
11762 * Same as setvar except that the variable and value are passed in
11763 * the first argument as name=value. Since the first argument will
11764 * be actually stored in the table, it should not be a string that
11765 * will go away.
11766 */
11767
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011768static void setvareq(char *s, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +000011769{
11770 struct var *vp, **vpp;
11771
11772 vpp = hashvar(s);
11773 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
11774 if ((vp = *findvar(vpp, s))) {
11775 if (vp->flags & VREADONLY) {
11776 size_t len = strchr(s, '=') - s;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011777
Eric Andersencb57d552001-06-28 07:25:16 +000011778 error("%.*s: is read only", len, s);
11779 }
11780 INTOFF;
11781
11782 if (vp->func && (flags & VNOFUNC) == 0)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011783 (*vp->func) (strchr(s, '=') + 1);
Eric Andersencb57d552001-06-28 07:25:16 +000011784
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011785 if ((vp->flags & (VTEXTFIXED | VSTACK)) == 0)
Aaron Lehmann49c024a2002-08-02 06:39:47 +000011786 free(vp->text);
Eric Andersencb57d552001-06-28 07:25:16 +000011787
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011788 vp->flags &= ~(VTEXTFIXED | VSTACK | VUNSET);
Eric Andersencb57d552001-06-28 07:25:16 +000011789 vp->flags |= flags;
11790 vp->text = s;
11791
Eric Andersend35c5df2002-01-09 15:37:36 +000011792#ifdef CONFIG_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011793 /*
11794 * We could roll this to a function, to handle it as
11795 * a regular variable function callback, but why bother?
11796 */
11797 if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset())))
11798 chkmail(1);
Eric Andersenec074692001-10-31 11:05:49 +000011799#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011800 INTON;
11801 return;
11802 }
11803 /* not found */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011804 vp = xmalloc(sizeof(*vp));
Eric Andersencb57d552001-06-28 07:25:16 +000011805 vp->flags = flags;
11806 vp->text = s;
11807 vp->next = *vpp;
11808 vp->func = NULL;
11809 *vpp = vp;
11810}
11811
11812
11813
11814/*
11815 * Process a linked list of variable assignments.
11816 */
11817
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011818static void listsetvar(struct strlist *mylist)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011819{
Eric Andersencb57d552001-06-28 07:25:16 +000011820 struct strlist *lp;
11821
11822 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011823 for (lp = mylist; lp; lp = lp->next) {
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011824 setvareq(xstrdup(lp->text), 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011825 }
11826 INTON;
11827}
11828
11829
11830
11831/*
11832 * Find the value of a variable. Returns NULL if not set.
11833 */
11834
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011835static const char *lookupvar(const char *name)
Eric Andersenec074692001-10-31 11:05:49 +000011836{
Eric Andersencb57d552001-06-28 07:25:16 +000011837 struct var *v;
11838
11839 if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
11840 return strchr(v->text, '=') + 1;
11841 }
11842 return NULL;
11843}
11844
11845
11846
11847/*
11848 * Search the environment of a builtin command.
11849 */
11850
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011851static const char *bltinlookup(const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +000011852{
Eric Andersen62483552001-07-10 06:09:16 +000011853 const struct strlist *sp;
Eric Andersencb57d552001-06-28 07:25:16 +000011854
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011855 for (sp = cmdenviron; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +000011856 if (varequal(sp->text, name))
11857 return strchr(sp->text, '=') + 1;
11858 }
11859 return lookupvar(name);
11860}
11861
11862
11863
11864/*
11865 * Generate a list of exported variables. This routine is used to construct
11866 * the third argument to execve when executing a program.
11867 */
11868
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011869static char **environment()
11870{
Eric Andersencb57d552001-06-28 07:25:16 +000011871 int nenv;
11872 struct var **vpp;
11873 struct var *vp;
11874 char **env;
11875 char **ep;
11876
11877 nenv = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011878 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
11879 for (vp = *vpp; vp; vp = vp->next)
Eric Andersencb57d552001-06-28 07:25:16 +000011880 if (vp->flags & VEXPORT)
11881 nenv++;
11882 }
11883 ep = env = stalloc((nenv + 1) * sizeof *env);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011884 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
11885 for (vp = *vpp; vp; vp = vp->next)
Eric Andersencb57d552001-06-28 07:25:16 +000011886 if (vp->flags & VEXPORT)
11887 *ep++ = vp->text;
11888 }
11889 *ep = NULL;
11890 return env;
11891}
11892
11893
11894/*
Eric Andersencb57d552001-06-28 07:25:16 +000011895 * Command to list all variables which are set. Currently this command
11896 * is invoked from the set command when the set command is called without
11897 * any variables.
11898 */
11899
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011900static int showvarscmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011901{
11902 showvars(nullstr, VUNSET, VUNSET);
11903 return 0;
11904}
11905
11906
11907
11908/*
11909 * The export and readonly commands.
11910 */
11911
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011912static int exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011913{
11914 struct var *vp;
11915 char *name;
11916 const char *p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011917 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011918 int pflag;
11919
11920 listsetvar(cmdenviron);
11921 pflag = (nextopt("p") == 'p');
11922 if (argc > 1 && !pflag) {
11923 while ((name = *argptr++) != NULL) {
11924 if ((p = strchr(name, '=')) != NULL) {
11925 p++;
11926 } else {
11927 if ((vp = *findvar(hashvar(name), name))) {
11928 vp->flags |= flag;
11929 goto found;
11930 }
11931 }
11932 setvar(name, p, flag);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011933 found:;
Eric Andersencb57d552001-06-28 07:25:16 +000011934 }
11935 } else {
11936 showvars(argv[0], flag, 0);
11937 }
11938 return 0;
11939}
11940
Eric Andersen34506362001-08-02 05:02:46 +000011941
Eric Andersencb57d552001-06-28 07:25:16 +000011942/*
11943 * The "local" command.
11944 */
11945
Eric Andersen2870d962001-07-02 17:27:21 +000011946/* funcnest nonzero if we are currently evaluating a function */
11947
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011948static int localcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011949{
11950 char *name;
11951
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011952 if (!funcnest)
Eric Andersencb57d552001-06-28 07:25:16 +000011953 error("Not in a function");
11954 while ((name = *argptr++) != NULL) {
11955 mklocal(name);
11956 }
11957 return 0;
11958}
11959
11960
11961/*
11962 * Make a variable a local variable. When a variable is made local, it's
11963 * value and flags are saved in a localvar structure. The saved values
11964 * will be restored when the shell function returns. We handle the name
11965 * "-" as a special case.
11966 */
11967
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011968static void mklocal(char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011969{
Eric Andersencb57d552001-06-28 07:25:16 +000011970 struct localvar *lvp;
11971 struct var **vpp;
11972 struct var *vp;
11973
11974 INTOFF;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011975 lvp = xmalloc(sizeof(struct localvar));
Eric Andersencb57d552001-06-28 07:25:16 +000011976 if (name[0] == '-' && name[1] == '\0') {
11977 char *p;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011978
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011979 p = xmalloc(sizeof optet_vals);
Eric Andersen2870d962001-07-02 17:27:21 +000011980 lvp->text = memcpy(p, optet_vals, sizeof optet_vals);
Eric Andersencb57d552001-06-28 07:25:16 +000011981 vp = NULL;
11982 } else {
11983 vpp = hashvar(name);
11984 vp = *findvar(vpp, name);
11985 if (vp == NULL) {
11986 if (strchr(name, '='))
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011987 setvareq(xstrdup(name), VSTRFIXED);
Eric Andersencb57d552001-06-28 07:25:16 +000011988 else
11989 setvar(name, NULL, VSTRFIXED);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011990 vp = *vpp; /* the new variable */
Eric Andersencb57d552001-06-28 07:25:16 +000011991 lvp->text = NULL;
11992 lvp->flags = VUNSET;
11993 } else {
11994 lvp->text = vp->text;
11995 lvp->flags = vp->flags;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011996 vp->flags |= VSTRFIXED | VTEXTFIXED;
Eric Andersencb57d552001-06-28 07:25:16 +000011997 if (strchr(name, '='))
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011998 setvareq(xstrdup(name), 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011999 }
12000 }
12001 lvp->vp = vp;
12002 lvp->next = localvars;
12003 localvars = lvp;
12004 INTON;
12005}
12006
12007
12008/*
12009 * Called after a function returns.
12010 */
12011
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012012static void poplocalvars()
12013{
Eric Andersencb57d552001-06-28 07:25:16 +000012014 struct localvar *lvp;
12015 struct var *vp;
12016
12017 while ((lvp = localvars) != NULL) {
12018 localvars = lvp->next;
12019 vp = lvp->vp;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012020 if (vp == NULL) { /* $- saved */
Eric Andersen2870d962001-07-02 17:27:21 +000012021 memcpy(optet_vals, lvp->text, sizeof optet_vals);
Aaron Lehmann49c024a2002-08-02 06:39:47 +000012022 free(lvp->text);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012023 } else if ((lvp->flags & (VUNSET | VSTRFIXED)) == VUNSET) {
12024 (void) unsetvar(vp->text);
Eric Andersencb57d552001-06-28 07:25:16 +000012025 } else {
12026 if ((vp->flags & VTEXTFIXED) == 0)
Aaron Lehmann49c024a2002-08-02 06:39:47 +000012027 free(vp->text);
Eric Andersencb57d552001-06-28 07:25:16 +000012028 vp->flags = lvp->flags;
12029 vp->text = lvp->text;
12030 }
Aaron Lehmann49c024a2002-08-02 06:39:47 +000012031 free(lvp);
Eric Andersencb57d552001-06-28 07:25:16 +000012032 }
12033}
12034
12035
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012036static int setvarcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012037{
12038 if (argc <= 2)
12039 return unsetcmd(argc, argv);
12040 else if (argc == 3)
12041 setvar(argv[1], argv[2], 0);
12042 else
12043 error("List assignment not implemented");
12044 return 0;
12045}
12046
12047
12048/*
12049 * The unset builtin command. We unset the function before we unset the
12050 * variable to allow a function to be unset when there is a readonly variable
12051 * with the same name.
12052 */
12053
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012054static int unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012055{
12056 char **ap;
12057 int i;
12058 int flg_func = 0;
12059 int flg_var = 0;
12060 int ret = 0;
12061
12062 while ((i = nextopt("vf")) != '\0') {
12063 if (i == 'f')
12064 flg_func = 1;
12065 else
12066 flg_var = 1;
12067 }
12068 if (flg_func == 0 && flg_var == 0)
12069 flg_var = 1;
12070
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012071 for (ap = argptr; *ap; ap++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012072 if (flg_func)
12073 unsetfunc(*ap);
12074 if (flg_var)
12075 ret |= unsetvar(*ap);
12076 }
12077 return ret;
12078}
12079
12080
12081/*
12082 * Unset the specified variable.
12083 */
12084
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012085static int unsetvar(const char *s)
Eric Andersen62483552001-07-10 06:09:16 +000012086{
Eric Andersencb57d552001-06-28 07:25:16 +000012087 struct var **vpp;
12088 struct var *vp;
12089
12090 vpp = findvar(hashvar(s), s);
12091 vp = *vpp;
12092 if (vp) {
12093 if (vp->flags & VREADONLY)
12094 return (1);
12095 INTOFF;
12096 if (*(strchr(vp->text, '=') + 1) != '\0')
12097 setvar(s, nullstr, 0);
12098 vp->flags &= ~VEXPORT;
12099 vp->flags |= VUNSET;
12100 if ((vp->flags & VSTRFIXED) == 0) {
12101 if ((vp->flags & VTEXTFIXED) == 0)
Aaron Lehmann49c024a2002-08-02 06:39:47 +000012102 free(vp->text);
Eric Andersencb57d552001-06-28 07:25:16 +000012103 *vpp = vp->next;
Aaron Lehmann49c024a2002-08-02 06:39:47 +000012104 free(vp);
Eric Andersencb57d552001-06-28 07:25:16 +000012105 }
12106 INTON;
12107 return (0);
12108 }
12109
12110 return (0);
12111}
12112
12113
12114
12115/*
12116 * Find the appropriate entry in the hash table from the name.
12117 */
12118
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012119static struct var **hashvar(const char *p)
Eric Andersen62483552001-07-10 06:09:16 +000012120{
Eric Andersencb57d552001-06-28 07:25:16 +000012121 unsigned int hashval;
12122
12123 hashval = ((unsigned char) *p) << 4;
12124 while (*p && *p != '=')
12125 hashval += (unsigned char) *p++;
12126 return &vartab[hashval % VTABSIZE];
12127}
12128
12129
12130
12131/*
12132 * Returns true if the two strings specify the same varable. The first
12133 * variable name is terminated by '='; the second may be terminated by
12134 * either '=' or '\0'.
12135 */
12136
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012137static int varequal(const char *p, const char *q)
Eric Andersen62483552001-07-10 06:09:16 +000012138{
Eric Andersencb57d552001-06-28 07:25:16 +000012139 while (*p == *q++) {
12140 if (*p++ == '=')
12141 return 1;
12142 }
12143 if (*p == '=' && *(q - 1) == '\0')
12144 return 1;
12145 return 0;
12146}
12147
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012148static void showvars(const char *myprefix, int mask, int xor)
Eric Andersencb57d552001-06-28 07:25:16 +000012149{
12150 struct var **vpp;
12151 struct var *vp;
12152 const char *sep = myprefix == nullstr ? myprefix : spcstr;
12153
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012154 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
12155 for (vp = *vpp; vp; vp = vp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +000012156 if ((vp->flags & mask) ^ xor) {
12157 char *p;
12158 int len;
12159
12160 p = strchr(vp->text, '=') + 1;
12161 len = p - vp->text;
12162 p = single_quote(p);
12163
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012164 printf("%s%s%.*s%s\n", myprefix, sep, len, vp->text, p);
Eric Andersencb57d552001-06-28 07:25:16 +000012165 stunalloc(p);
12166 }
12167 }
12168 }
12169}
12170
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012171static struct var **findvar(struct var **vpp, const char *name)
Eric Andersencb57d552001-06-28 07:25:16 +000012172{
12173 for (; *vpp; vpp = &(*vpp)->next) {
12174 if (varequal((*vpp)->text, name)) {
12175 break;
12176 }
12177 }
12178 return vpp;
12179}
12180
12181/*
12182 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
12183 * This file contains code for the times builtin.
Eric Andersencb57d552001-06-28 07:25:16 +000012184 */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012185static int timescmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012186{
12187 struct tms buf;
12188 long int clk_tck = sysconf(_SC_CLK_TCK);
12189
12190 times(&buf);
12191 printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012192 (int) (buf.tms_utime / clk_tck / 60),
12193 ((double) buf.tms_utime) / clk_tck,
12194 (int) (buf.tms_stime / clk_tck / 60),
12195 ((double) buf.tms_stime) / clk_tck,
12196 (int) (buf.tms_cutime / clk_tck / 60),
12197 ((double) buf.tms_cutime) / clk_tck,
12198 (int) (buf.tms_cstime / clk_tck / 60),
12199 ((double) buf.tms_cstime) / clk_tck);
Eric Andersencb57d552001-06-28 07:25:16 +000012200 return 0;
12201}
12202
Eric Andersend35c5df2002-01-09 15:37:36 +000012203#ifdef CONFIG_ASH_MATH_SUPPORT
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012204/* The let builtin. */
12205int letcmd(int argc, char **argv)
Eric Andersen74bcd162001-07-30 21:41:37 +000012206{
Eric Andersen34506362001-08-02 05:02:46 +000012207 int errcode;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012208 long result = 0;
12209
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012210 if (argc == 2) {
12211 char *tmp, *expression, p[13];
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012212
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012213 expression = strchr(argv[1], '=');
12214 if (!expression) {
Eric Andersen34506362001-08-02 05:02:46 +000012215 /* Cannot use 'error()' here, or the return code
12216 * will be incorrect */
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012217 out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]);
12218 return 0;
Eric Andersen74bcd162001-07-30 21:41:37 +000012219 }
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012220 *expression = '\0';
12221 tmp = ++expression;
Eric Andersen34506362001-08-02 05:02:46 +000012222 result = arith(tmp, &errcode);
12223 if (errcode < 0) {
12224 /* Cannot use 'error()' here, or the return code
12225 * will be incorrect */
12226 out2fmt("sh: let: ");
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012227 if (errcode == -2)
Eric Andersen34506362001-08-02 05:02:46 +000012228 out2fmt("divide by zero");
12229 else
12230 out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression);
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012231 return 0;
12232 }
12233 snprintf(p, 12, "%ld", result);
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012234 setvar(argv[1], xstrdup(p), 0);
Eric Andersenfa1c5aa2001-07-31 21:38:23 +000012235 } else if (argc >= 3)
12236 synerror("invalid operand");
12237 return !result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012238}
12239#endif
12240
12241
12242
Eric Andersendf82f612001-06-28 07:46:40 +000012243/*-
12244 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000012245 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000012246 *
12247 * This code is derived from software contributed to Berkeley by
12248 * Kenneth Almquist.
12249 *
12250 * Redistribution and use in source and binary forms, with or without
12251 * modification, are permitted provided that the following conditions
12252 * are met:
12253 * 1. Redistributions of source code must retain the above copyright
12254 * notice, this list of conditions and the following disclaimer.
12255 * 2. Redistributions in binary form must reproduce the above copyright
12256 * notice, this list of conditions and the following disclaimer in the
12257 * documentation and/or other materials provided with the distribution.
12258 *
Eric Andersen2870d962001-07-02 17:27:21 +000012259 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
12260 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
Eric Andersendf82f612001-06-28 07:46:40 +000012261 *
12262 * 4. Neither the name of the University nor the names of its contributors
12263 * may be used to endorse or promote products derived from this software
12264 * without specific prior written permission.
12265 *
12266 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
12267 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12268 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
12269 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
12270 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12271 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
12272 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
12273 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
12274 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
12275 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
12276 * SUCH DAMAGE.
12277 */