1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#define DEBUG 0
30
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
35#define PROFILE 0
36
37#define JOBS ENABLE_ASH_JOB_CONTROL
38
39#include "busybox.h"
40#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
43#include <sys/times.h>
44
45#include "shell_common.h"
46#include "math.h"
47#if ENABLE_ASH_RANDOM_SUPPORT
48# include "random.h"
49#else
50# define CLEAR_RANDOM_T(rnd) ((void)0)
51#endif
52
53#include "NUM_APPLETS.h"
54#if NUM_APPLETS == 1
55
56# undef CONFIG_FEATURE_SH_STANDALONE
57# undef ENABLE_FEATURE_SH_STANDALONE
58# undef IF_FEATURE_SH_STANDALONE
59# undef IF_NOT_FEATURE_SH_STANDALONE
60# define ENABLE_FEATURE_SH_STANDALONE 0
61# define IF_FEATURE_SH_STANDALONE(...)
62# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
63#endif
64
65#ifndef PIPE_BUF
66# define PIPE_BUF 4096
67#endif
68
69#if !BB_MMU
70# error "Do not even bother, ash will not run on NOMMU machine"
71#endif
72
73
74
75
76#define VTABSIZE 39
77#define ATABSIZE 39
78#define CMDTABLESIZE 31
79
80
81
82
83static const char *const optletters_optnames[] = {
84 "e" "errexit",
85 "f" "noglob",
86 "I" "ignoreeof",
87 "i" "interactive",
88 "m" "monitor",
89 "n" "noexec",
90 "s" "stdin",
91 "x" "xtrace",
92 "v" "verbose",
93 "C" "noclobber",
94 "a" "allexport",
95 "b" "notify",
96 "u" "nounset",
97 "\0" "vi"
98#if ENABLE_ASH_BASH_COMPAT
99 ,"\0" "pipefail"
100#endif
101#if DEBUG
102 ,"\0" "nolog"
103 ,"\0" "debug"
104#endif
105};
106
107#define optletters(n) optletters_optnames[n][0]
108#define optnames(n) (optletters_optnames[n] + 1)
109
110enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
111
112
113
114
115#define msg_illnum "Illegal number: %s"
116
117
118
119
120
121
122
123
124
125
126struct jmploc {
127 jmp_buf loc;
128};
129
130struct globals_misc {
131
132 int rootpid;
133
134 int shlvl;
135#define rootshell (!shlvl)
136 char *minusc;
137
138 char *curdir;
139 char *physdir;
140
141 char *arg0;
142
143 struct jmploc *exception_handler;
144
145 volatile int suppress_int;
146 volatile smallint pending_int;
147
148 volatile smallint pending_sig;
149 smallint exception_type;
150
151#define EXINT 0
152#define EXERROR 1
153#define EXSHELLPROC 2
154#define EXEXEC 3
155#define EXEXIT 4
156#define EXSIG 5
157
158 smallint isloginsh;
159 char nullstr[1];
160
161 char optlist[NOPTS];
162#define eflag optlist[0]
163#define fflag optlist[1]
164#define Iflag optlist[2]
165#define iflag optlist[3]
166#define mflag optlist[4]
167#define nflag optlist[5]
168#define sflag optlist[6]
169#define xflag optlist[7]
170#define vflag optlist[8]
171#define Cflag optlist[9]
172#define aflag optlist[10]
173#define bflag optlist[11]
174#define uflag optlist[12]
175#define viflag optlist[13]
176#if ENABLE_ASH_BASH_COMPAT
177# define pipefail optlist[14]
178#else
179# define pipefail 0
180#endif
181#if DEBUG
182# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
183# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
184#endif
185
186
187
188
189
190
191
192 char sigmode[NSIG - 1];
193#define S_DFL 1
194#define S_CATCH 2
195#define S_IGN 3
196#define S_HARD_IGN 4
197
198
199 uint8_t gotsig[NSIG - 1];
200 uint8_t may_have_traps;
201 char *trap[NSIG];
202 char **trap_ptr;
203
204
205#if ENABLE_ASH_RANDOM_SUPPORT
206 random_t random_gen;
207#endif
208 pid_t backgndpid;
209 smallint job_warning;
210};
211extern struct globals_misc *const ash_ptr_to_globals_misc;
212#define G_misc (*ash_ptr_to_globals_misc)
213#define rootpid (G_misc.rootpid )
214#define shlvl (G_misc.shlvl )
215#define minusc (G_misc.minusc )
216#define curdir (G_misc.curdir )
217#define physdir (G_misc.physdir )
218#define arg0 (G_misc.arg0 )
219#define exception_handler (G_misc.exception_handler)
220#define exception_type (G_misc.exception_type )
221#define suppress_int (G_misc.suppress_int )
222#define pending_int (G_misc.pending_int )
223#define pending_sig (G_misc.pending_sig )
224#define isloginsh (G_misc.isloginsh )
225#define nullstr (G_misc.nullstr )
226#define optlist (G_misc.optlist )
227#define sigmode (G_misc.sigmode )
228#define gotsig (G_misc.gotsig )
229#define may_have_traps (G_misc.may_have_traps )
230#define trap (G_misc.trap )
231#define trap_ptr (G_misc.trap_ptr )
232#define random_gen (G_misc.random_gen )
233#define backgndpid (G_misc.backgndpid )
234#define job_warning (G_misc.job_warning)
235#define INIT_G_misc() do { \
236 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
237 barrier(); \
238 curdir = nullstr; \
239 physdir = nullstr; \
240 trap_ptr = trap; \
241} while (0)
242
243
244
245#if DEBUG
246static void trace_printf(const char *fmt, ...);
247static void trace_vprintf(const char *fmt, va_list va);
248# define TRACE(param) trace_printf param
249# define TRACEV(param) trace_vprintf param
250# define close(fd) do { \
251 int dfd = (fd); \
252 if (close(dfd) < 0) \
253 bb_error_msg("bug on %d: closing %d(0x%x)", \
254 __LINE__, dfd, dfd); \
255} while (0)
256#else
257# define TRACE(param)
258# define TRACEV(param)
259#endif
260
261
262
263#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
264
265static int isdigit_str9(const char *str)
266{
267 int maxlen = 9 + 1;
268 while (--maxlen && isdigit(*str))
269 str++;
270 return (*str == '\0');
271}
272
273static const char *var_end(const char *var)
274{
275 while (*var)
276 if (*var++ == '=')
277 break;
278 return var;
279}
280
281
282
283
284
285
286
287
288
289#define INT_OFF do { \
290 suppress_int++; \
291 xbarrier(); \
292} while (0)
293
294
295
296
297
298
299static void raise_exception(int) NORETURN;
300static void
301raise_exception(int e)
302{
303#if DEBUG
304 if (exception_handler == NULL)
305 abort();
306#endif
307 INT_OFF;
308 exception_type = e;
309 longjmp(exception_handler->loc, 1);
310}
311#if DEBUG
312#define raise_exception(e) do { \
313 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
314 raise_exception(e); \
315} while (0)
316#endif
317
318
319
320
321
322
323
324
325static void raise_interrupt(void) NORETURN;
326static void
327raise_interrupt(void)
328{
329 int ex_type;
330
331 pending_int = 0;
332
333
334 sigprocmask_allsigs(SIG_UNBLOCK);
335
336
337 ex_type = EXSIG;
338 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
339 if (!(rootshell && iflag)) {
340
341 signal(SIGINT, SIG_DFL);
342 raise(SIGINT);
343 }
344 ex_type = EXINT;
345 }
346 raise_exception(ex_type);
347
348}
349#if DEBUG
350#define raise_interrupt() do { \
351 TRACE(("raising interrupt on line %d\n", __LINE__)); \
352 raise_interrupt(); \
353} while (0)
354#endif
355
356static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
357int_on(void)
358{
359 xbarrier();
360 if (--suppress_int == 0 && pending_int) {
361 raise_interrupt();
362 }
363}
364#define INT_ON int_on()
365static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
366force_int_on(void)
367{
368 xbarrier();
369 suppress_int = 0;
370 if (pending_int)
371 raise_interrupt();
372}
373#define FORCE_INT_ON force_int_on()
374
375#define SAVE_INT(v) ((v) = suppress_int)
376
377#define RESTORE_INT(v) do { \
378 xbarrier(); \
379 suppress_int = (v); \
380 if (suppress_int == 0 && pending_int) \
381 raise_interrupt(); \
382} while (0)
383
384
385
386
387static void
388outstr(const char *p, FILE *file)
389{
390 INT_OFF;
391 fputs(p, file);
392 INT_ON;
393}
394
395static void
396flush_stdout_stderr(void)
397{
398 INT_OFF;
399 fflush_all();
400 INT_ON;
401}
402
403static void
404outcslow(int c, FILE *dest)
405{
406 INT_OFF;
407 putc(c, dest);
408 fflush(dest);
409 INT_ON;
410}
411
412static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
413static int
414out1fmt(const char *fmt, ...)
415{
416 va_list ap;
417 int r;
418
419 INT_OFF;
420 va_start(ap, fmt);
421 r = vprintf(fmt, ap);
422 va_end(ap);
423 INT_ON;
424 return r;
425}
426
427static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
428static int
429fmtstr(char *outbuf, size_t length, const char *fmt, ...)
430{
431 va_list ap;
432 int ret;
433
434 va_start(ap, fmt);
435 INT_OFF;
436 ret = vsnprintf(outbuf, length, fmt, ap);
437 va_end(ap);
438 INT_ON;
439 return ret;
440}
441
442static void
443out1str(const char *p)
444{
445 outstr(p, stdout);
446}
447
448static void
449out2str(const char *p)
450{
451 outstr(p, stderr);
452 flush_stdout_stderr();
453}
454
455
456
457
458
459#define CTL_FIRST CTLESC
460#define CTLESC ((unsigned char)'\201')
461#define CTLVAR ((unsigned char)'\202')
462#define CTLENDVAR ((unsigned char)'\203')
463#define CTLBACKQ ((unsigned char)'\204')
464#define CTLQUOTE 01
465
466#define CTLARI ((unsigned char)'\206')
467#define CTLENDARI ((unsigned char)'\207')
468#define CTLQUOTEMARK ((unsigned char)'\210')
469#define CTL_LAST CTLQUOTEMARK
470
471
472#define VSTYPE 0x0f
473#define VSNUL 0x10
474#define VSQUOTE 0x80
475
476
477#define VSNORMAL 0x1
478#define VSMINUS 0x2
479#define VSPLUS 0x3
480#define VSQUESTION 0x4
481#define VSASSIGN 0x5
482#define VSTRIMRIGHT 0x6
483#define VSTRIMRIGHTMAX 0x7
484#define VSTRIMLEFT 0x8
485#define VSTRIMLEFTMAX 0x9
486#define VSLENGTH 0xa
487#if ENABLE_ASH_BASH_COMPAT
488#define VSSUBSTR 0xc
489#define VSREPLACE 0xd
490#define VSREPLACEALL 0xe
491#endif
492
493static const char dolatstr[] ALIGN1 = {
494 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
495};
496
497#define NCMD 0
498#define NPIPE 1
499#define NREDIR 2
500#define NBACKGND 3
501#define NSUBSHELL 4
502#define NAND 5
503#define NOR 6
504#define NSEMI 7
505#define NIF 8
506#define NWHILE 9
507#define NUNTIL 10
508#define NFOR 11
509#define NCASE 12
510#define NCLIST 13
511#define NDEFUN 14
512#define NARG 15
513#define NTO 16
514#if ENABLE_ASH_BASH_COMPAT
515#define NTO2 17
516#endif
517#define NCLOBBER 18
518#define NFROM 19
519#define NFROMTO 20
520#define NAPPEND 21
521#define NTOFD 22
522#define NFROMFD 23
523#define NHERE 24
524#define NXHERE 25
525#define NNOT 26
526#define N_NUMBER 27
527
528union node;
529
530struct ncmd {
531 smallint type;
532 union node *assign;
533 union node *args;
534 union node *redirect;
535};
536
537struct npipe {
538 smallint type;
539 smallint pipe_backgnd;
540 struct nodelist *cmdlist;
541};
542
543struct nredir {
544 smallint type;
545 union node *n;
546 union node *redirect;
547};
548
549struct nbinary {
550 smallint type;
551 union node *ch1;
552 union node *ch2;
553};
554
555struct nif {
556 smallint type;
557 union node *test;
558 union node *ifpart;
559 union node *elsepart;
560};
561
562struct nfor {
563 smallint type;
564 union node *args;
565 union node *body;
566 char *var;
567};
568
569struct ncase {
570 smallint type;
571 union node *expr;
572 union node *cases;
573};
574
575struct nclist {
576 smallint type;
577 union node *next;
578 union node *pattern;
579 union node *body;
580};
581
582struct narg {
583 smallint type;
584 union node *next;
585 char *text;
586 struct nodelist *backquote;
587};
588
589
590
591
592
593struct nfile {
594 smallint type;
595 union node *next;
596 int fd;
597 int _unused_dupfd;
598 union node *fname;
599 char *expfname;
600};
601
602struct ndup {
603 smallint type;
604 union node *next;
605 int fd;
606 int dupfd;
607 union node *vname;
608 char *_unused_expfname;
609};
610
611struct nhere {
612 smallint type;
613 union node *next;
614 int fd;
615 union node *doc;
616};
617
618struct nnot {
619 smallint type;
620 union node *com;
621};
622
623union node {
624 smallint type;
625 struct ncmd ncmd;
626 struct npipe npipe;
627 struct nredir nredir;
628 struct nbinary nbinary;
629 struct nif nif;
630 struct nfor nfor;
631 struct ncase ncase;
632 struct nclist nclist;
633 struct narg narg;
634 struct nfile nfile;
635 struct ndup ndup;
636 struct nhere nhere;
637 struct nnot nnot;
638};
639
640
641
642
643
644#define NODE_EOF ((union node *) -1L)
645
646struct nodelist {
647 struct nodelist *next;
648 union node *n;
649};
650
651struct funcnode {
652 int count;
653 union node n;
654};
655
656
657
658
659static void
660freefunc(struct funcnode *f)
661{
662 if (f && --f->count < 0)
663 free(f);
664}
665
666
667
668
669#if DEBUG
670
671static FILE *tracefile;
672
673static void
674trace_printf(const char *fmt, ...)
675{
676 va_list va;
677
678 if (debug != 1)
679 return;
680 if (DEBUG_TIME)
681 fprintf(tracefile, "%u ", (int) time(NULL));
682 if (DEBUG_PID)
683 fprintf(tracefile, "[%u] ", (int) getpid());
684 if (DEBUG_SIG)
685 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
686 va_start(va, fmt);
687 vfprintf(tracefile, fmt, va);
688 va_end(va);
689}
690
691static void
692trace_vprintf(const char *fmt, va_list va)
693{
694 if (debug != 1)
695 return;
696 if (DEBUG_TIME)
697 fprintf(tracefile, "%u ", (int) time(NULL));
698 if (DEBUG_PID)
699 fprintf(tracefile, "[%u] ", (int) getpid());
700 if (DEBUG_SIG)
701 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
702 vfprintf(tracefile, fmt, va);
703}
704
705static void
706trace_puts(const char *s)
707{
708 if (debug != 1)
709 return;
710 fputs(s, tracefile);
711}
712
713static void
714trace_puts_quoted(char *s)
715{
716 char *p;
717 char c;
718
719 if (debug != 1)
720 return;
721 putc('"', tracefile);
722 for (p = s; *p; p++) {
723 switch ((unsigned char)*p) {
724 case '\n': c = 'n'; goto backslash;
725 case '\t': c = 't'; goto backslash;
726 case '\r': c = 'r'; goto backslash;
727 case '\"': c = '\"'; goto backslash;
728 case '\\': c = '\\'; goto backslash;
729 case CTLESC: c = 'e'; goto backslash;
730 case CTLVAR: c = 'v'; goto backslash;
731 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
732 case CTLBACKQ: c = 'q'; goto backslash;
733 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
734 backslash:
735 putc('\\', tracefile);
736 putc(c, tracefile);
737 break;
738 default:
739 if (*p >= ' ' && *p <= '~')
740 putc(*p, tracefile);
741 else {
742 putc('\\', tracefile);
743 putc((*p >> 6) & 03, tracefile);
744 putc((*p >> 3) & 07, tracefile);
745 putc(*p & 07, tracefile);
746 }
747 break;
748 }
749 }
750 putc('"', tracefile);
751}
752
753static void
754trace_puts_args(char **ap)
755{
756 if (debug != 1)
757 return;
758 if (!*ap)
759 return;
760 while (1) {
761 trace_puts_quoted(*ap);
762 if (!*++ap) {
763 putc('\n', tracefile);
764 break;
765 }
766 putc(' ', tracefile);
767 }
768}
769
770static void
771opentrace(void)
772{
773 char s[100];
774#ifdef O_APPEND
775 int flags;
776#endif
777
778 if (debug != 1) {
779 if (tracefile)
780 fflush(tracefile);
781
782 return;
783 }
784 strcpy(s, "./trace");
785 if (tracefile) {
786 if (!freopen(s, "a", tracefile)) {
787 fprintf(stderr, "Can't re-open %s\n", s);
788 debug = 0;
789 return;
790 }
791 } else {
792 tracefile = fopen(s, "a");
793 if (tracefile == NULL) {
794 fprintf(stderr, "Can't open %s\n", s);
795 debug = 0;
796 return;
797 }
798 }
799#ifdef O_APPEND
800 flags = fcntl(fileno(tracefile), F_GETFL);
801 if (flags >= 0)
802 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
803#endif
804 setlinebuf(tracefile);
805 fputs("\nTracing started.\n", tracefile);
806}
807
808static void
809indent(int amount, char *pfx, FILE *fp)
810{
811 int i;
812
813 for (i = 0; i < amount; i++) {
814 if (pfx && i == amount - 1)
815 fputs(pfx, fp);
816 putc('\t', fp);
817 }
818}
819
820
821static void shtree(union node *n, int ind, char *pfx, FILE *fp);
822
823static void
824sharg(union node *arg, FILE *fp)
825{
826 char *p;
827 struct nodelist *bqlist;
828 unsigned char subtype;
829
830 if (arg->type != NARG) {
831 out1fmt("<node type %d>\n", arg->type);
832 abort();
833 }
834 bqlist = arg->narg.backquote;
835 for (p = arg->narg.text; *p; p++) {
836 switch ((unsigned char)*p) {
837 case CTLESC:
838 putc(*++p, fp);
839 break;
840 case CTLVAR:
841 putc('$', fp);
842 putc('{', fp);
843 subtype = *++p;
844 if (subtype == VSLENGTH)
845 putc('#', fp);
846
847 while (*p != '=')
848 putc(*p++, fp);
849
850 if (subtype & VSNUL)
851 putc(':', fp);
852
853 switch (subtype & VSTYPE) {
854 case VSNORMAL:
855 putc('}', fp);
856 break;
857 case VSMINUS:
858 putc('-', fp);
859 break;
860 case VSPLUS:
861 putc('+', fp);
862 break;
863 case VSQUESTION:
864 putc('?', fp);
865 break;
866 case VSASSIGN:
867 putc('=', fp);
868 break;
869 case VSTRIMLEFT:
870 putc('#', fp);
871 break;
872 case VSTRIMLEFTMAX:
873 putc('#', fp);
874 putc('#', fp);
875 break;
876 case VSTRIMRIGHT:
877 putc('%', fp);
878 break;
879 case VSTRIMRIGHTMAX:
880 putc('%', fp);
881 putc('%', fp);
882 break;
883 case VSLENGTH:
884 break;
885 default:
886 out1fmt("<subtype %d>", subtype);
887 }
888 break;
889 case CTLENDVAR:
890 putc('}', fp);
891 break;
892 case CTLBACKQ:
893 case CTLBACKQ|CTLQUOTE:
894 putc('$', fp);
895 putc('(', fp);
896 shtree(bqlist->n, -1, NULL, fp);
897 putc(')', fp);
898 break;
899 default:
900 putc(*p, fp);
901 break;
902 }
903 }
904}
905
906static void
907shcmd(union node *cmd, FILE *fp)
908{
909 union node *np;
910 int first;
911 const char *s;
912 int dftfd;
913
914 first = 1;
915 for (np = cmd->ncmd.args; np; np = np->narg.next) {
916 if (!first)
917 putc(' ', fp);
918 sharg(np, fp);
919 first = 0;
920 }
921 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
922 if (!first)
923 putc(' ', fp);
924 dftfd = 0;
925 switch (np->nfile.type) {
926 case NTO: s = ">>"+1; dftfd = 1; break;
927 case NCLOBBER: s = ">|"; dftfd = 1; break;
928 case NAPPEND: s = ">>"; dftfd = 1; break;
929#if ENABLE_ASH_BASH_COMPAT
930 case NTO2:
931#endif
932 case NTOFD: s = ">&"; dftfd = 1; break;
933 case NFROM: s = "<"; break;
934 case NFROMFD: s = "<&"; break;
935 case NFROMTO: s = "<>"; break;
936 default: s = "*error*"; break;
937 }
938 if (np->nfile.fd != dftfd)
939 fprintf(fp, "%d", np->nfile.fd);
940 fputs(s, fp);
941 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
942 fprintf(fp, "%d", np->ndup.dupfd);
943 } else {
944 sharg(np->nfile.fname, fp);
945 }
946 first = 0;
947 }
948}
949
950static void
951shtree(union node *n, int ind, char *pfx, FILE *fp)
952{
953 struct nodelist *lp;
954 const char *s;
955
956 if (n == NULL)
957 return;
958
959 indent(ind, pfx, fp);
960
961 if (n == NODE_EOF) {
962 fputs("<EOF>", fp);
963 return;
964 }
965
966 switch (n->type) {
967 case NSEMI:
968 s = "; ";
969 goto binop;
970 case NAND:
971 s = " && ";
972 goto binop;
973 case NOR:
974 s = " || ";
975 binop:
976 shtree(n->nbinary.ch1, ind, NULL, fp);
977
978 fputs(s, fp);
979 shtree(n->nbinary.ch2, ind, NULL, fp);
980 break;
981 case NCMD:
982 shcmd(n, fp);
983 if (ind >= 0)
984 putc('\n', fp);
985 break;
986 case NPIPE:
987 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
988 shtree(lp->n, 0, NULL, fp);
989 if (lp->next)
990 fputs(" | ", fp);
991 }
992 if (n->npipe.pipe_backgnd)
993 fputs(" &", fp);
994 if (ind >= 0)
995 putc('\n', fp);
996 break;
997 default:
998 fprintf(fp, "<node type %d>", n->type);
999 if (ind >= 0)
1000 putc('\n', fp);
1001 break;
1002 }
1003}
1004
1005static void
1006showtree(union node *n)
1007{
1008 trace_puts("showtree called\n");
1009 shtree(n, 1, NULL, stderr);
1010}
1011
1012#endif
1013
1014
1015
1016
1017
1018
1019
1020struct strlist {
1021 struct strlist *next;
1022 char *text;
1023};
1024
1025struct alias;
1026
1027struct strpush {
1028 struct strpush *prev;
1029 char *prev_string;
1030 int prev_left_in_line;
1031#if ENABLE_ASH_ALIAS
1032 struct alias *ap;
1033#endif
1034 char *string;
1035};
1036
1037struct parsefile {
1038 struct parsefile *prev;
1039 int linno;
1040 int pf_fd;
1041 int left_in_line;
1042 int left_in_buffer;
1043 char *next_to_pgetc;
1044 char *buf;
1045 struct strpush *strpush;
1046 struct strpush basestrpush;
1047};
1048
1049static struct parsefile basepf;
1050static struct parsefile *g_parsefile = &basepf;
1051static int startlinno;
1052static char *commandname;
1053static struct strlist *cmdenviron;
1054static uint8_t exitstatus;
1055
1056
1057
1058
1059static void
1060ash_vmsg(const char *msg, va_list ap)
1061{
1062 fprintf(stderr, "%s: ", arg0);
1063 if (commandname) {
1064 if (strcmp(arg0, commandname))
1065 fprintf(stderr, "%s: ", commandname);
1066 if (!iflag || g_parsefile->pf_fd > 0)
1067 fprintf(stderr, "line %d: ", startlinno);
1068 }
1069 vfprintf(stderr, msg, ap);
1070 outcslow('\n', stderr);
1071}
1072
1073
1074
1075
1076
1077
1078static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
1079static void
1080ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
1081{
1082#if DEBUG
1083 if (msg) {
1084 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1085 TRACEV((msg, ap));
1086 TRACE(("\") pid=%d\n", getpid()));
1087 } else
1088 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1089 if (msg)
1090#endif
1091 ash_vmsg(msg, ap);
1092
1093 flush_stdout_stderr();
1094 raise_exception(cond);
1095
1096}
1097
1098static void ash_msg_and_raise_error(const char *, ...) NORETURN;
1099static void
1100ash_msg_and_raise_error(const char *msg, ...)
1101{
1102 va_list ap;
1103
1104 va_start(ap, msg);
1105 ash_vmsg_and_raise(EXERROR, msg, ap);
1106
1107 va_end(ap);
1108}
1109
1110static void raise_error_syntax(const char *) NORETURN;
1111static void
1112raise_error_syntax(const char *msg)
1113{
1114 ash_msg_and_raise_error("syntax error: %s", msg);
1115
1116}
1117
1118static void ash_msg_and_raise(int, const char *, ...) NORETURN;
1119static void
1120ash_msg_and_raise(int cond, const char *msg, ...)
1121{
1122 va_list ap;
1123
1124 va_start(ap, msg);
1125 ash_vmsg_and_raise(cond, msg, ap);
1126
1127 va_end(ap);
1128}
1129
1130
1131
1132
1133static void
1134ash_msg(const char *fmt, ...)
1135{
1136 va_list ap;
1137
1138 va_start(ap, fmt);
1139 ash_vmsg(fmt, ap);
1140 va_end(ap);
1141}
1142
1143
1144
1145
1146
1147
1148static const char *
1149errmsg(int e, const char *em)
1150{
1151 if (e == ENOENT || e == ENOTDIR) {
1152 return em;
1153 }
1154 return strerror(e);
1155}
1156
1157
1158
1159
1160#if 0
1161
1162
1163
1164
1165
1166static void *
1167ckrealloc(void * p, size_t nbytes)
1168{
1169 p = realloc(p, nbytes);
1170 if (!p)
1171 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1172 return p;
1173}
1174
1175static void *
1176ckmalloc(size_t nbytes)
1177{
1178 return ckrealloc(NULL, nbytes);
1179}
1180
1181static void *
1182ckzalloc(size_t nbytes)
1183{
1184 return memset(ckmalloc(nbytes), 0, nbytes);
1185}
1186
1187static char *
1188ckstrdup(const char *s)
1189{
1190 char *p = strdup(s);
1191 if (!p)
1192 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1193 return p;
1194}
1195#else
1196
1197# define ckrealloc xrealloc
1198# define ckmalloc xmalloc
1199# define ckzalloc xzalloc
1200# define ckstrdup xstrdup
1201#endif
1202
1203
1204
1205
1206
1207#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1208enum {
1209
1210
1211
1212 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
1213
1214 MINSIZE = SHELL_ALIGN(504),
1215};
1216
1217struct stack_block {
1218 struct stack_block *prev;
1219 char space[MINSIZE];
1220};
1221
1222struct stackmark {
1223 struct stack_block *stackp;
1224 char *stacknxt;
1225 size_t stacknleft;
1226 struct stackmark *marknext;
1227};
1228
1229
1230struct globals_memstack {
1231 struct stack_block *g_stackp;
1232 struct stackmark *markp;
1233 char *g_stacknxt;
1234 char *sstrend;
1235 size_t g_stacknleft;
1236 int herefd;
1237 struct stack_block stackbase;
1238};
1239extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1240#define G_memstack (*ash_ptr_to_globals_memstack)
1241#define g_stackp (G_memstack.g_stackp )
1242#define markp (G_memstack.markp )
1243#define g_stacknxt (G_memstack.g_stacknxt )
1244#define sstrend (G_memstack.sstrend )
1245#define g_stacknleft (G_memstack.g_stacknleft)
1246#define herefd (G_memstack.herefd )
1247#define stackbase (G_memstack.stackbase )
1248#define INIT_G_memstack() do { \
1249 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1250 barrier(); \
1251 g_stackp = &stackbase; \
1252 g_stacknxt = stackbase.space; \
1253 g_stacknleft = MINSIZE; \
1254 sstrend = stackbase.space + MINSIZE; \
1255 herefd = -1; \
1256} while (0)
1257
1258
1259#define stackblock() ((void *)g_stacknxt)
1260#define stackblocksize() g_stacknleft
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270static void *
1271stalloc(size_t nbytes)
1272{
1273 char *p;
1274 size_t aligned;
1275
1276 aligned = SHELL_ALIGN(nbytes);
1277 if (aligned > g_stacknleft) {
1278 size_t len;
1279 size_t blocksize;
1280 struct stack_block *sp;
1281
1282 blocksize = aligned;
1283 if (blocksize < MINSIZE)
1284 blocksize = MINSIZE;
1285 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1286 if (len < blocksize)
1287 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1288 INT_OFF;
1289 sp = ckmalloc(len);
1290 sp->prev = g_stackp;
1291 g_stacknxt = sp->space;
1292 g_stacknleft = blocksize;
1293 sstrend = g_stacknxt + blocksize;
1294 g_stackp = sp;
1295 INT_ON;
1296 }
1297 p = g_stacknxt;
1298 g_stacknxt += aligned;
1299 g_stacknleft -= aligned;
1300 return p;
1301}
1302
1303static void *
1304stzalloc(size_t nbytes)
1305{
1306 return memset(stalloc(nbytes), 0, nbytes);
1307}
1308
1309static void
1310stunalloc(void *p)
1311{
1312#if DEBUG
1313 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
1314 write(STDERR_FILENO, "stunalloc\n", 10);
1315 abort();
1316 }
1317#endif
1318 g_stacknleft += g_stacknxt - (char *)p;
1319 g_stacknxt = p;
1320}
1321
1322
1323
1324
1325static char *
1326ststrdup(const char *p)
1327{
1328 size_t len = strlen(p) + 1;
1329 return memcpy(stalloc(len), p, len);
1330}
1331
1332static void
1333setstackmark(struct stackmark *mark)
1334{
1335 mark->stackp = g_stackp;
1336 mark->stacknxt = g_stacknxt;
1337 mark->stacknleft = g_stacknleft;
1338 mark->marknext = markp;
1339 markp = mark;
1340}
1341
1342static void
1343popstackmark(struct stackmark *mark)
1344{
1345 struct stack_block *sp;
1346
1347 if (!mark->stackp)
1348 return;
1349
1350 INT_OFF;
1351 markp = mark->marknext;
1352 while (g_stackp != mark->stackp) {
1353 sp = g_stackp;
1354 g_stackp = sp->prev;
1355 free(sp);
1356 }
1357 g_stacknxt = mark->stacknxt;
1358 g_stacknleft = mark->stacknleft;
1359 sstrend = mark->stacknxt + mark->stacknleft;
1360 INT_ON;
1361}
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372static void
1373growstackblock(void)
1374{
1375 size_t newlen;
1376
1377 newlen = g_stacknleft * 2;
1378 if (newlen < g_stacknleft)
1379 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1380 if (newlen < 128)
1381 newlen += 128;
1382
1383 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
1384 struct stack_block *oldstackp;
1385 struct stackmark *xmark;
1386 struct stack_block *sp;
1387 struct stack_block *prevstackp;
1388 size_t grosslen;
1389
1390 INT_OFF;
1391 oldstackp = g_stackp;
1392 sp = g_stackp;
1393 prevstackp = sp->prev;
1394 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1395 sp = ckrealloc(sp, grosslen);
1396 sp->prev = prevstackp;
1397 g_stackp = sp;
1398 g_stacknxt = sp->space;
1399 g_stacknleft = newlen;
1400 sstrend = sp->space + newlen;
1401
1402
1403
1404
1405
1406 xmark = markp;
1407 while (xmark != NULL && xmark->stackp == oldstackp) {
1408 xmark->stackp = g_stackp;
1409 xmark->stacknxt = g_stacknxt;
1410 xmark->stacknleft = g_stacknleft;
1411 xmark = xmark->marknext;
1412 }
1413 INT_ON;
1414 } else {
1415 char *oldspace = g_stacknxt;
1416 size_t oldlen = g_stacknleft;
1417 char *p = stalloc(newlen);
1418
1419
1420 g_stacknxt = memcpy(p, oldspace, oldlen);
1421 g_stacknleft += newlen;
1422 }
1423}
1424
1425static void
1426grabstackblock(size_t len)
1427{
1428 len = SHELL_ALIGN(len);
1429 g_stacknxt += len;
1430 g_stacknleft -= len;
1431}
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450static void *
1451growstackstr(void)
1452{
1453 size_t len = stackblocksize();
1454 if (herefd >= 0 && len >= 1024) {
1455 full_write(herefd, stackblock(), len);
1456 return stackblock();
1457 }
1458 growstackblock();
1459 return (char *)stackblock() + len;
1460}
1461
1462
1463
1464
1465static char *
1466makestrspace(size_t newlen, char *p)
1467{
1468 size_t len = p - g_stacknxt;
1469 size_t size = stackblocksize();
1470
1471 for (;;) {
1472 size_t nleft;
1473
1474 size = stackblocksize();
1475 nleft = size - len;
1476 if (nleft >= newlen)
1477 break;
1478 growstackblock();
1479 }
1480 return (char *)stackblock() + len;
1481}
1482
1483static char *
1484stack_nputstr(const char *s, size_t n, char *p)
1485{
1486 p = makestrspace(n, p);
1487 p = (char *)memcpy(p, s, n) + n;
1488 return p;
1489}
1490
1491static char *
1492stack_putstr(const char *s, char *p)
1493{
1494 return stack_nputstr(s, strlen(s), p);
1495}
1496
1497static char *
1498_STPUTC(int c, char *p)
1499{
1500 if (p == sstrend)
1501 p = growstackstr();
1502 *p++ = c;
1503 return p;
1504}
1505
1506#define STARTSTACKSTR(p) ((p) = stackblock())
1507#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
1508#define CHECKSTRSPACE(n, p) do { \
1509 char *q = (p); \
1510 size_t l = (n); \
1511 size_t m = sstrend - q; \
1512 if (l > m) \
1513 (p) = makestrspace(l, q); \
1514} while (0)
1515#define USTPUTC(c, p) (*(p)++ = (c))
1516#define STACKSTRNUL(p) do { \
1517 if ((p) == sstrend) \
1518 (p) = growstackstr(); \
1519 *(p) = '\0'; \
1520} while (0)
1521#define STUNPUTC(p) (--(p))
1522#define STTOPC(p) ((p)[-1])
1523#define STADJUST(amount, p) ((p) += (amount))
1524
1525#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1526#define ungrabstackstr(s, p) stunalloc(s)
1527#define stackstrend() ((void *)sstrend)
1528
1529
1530
1531
1532
1533
1534
1535static char *
1536prefix(const char *string, const char *pfx)
1537{
1538 while (*pfx) {
1539 if (*pfx++ != *string++)
1540 return NULL;
1541 }
1542 return (char *) string;
1543}
1544
1545
1546
1547
1548static int
1549is_number(const char *p)
1550{
1551 do {
1552 if (!isdigit(*p))
1553 return 0;
1554 } while (*++p != '\0');
1555 return 1;
1556}
1557
1558
1559
1560
1561
1562static int
1563number(const char *s)
1564{
1565 if (!is_number(s))
1566 ash_msg_and_raise_error(msg_illnum, s);
1567 return atoi(s);
1568}
1569
1570
1571
1572
1573
1574static char *
1575single_quote(const char *s)
1576{
1577 char *p;
1578
1579 STARTSTACKSTR(p);
1580
1581 do {
1582 char *q;
1583 size_t len;
1584
1585 len = strchrnul(s, '\'') - s;
1586
1587 q = p = makestrspace(len + 3, p);
1588
1589 *q++ = '\'';
1590 q = (char *)memcpy(q, s, len) + len;
1591 *q++ = '\'';
1592 s += len;
1593
1594 STADJUST(q - p, p);
1595
1596 if (*s != '\'')
1597 break;
1598 len = 0;
1599 do len++; while (*++s == '\'');
1600
1601 q = p = makestrspace(len + 3, p);
1602
1603 *q++ = '"';
1604 q = (char *)memcpy(q, s - len, len) + len;
1605 *q++ = '"';
1606
1607 STADJUST(q - p, p);
1608 } while (*s);
1609
1610 USTPUTC('\0', p);
1611
1612 return stackblock();
1613}
1614
1615
1616
1617
1618static char **argptr;
1619static char *optionarg;
1620static char *optptr;
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632static int
1633nextopt(const char *optstring)
1634{
1635 char *p;
1636 const char *q;
1637 char c;
1638
1639 p = optptr;
1640 if (p == NULL || *p == '\0') {
1641
1642 p = *argptr;
1643 if (p == NULL)
1644 return '\0';
1645 if (*p != '-')
1646 return '\0';
1647 if (*++p == '\0')
1648 return '\0';
1649 argptr++;
1650 if (LONE_DASH(p))
1651 return '\0';
1652
1653 }
1654
1655 c = *p++;
1656 for (q = optstring; *q != c;) {
1657 if (*q == '\0')
1658 ash_msg_and_raise_error("illegal option -%c", c);
1659 if (*++q == ':')
1660 q++;
1661 }
1662 if (*++q == ':') {
1663 if (*p == '\0') {
1664 p = *argptr++;
1665 if (p == NULL)
1666 ash_msg_and_raise_error("no arg for -%c option", c);
1667 }
1668 optionarg = p;
1669 p = NULL;
1670 }
1671 optptr = p;
1672 return c;
1673}
1674
1675
1676
1677
1678
1679
1680
1681
1682struct shparam {
1683 int nparam;
1684#if ENABLE_ASH_GETOPTS
1685 int optind;
1686 int optoff;
1687#endif
1688 unsigned char malloced;
1689 char **p;
1690};
1691
1692
1693
1694
1695static void
1696freeparam(volatile struct shparam *param)
1697{
1698 if (param->malloced) {
1699 char **ap, **ap1;
1700 ap = ap1 = param->p;
1701 while (*ap)
1702 free(*ap++);
1703 free(ap1);
1704 }
1705}
1706
1707#if ENABLE_ASH_GETOPTS
1708static void FAST_FUNC getoptsreset(const char *value);
1709#endif
1710
1711struct var {
1712 struct var *next;
1713 int flags;
1714 const char *var_text;
1715 void (*var_func)(const char *) FAST_FUNC;
1716
1717};
1718
1719struct localvar {
1720 struct localvar *next;
1721 struct var *vp;
1722 int flags;
1723 const char *text;
1724};
1725
1726
1727#define VEXPORT 0x01
1728#define VREADONLY 0x02
1729#define VSTRFIXED 0x04
1730#define VTEXTFIXED 0x08
1731#define VSTACK 0x10
1732#define VUNSET 0x20
1733#define VNOFUNC 0x40
1734#define VNOSET 0x80
1735#define VNOSAVE 0x100
1736#if ENABLE_ASH_RANDOM_SUPPORT
1737# define VDYNAMIC 0x200
1738#else
1739# define VDYNAMIC 0
1740#endif
1741
1742
1743
1744#if ENABLE_LOCALE_SUPPORT
1745static void FAST_FUNC
1746change_lc_all(const char *value)
1747{
1748 if (value && *value != '\0')
1749 setlocale(LC_ALL, value);
1750}
1751static void FAST_FUNC
1752change_lc_ctype(const char *value)
1753{
1754 if (value && *value != '\0')
1755 setlocale(LC_CTYPE, value);
1756}
1757#endif
1758#if ENABLE_ASH_MAIL
1759static void chkmail(void);
1760static void changemail(const char *) FAST_FUNC;
1761#endif
1762static void changepath(const char *) FAST_FUNC;
1763#if ENABLE_ASH_RANDOM_SUPPORT
1764static void change_random(const char *) FAST_FUNC;
1765#endif
1766
1767static const struct {
1768 int flags;
1769 const char *var_text;
1770 void (*var_func)(const char *) FAST_FUNC;
1771} varinit_data[] = {
1772 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
1773#if ENABLE_ASH_MAIL
1774 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1775 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
1776#endif
1777 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1778 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1779 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1780 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
1781#if ENABLE_ASH_GETOPTS
1782 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
1783#endif
1784#if ENABLE_ASH_RANDOM_SUPPORT
1785 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
1786#endif
1787#if ENABLE_LOCALE_SUPPORT
1788 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1789 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
1790#endif
1791#if ENABLE_FEATURE_EDITING_SAVEHISTORY
1792 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
1793#endif
1794};
1795
1796struct redirtab;
1797
1798struct globals_var {
1799 struct shparam shellparam;
1800 struct redirtab *redirlist;
1801 int g_nullredirs;
1802 int preverrout_fd;
1803 struct var *vartab[VTABSIZE];
1804 struct var varinit[ARRAY_SIZE(varinit_data)];
1805};
1806extern struct globals_var *const ash_ptr_to_globals_var;
1807#define G_var (*ash_ptr_to_globals_var)
1808#define shellparam (G_var.shellparam )
1809
1810#define g_nullredirs (G_var.g_nullredirs )
1811#define preverrout_fd (G_var.preverrout_fd)
1812#define vartab (G_var.vartab )
1813#define varinit (G_var.varinit )
1814#define INIT_G_var() do { \
1815 unsigned i; \
1816 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1817 barrier(); \
1818 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1819 varinit[i].flags = varinit_data[i].flags; \
1820 varinit[i].var_text = varinit_data[i].var_text; \
1821 varinit[i].var_func = varinit_data[i].var_func; \
1822 } \
1823} while (0)
1824
1825#define vifs varinit[0]
1826#if ENABLE_ASH_MAIL
1827# define vmail (&vifs)[1]
1828# define vmpath (&vmail)[1]
1829# define vpath (&vmpath)[1]
1830#else
1831# define vpath (&vifs)[1]
1832#endif
1833#define vps1 (&vpath)[1]
1834#define vps2 (&vps1)[1]
1835#define vps4 (&vps2)[1]
1836#if ENABLE_ASH_GETOPTS
1837# define voptind (&vps4)[1]
1838# if ENABLE_ASH_RANDOM_SUPPORT
1839# define vrandom (&voptind)[1]
1840# endif
1841#else
1842# if ENABLE_ASH_RANDOM_SUPPORT
1843# define vrandom (&vps4)[1]
1844# endif
1845#endif
1846
1847
1848
1849
1850
1851
1852#define ifsval() (vifs.var_text + 4)
1853#define ifsset() ((vifs.flags & VUNSET) == 0)
1854#if ENABLE_ASH_MAIL
1855# define mailval() (vmail.var_text + 5)
1856# define mpathval() (vmpath.var_text + 9)
1857# define mpathset() ((vmpath.flags & VUNSET) == 0)
1858#endif
1859#define pathval() (vpath.var_text + 5)
1860#define ps1val() (vps1.var_text + 4)
1861#define ps2val() (vps2.var_text + 4)
1862#define ps4val() (vps4.var_text + 4)
1863#if ENABLE_ASH_GETOPTS
1864# define optindval() (voptind.var_text + 7)
1865#endif
1866
1867
1868#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1869#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1870
1871#if ENABLE_ASH_GETOPTS
1872static void FAST_FUNC
1873getoptsreset(const char *value)
1874{
1875 shellparam.optind = number(value);
1876 shellparam.optoff = -1;
1877}
1878#endif
1879
1880
1881
1882
1883
1884static char* FAST_FUNC
1885endofname(const char *name)
1886{
1887 char *p;
1888
1889 p = (char *) name;
1890 if (!is_name(*p))
1891 return p;
1892 while (*++p) {
1893 if (!is_in_name(*p))
1894 break;
1895 }
1896 return p;
1897}
1898
1899
1900
1901
1902
1903
1904static int
1905varcmp(const char *p, const char *q)
1906{
1907 int c, d;
1908
1909 while ((c = *p) == (d = *q)) {
1910 if (!c || c == '=')
1911 goto out;
1912 p++;
1913 q++;
1914 }
1915 if (c == '=')
1916 c = '\0';
1917 if (d == '=')
1918 d = '\0';
1919 out:
1920 return c - d;
1921}
1922
1923
1924
1925
1926static struct var **
1927hashvar(const char *p)
1928{
1929 unsigned hashval;
1930
1931 hashval = ((unsigned char) *p) << 4;
1932 while (*p && *p != '=')
1933 hashval += (unsigned char) *p++;
1934 return &vartab[hashval % VTABSIZE];
1935}
1936
1937static int
1938vpcmp(const void *a, const void *b)
1939{
1940 return varcmp(*(const char **)a, *(const char **)b);
1941}
1942
1943
1944
1945
1946static void
1947initvar(void)
1948{
1949 struct var *vp;
1950 struct var *end;
1951 struct var **vpp;
1952
1953
1954
1955
1956#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1957 vps1.var_text = "PS1=\\w \\$ ";
1958#else
1959 if (!geteuid())
1960 vps1.var_text = "PS1=# ";
1961#endif
1962 vp = varinit;
1963 end = vp + ARRAY_SIZE(varinit);
1964 do {
1965 vpp = hashvar(vp->var_text);
1966 vp->next = *vpp;
1967 *vpp = vp;
1968 } while (++vp < end);
1969}
1970
1971static struct var **
1972findvar(struct var **vpp, const char *name)
1973{
1974 for (; *vpp; vpp = &(*vpp)->next) {
1975 if (varcmp((*vpp)->var_text, name) == 0) {
1976 break;
1977 }
1978 }
1979 return vpp;
1980}
1981
1982
1983
1984
1985static const char* FAST_FUNC
1986lookupvar(const char *name)
1987{
1988 struct var *v;
1989
1990 v = *findvar(hashvar(name), name);
1991 if (v) {
1992#if ENABLE_ASH_RANDOM_SUPPORT
1993
1994
1995
1996
1997
1998
1999 if (v->flags & VDYNAMIC)
2000 v->var_func(NULL);
2001#endif
2002 if (!(v->flags & VUNSET))
2003 return var_end(v->var_text);
2004 }
2005 return NULL;
2006}
2007
2008
2009
2010
2011static const char *
2012bltinlookup(const char *name)
2013{
2014 struct strlist *sp;
2015
2016 for (sp = cmdenviron; sp; sp = sp->next) {
2017 if (varcmp(sp->text, name) == 0)
2018 return var_end(sp->text);
2019 }
2020 return lookupvar(name);
2021}
2022
2023
2024
2025
2026
2027
2028
2029
2030static void
2031setvareq(char *s, int flags)
2032{
2033 struct var *vp, **vpp;
2034
2035 vpp = hashvar(s);
2036 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2037 vp = *findvar(vpp, s);
2038 if (vp) {
2039 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2040 const char *n;
2041
2042 if (flags & VNOSAVE)
2043 free(s);
2044 n = vp->var_text;
2045 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2046 }
2047
2048 if (flags & VNOSET)
2049 return;
2050
2051 if (vp->var_func && !(flags & VNOFUNC))
2052 vp->var_func(var_end(s));
2053
2054 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2055 free((char*)vp->var_text);
2056
2057 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2058 } else {
2059
2060 if (flags & VNOSET)
2061 return;
2062 vp = ckzalloc(sizeof(*vp));
2063 vp->next = *vpp;
2064
2065 *vpp = vp;
2066 }
2067 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2068 s = ckstrdup(s);
2069 vp->var_text = s;
2070 vp->flags = flags;
2071}
2072
2073
2074
2075
2076
2077static void
2078setvar(const char *name, const char *val, int flags)
2079{
2080 char *p, *q;
2081 size_t namelen;
2082 char *nameeq;
2083 size_t vallen;
2084
2085 q = endofname(name);
2086 p = strchrnul(q, '=');
2087 namelen = p - name;
2088 if (!namelen || p != q)
2089 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2090 vallen = 0;
2091 if (val == NULL) {
2092 flags |= VUNSET;
2093 } else {
2094 vallen = strlen(val);
2095 }
2096 INT_OFF;
2097 nameeq = ckmalloc(namelen + vallen + 2);
2098 p = (char *)memcpy(nameeq, name, namelen) + namelen;
2099 if (val) {
2100 *p++ = '=';
2101 p = (char *)memcpy(p, val, vallen) + vallen;
2102 }
2103 *p = '\0';
2104 setvareq(nameeq, flags | VNOSAVE);
2105 INT_ON;
2106}
2107
2108static void FAST_FUNC
2109setvar2(const char *name, const char *val)
2110{
2111 setvar(name, val, 0);
2112}
2113
2114#if ENABLE_ASH_GETOPTS
2115
2116
2117
2118static int
2119setvarsafe(const char *name, const char *val, int flags)
2120{
2121 int err;
2122 volatile int saveint;
2123 struct jmploc *volatile savehandler = exception_handler;
2124 struct jmploc jmploc;
2125
2126 SAVE_INT(saveint);
2127 if (setjmp(jmploc.loc))
2128 err = 1;
2129 else {
2130 exception_handler = &jmploc;
2131 setvar(name, val, flags);
2132 err = 0;
2133 }
2134 exception_handler = savehandler;
2135 RESTORE_INT(saveint);
2136 return err;
2137}
2138#endif
2139
2140
2141
2142
2143static int
2144unsetvar(const char *s)
2145{
2146 struct var **vpp;
2147 struct var *vp;
2148 int retval;
2149
2150 vpp = findvar(hashvar(s), s);
2151 vp = *vpp;
2152 retval = 2;
2153 if (vp) {
2154 int flags = vp->flags;
2155
2156 retval = 1;
2157 if (flags & VREADONLY)
2158 goto out;
2159#if ENABLE_ASH_RANDOM_SUPPORT
2160 vp->flags &= ~VDYNAMIC;
2161#endif
2162 if (flags & VUNSET)
2163 goto ok;
2164 if ((flags & VSTRFIXED) == 0) {
2165 INT_OFF;
2166 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2167 free((char*)vp->var_text);
2168 *vpp = vp->next;
2169 free(vp);
2170 INT_ON;
2171 } else {
2172 setvar(s, 0, 0);
2173 vp->flags &= ~VEXPORT;
2174 }
2175 ok:
2176 retval = 0;
2177 }
2178 out:
2179 return retval;
2180}
2181
2182
2183
2184
2185static void
2186listsetvar(struct strlist *list_set_var, int flags)
2187{
2188 struct strlist *lp = list_set_var;
2189
2190 if (!lp)
2191 return;
2192 INT_OFF;
2193 do {
2194 setvareq(lp->text, flags);
2195 lp = lp->next;
2196 } while (lp);
2197 INT_ON;
2198}
2199
2200
2201
2202
2203static char **
2204listvars(int on, int off, char ***end)
2205{
2206 struct var **vpp;
2207 struct var *vp;
2208 char **ep;
2209 int mask;
2210
2211 STARTSTACKSTR(ep);
2212 vpp = vartab;
2213 mask = on | off;
2214 do {
2215 for (vp = *vpp; vp; vp = vp->next) {
2216 if ((vp->flags & mask) == on) {
2217 if (ep == stackstrend())
2218 ep = growstackstr();
2219 *ep++ = (char*)vp->var_text;
2220 }
2221 }
2222 } while (++vpp < vartab + VTABSIZE);
2223 if (ep == stackstrend())
2224 ep = growstackstr();
2225 if (end)
2226 *end = ep;
2227 *ep++ = NULL;
2228 return grabstackstr(ep);
2229}
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242static const char *pathopt;
2243
2244static char *
2245path_advance(const char **path, const char *name)
2246{
2247 const char *p;
2248 char *q;
2249 const char *start;
2250 size_t len;
2251
2252 if (*path == NULL)
2253 return NULL;
2254 start = *path;
2255 for (p = start; *p && *p != ':' && *p != '%'; p++)
2256 continue;
2257 len = p - start + strlen(name) + 2;
2258 while (stackblocksize() < len)
2259 growstackblock();
2260 q = stackblock();
2261 if (p != start) {
2262 memcpy(q, start, p - start);
2263 q += p - start;
2264 *q++ = '/';
2265 }
2266 strcpy(q, name);
2267 pathopt = NULL;
2268 if (*p == '%') {
2269 pathopt = ++p;
2270 while (*p && *p != ':')
2271 p++;
2272 }
2273 if (*p == ':')
2274 *path = p + 1;
2275 else
2276 *path = NULL;
2277 return stalloc(len);
2278}
2279
2280
2281
2282
2283static smallint doprompt;
2284static smallint needprompt;
2285
2286#if ENABLE_FEATURE_EDITING
2287static line_input_t *line_input_state;
2288static const char *cmdedit_prompt;
2289static void
2290putprompt(const char *s)
2291{
2292 if (ENABLE_ASH_EXPAND_PRMT) {
2293 free((char*)cmdedit_prompt);
2294 cmdedit_prompt = ckstrdup(s);
2295 return;
2296 }
2297 cmdedit_prompt = s;
2298}
2299#else
2300static void
2301putprompt(const char *s)
2302{
2303 out2str(s);
2304}
2305#endif
2306
2307#if ENABLE_ASH_EXPAND_PRMT
2308
2309static const char *expandstr(const char *ps);
2310#else
2311#define expandstr(s) s
2312#endif
2313
2314static void
2315setprompt(int whichprompt)
2316{
2317 const char *prompt;
2318#if ENABLE_ASH_EXPAND_PRMT
2319 struct stackmark smark;
2320#endif
2321
2322 needprompt = 0;
2323
2324 switch (whichprompt) {
2325 case 1:
2326 prompt = ps1val();
2327 break;
2328 case 2:
2329 prompt = ps2val();
2330 break;
2331 default:
2332 prompt = nullstr;
2333 }
2334#if ENABLE_ASH_EXPAND_PRMT
2335 setstackmark(&smark);
2336 stalloc(stackblocksize());
2337#endif
2338 putprompt(expandstr(prompt));
2339#if ENABLE_ASH_EXPAND_PRMT
2340 popstackmark(&smark);
2341#endif
2342}
2343
2344
2345
2346
2347#define CD_PHYSICAL 1
2348#define CD_PRINT 2
2349
2350static int
2351cdopt(void)
2352{
2353 int flags = 0;
2354 int i, j;
2355
2356 j = 'L';
2357 while ((i = nextopt("LP")) != '\0') {
2358 if (i != j) {
2359 flags ^= CD_PHYSICAL;
2360 j = i;
2361 }
2362 }
2363
2364 return flags;
2365}
2366
2367
2368
2369
2370
2371static const char *
2372updatepwd(const char *dir)
2373{
2374 char *new;
2375 char *p;
2376 char *cdcomppath;
2377 const char *lim;
2378
2379 cdcomppath = ststrdup(dir);
2380 STARTSTACKSTR(new);
2381 if (*dir != '/') {
2382 if (curdir == nullstr)
2383 return 0;
2384 new = stack_putstr(curdir, new);
2385 }
2386 new = makestrspace(strlen(dir) + 2, new);
2387 lim = (char *)stackblock() + 1;
2388 if (*dir != '/') {
2389 if (new[-1] != '/')
2390 USTPUTC('/', new);
2391 if (new > lim && *lim == '/')
2392 lim++;
2393 } else {
2394 USTPUTC('/', new);
2395 cdcomppath++;
2396 if (dir[1] == '/' && dir[2] != '/') {
2397 USTPUTC('/', new);
2398 cdcomppath++;
2399 lim++;
2400 }
2401 }
2402 p = strtok(cdcomppath, "/");
2403 while (p) {
2404 switch (*p) {
2405 case '.':
2406 if (p[1] == '.' && p[2] == '\0') {
2407 while (new > lim) {
2408 STUNPUTC(new);
2409 if (new[-1] == '/')
2410 break;
2411 }
2412 break;
2413 }
2414 if (p[1] == '\0')
2415 break;
2416
2417 default:
2418 new = stack_putstr(p, new);
2419 USTPUTC('/', new);
2420 }
2421 p = strtok(0, "/");
2422 }
2423 if (new > lim)
2424 STUNPUTC(new);
2425 *new = 0;
2426 return stackblock();
2427}
2428
2429
2430
2431
2432
2433static char *
2434getpwd(void)
2435{
2436 char *dir = getcwd(NULL, 0);
2437 return dir ? dir : nullstr;
2438}
2439
2440static void
2441setpwd(const char *val, int setold)
2442{
2443 char *oldcur, *dir;
2444
2445 oldcur = dir = curdir;
2446
2447 if (setold) {
2448 setvar("OLDPWD", oldcur, VEXPORT);
2449 }
2450 INT_OFF;
2451 if (physdir != nullstr) {
2452 if (physdir != oldcur)
2453 free(physdir);
2454 physdir = nullstr;
2455 }
2456 if (oldcur == val || !val) {
2457 char *s = getpwd();
2458 physdir = s;
2459 if (!val)
2460 dir = s;
2461 } else
2462 dir = ckstrdup(val);
2463 if (oldcur != dir && oldcur != nullstr) {
2464 free(oldcur);
2465 }
2466 curdir = dir;
2467 INT_ON;
2468 setvar("PWD", dir, VEXPORT);
2469}
2470
2471static void hashcd(void);
2472
2473
2474
2475
2476
2477static int
2478docd(const char *dest, int flags)
2479{
2480 const char *dir = NULL;
2481 int err;
2482
2483 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2484
2485 INT_OFF;
2486 if (!(flags & CD_PHYSICAL)) {
2487 dir = updatepwd(dest);
2488 if (dir)
2489 dest = dir;
2490 }
2491 err = chdir(dest);
2492 if (err)
2493 goto out;
2494 setpwd(dir, 1);
2495 hashcd();
2496 out:
2497 INT_ON;
2498 return err;
2499}
2500
2501static int FAST_FUNC
2502cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2503{
2504 const char *dest;
2505 const char *path;
2506 const char *p;
2507 char c;
2508 struct stat statb;
2509 int flags;
2510
2511 flags = cdopt();
2512 dest = *argptr;
2513 if (!dest)
2514 dest = bltinlookup("HOME");
2515 else if (LONE_DASH(dest)) {
2516 dest = bltinlookup("OLDPWD");
2517 flags |= CD_PRINT;
2518 }
2519 if (!dest)
2520 dest = nullstr;
2521 if (*dest == '/')
2522 goto step7;
2523 if (*dest == '.') {
2524 c = dest[1];
2525 dotdot:
2526 switch (c) {
2527 case '\0':
2528 case '/':
2529 goto step6;
2530 case '.':
2531 c = dest[2];
2532 if (c != '.')
2533 goto dotdot;
2534 }
2535 }
2536 if (!*dest)
2537 dest = ".";
2538 path = bltinlookup("CDPATH");
2539 if (!path) {
2540 step6:
2541 step7:
2542 p = dest;
2543 goto docd;
2544 }
2545 do {
2546 c = *path;
2547 p = path_advance(&path, dest);
2548 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2549 if (c && c != ':')
2550 flags |= CD_PRINT;
2551 docd:
2552 if (!docd(p, flags))
2553 goto out;
2554 break;
2555 }
2556 } while (path);
2557 ash_msg_and_raise_error("can't cd to %s", dest);
2558
2559 out:
2560 if (flags & CD_PRINT)
2561 out1fmt("%s\n", curdir);
2562 return 0;
2563}
2564
2565static int FAST_FUNC
2566pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2567{
2568 int flags;
2569 const char *dir = curdir;
2570
2571 flags = cdopt();
2572 if (flags) {
2573 if (physdir == nullstr)
2574 setpwd(dir, 0);
2575 dir = physdir;
2576 }
2577 out1fmt("%s\n", dir);
2578 return 0;
2579}
2580
2581
2582
2583
2584
2585#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
2586
2587
2588#define CWORD 0
2589#define CNL 1
2590#define CBACK 2
2591#define CSQUOTE 3
2592#define CDQUOTE 4
2593#define CENDQUOTE 5
2594#define CBQUOTE 6
2595#define CVAR 7
2596#define CENDVAR 8
2597#define CLP 9
2598#define CRP 10
2599#define CENDFILE 11
2600#define CCTL 12
2601#define CSPCL 13
2602#define CIGN 14
2603
2604#define PEOF 256
2605#if ENABLE_ASH_ALIAS
2606# define PEOA 257
2607#endif
2608
2609#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
2610
2611#if ENABLE_SH_MATH_SUPPORT
2612# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
2613#else
2614# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
2615#endif
2616static const uint16_t S_I_T[] = {
2617#if ENABLE_ASH_ALIAS
2618 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ),
2619#endif
2620 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ),
2621 SIT_ITEM(CNL , CNL , CNL , CNL ),
2622 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ),
2623 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ),
2624 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ),
2625 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD),
2626 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ),
2627 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ),
2628 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ),
2629 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE),
2630 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR),
2631#if !USE_SIT_FUNCTION
2632 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),
2633 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ),
2634 SIT_ITEM(CCTL , CCTL , CCTL , CCTL )
2635#endif
2636#undef SIT_ITEM
2637};
2638
2639enum {
2640#if ENABLE_ASH_ALIAS
2641 CSPCL_CIGN_CIGN_CIGN ,
2642#endif
2643 CSPCL_CWORD_CWORD_CWORD ,
2644 CNL_CNL_CNL_CNL ,
2645 CWORD_CCTL_CCTL_CWORD ,
2646 CDQUOTE_CENDQUOTE_CWORD_CWORD ,
2647 CVAR_CVAR_CWORD_CVAR ,
2648 CSQUOTE_CWORD_CENDQUOTE_CWORD ,
2649 CSPCL_CWORD_CWORD_CLP ,
2650 CSPCL_CWORD_CWORD_CRP ,
2651 CBACK_CBACK_CCTL_CBACK ,
2652 CBQUOTE_CBQUOTE_CWORD_CBQUOTE ,
2653 CENDVAR_CENDVAR_CWORD_CENDVAR ,
2654 CENDFILE_CENDFILE_CENDFILE_CENDFILE,
2655 CWORD_CWORD_CWORD_CWORD ,
2656 CCTL_CCTL_CCTL_CCTL ,
2657};
2658
2659
2660
2661
2662
2663#define BASESYNTAX 0
2664#define DQSYNTAX 1
2665#define SQSYNTAX 2
2666#define ARISYNTAX 3
2667#define PSSYNTAX 4
2668
2669#if USE_SIT_FUNCTION
2670
2671static int
2672SIT(int c, int syntax)
2673{
2674 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
2675# if ENABLE_ASH_ALIAS
2676 static const uint8_t syntax_index_table[] ALIGN1 = {
2677 1, 2, 1, 3, 4, 5, 1, 6,
2678 7, 8, 3, 3, 3, 3, 1, 1,
2679 3, 1, 3, 3, 9, 3, 10, 1,
2680 11, 3
2681 };
2682# else
2683 static const uint8_t syntax_index_table[] ALIGN1 = {
2684 0, 1, 0, 2, 3, 4, 0, 5,
2685 6, 7, 2, 2, 2, 2, 0, 0,
2686 2, 0, 2, 2, 8, 2, 9, 0,
2687 10, 2
2688 };
2689# endif
2690 const char *s;
2691 int indx;
2692
2693 if (c == PEOF)
2694 return CENDFILE;
2695# if ENABLE_ASH_ALIAS
2696 if (c == PEOA)
2697 indx = 0;
2698 else
2699# endif
2700 {
2701
2702
2703 if ((unsigned char)c >= CTL_FIRST
2704 && (unsigned char)c <= CTL_LAST
2705 ) {
2706 return CCTL;
2707 }
2708 s = strchrnul(spec_symbls, c);
2709 if (*s == '\0')
2710 return CWORD;
2711 indx = syntax_index_table[s - spec_symbls];
2712 }
2713 return (S_I_T[indx] >> (syntax*4)) & 0xf;
2714}
2715
2716#else
2717
2718static const uint8_t syntax_index_table[] = {
2719
2720 CWORD_CWORD_CWORD_CWORD,
2721 CWORD_CWORD_CWORD_CWORD,
2722 CWORD_CWORD_CWORD_CWORD,
2723 CWORD_CWORD_CWORD_CWORD,
2724 CWORD_CWORD_CWORD_CWORD,
2725 CWORD_CWORD_CWORD_CWORD,
2726 CWORD_CWORD_CWORD_CWORD,
2727 CWORD_CWORD_CWORD_CWORD,
2728 CWORD_CWORD_CWORD_CWORD,
2729 CSPCL_CWORD_CWORD_CWORD,
2730 CNL_CNL_CNL_CNL,
2731 CWORD_CWORD_CWORD_CWORD,
2732 CWORD_CWORD_CWORD_CWORD,
2733 CWORD_CWORD_CWORD_CWORD,
2734 CWORD_CWORD_CWORD_CWORD,
2735 CWORD_CWORD_CWORD_CWORD,
2736 CWORD_CWORD_CWORD_CWORD,
2737 CWORD_CWORD_CWORD_CWORD,
2738 CWORD_CWORD_CWORD_CWORD,
2739 CWORD_CWORD_CWORD_CWORD,
2740 CWORD_CWORD_CWORD_CWORD,
2741 CWORD_CWORD_CWORD_CWORD,
2742 CWORD_CWORD_CWORD_CWORD,
2743 CWORD_CWORD_CWORD_CWORD,
2744 CWORD_CWORD_CWORD_CWORD,
2745 CWORD_CWORD_CWORD_CWORD,
2746 CWORD_CWORD_CWORD_CWORD,
2747 CWORD_CWORD_CWORD_CWORD,
2748 CWORD_CWORD_CWORD_CWORD,
2749 CWORD_CWORD_CWORD_CWORD,
2750 CWORD_CWORD_CWORD_CWORD,
2751 CWORD_CWORD_CWORD_CWORD,
2752 CSPCL_CWORD_CWORD_CWORD,
2753 CWORD_CCTL_CCTL_CWORD,
2754 CDQUOTE_CENDQUOTE_CWORD_CWORD,
2755 CWORD_CWORD_CWORD_CWORD,
2756 CVAR_CVAR_CWORD_CVAR,
2757 CWORD_CWORD_CWORD_CWORD,
2758 CSPCL_CWORD_CWORD_CWORD,
2759 CSQUOTE_CWORD_CENDQUOTE_CWORD,
2760 CSPCL_CWORD_CWORD_CLP,
2761 CSPCL_CWORD_CWORD_CRP,
2762 CWORD_CCTL_CCTL_CWORD,
2763 CWORD_CWORD_CWORD_CWORD,
2764 CWORD_CWORD_CWORD_CWORD,
2765 CWORD_CCTL_CCTL_CWORD,
2766 CWORD_CWORD_CWORD_CWORD,
2767 CWORD_CCTL_CCTL_CWORD,
2768 CWORD_CWORD_CWORD_CWORD,
2769 CWORD_CWORD_CWORD_CWORD,
2770 CWORD_CWORD_CWORD_CWORD,
2771 CWORD_CWORD_CWORD_CWORD,
2772 CWORD_CWORD_CWORD_CWORD,
2773 CWORD_CWORD_CWORD_CWORD,
2774 CWORD_CWORD_CWORD_CWORD,
2775 CWORD_CWORD_CWORD_CWORD,
2776 CWORD_CWORD_CWORD_CWORD,
2777 CWORD_CWORD_CWORD_CWORD,
2778 CWORD_CCTL_CCTL_CWORD,
2779 CSPCL_CWORD_CWORD_CWORD,
2780 CSPCL_CWORD_CWORD_CWORD,
2781 CWORD_CCTL_CCTL_CWORD,
2782 CSPCL_CWORD_CWORD_CWORD,
2783 CWORD_CCTL_CCTL_CWORD,
2784 CWORD_CWORD_CWORD_CWORD,
2785 CWORD_CWORD_CWORD_CWORD,
2786 CWORD_CWORD_CWORD_CWORD,
2787 CWORD_CWORD_CWORD_CWORD,
2788 CWORD_CWORD_CWORD_CWORD,
2789 CWORD_CWORD_CWORD_CWORD,
2790 CWORD_CWORD_CWORD_CWORD,
2791 CWORD_CWORD_CWORD_CWORD,
2792 CWORD_CWORD_CWORD_CWORD,
2793 CWORD_CWORD_CWORD_CWORD,
2794 CWORD_CWORD_CWORD_CWORD,
2795 CWORD_CWORD_CWORD_CWORD,
2796 CWORD_CWORD_CWORD_CWORD,
2797 CWORD_CWORD_CWORD_CWORD,
2798 CWORD_CWORD_CWORD_CWORD,
2799 CWORD_CWORD_CWORD_CWORD,
2800 CWORD_CWORD_CWORD_CWORD,
2801 CWORD_CWORD_CWORD_CWORD,
2802 CWORD_CWORD_CWORD_CWORD,
2803 CWORD_CWORD_CWORD_CWORD,
2804 CWORD_CWORD_CWORD_CWORD,
2805 CWORD_CWORD_CWORD_CWORD,
2806 CWORD_CWORD_CWORD_CWORD,
2807 CWORD_CWORD_CWORD_CWORD,
2808 CWORD_CWORD_CWORD_CWORD,
2809 CWORD_CWORD_CWORD_CWORD,
2810 CWORD_CWORD_CWORD_CWORD,
2811 CWORD_CCTL_CCTL_CWORD,
2812 CBACK_CBACK_CCTL_CBACK,
2813 CWORD_CCTL_CCTL_CWORD,
2814 CWORD_CWORD_CWORD_CWORD,
2815 CWORD_CWORD_CWORD_CWORD,
2816 CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2817 CWORD_CWORD_CWORD_CWORD,
2818 CWORD_CWORD_CWORD_CWORD,
2819 CWORD_CWORD_CWORD_CWORD,
2820 CWORD_CWORD_CWORD_CWORD,
2821 CWORD_CWORD_CWORD_CWORD,
2822 CWORD_CWORD_CWORD_CWORD,
2823 CWORD_CWORD_CWORD_CWORD,
2824 CWORD_CWORD_CWORD_CWORD,
2825 CWORD_CWORD_CWORD_CWORD,
2826 CWORD_CWORD_CWORD_CWORD,
2827 CWORD_CWORD_CWORD_CWORD,
2828 CWORD_CWORD_CWORD_CWORD,
2829 CWORD_CWORD_CWORD_CWORD,
2830 CWORD_CWORD_CWORD_CWORD,
2831 CWORD_CWORD_CWORD_CWORD,
2832 CWORD_CWORD_CWORD_CWORD,
2833 CWORD_CWORD_CWORD_CWORD,
2834 CWORD_CWORD_CWORD_CWORD,
2835 CWORD_CWORD_CWORD_CWORD,
2836 CWORD_CWORD_CWORD_CWORD,
2837 CWORD_CWORD_CWORD_CWORD,
2838 CWORD_CWORD_CWORD_CWORD,
2839 CWORD_CWORD_CWORD_CWORD,
2840 CWORD_CWORD_CWORD_CWORD,
2841 CWORD_CWORD_CWORD_CWORD,
2842 CWORD_CWORD_CWORD_CWORD,
2843 CWORD_CWORD_CWORD_CWORD,
2844 CSPCL_CWORD_CWORD_CWORD,
2845 CENDVAR_CENDVAR_CWORD_CENDVAR,
2846 CWORD_CCTL_CCTL_CWORD,
2847 CWORD_CWORD_CWORD_CWORD,
2848 CWORD_CWORD_CWORD_CWORD,
2849 CCTL_CCTL_CCTL_CCTL,
2850 CCTL_CCTL_CCTL_CCTL,
2851 CCTL_CCTL_CCTL_CCTL,
2852 CCTL_CCTL_CCTL_CCTL,
2853 CCTL_CCTL_CCTL_CCTL,
2854 CCTL_CCTL_CCTL_CCTL,
2855 CCTL_CCTL_CCTL_CCTL,
2856 CCTL_CCTL_CCTL_CCTL,
2857 CWORD_CWORD_CWORD_CWORD,
2858 CWORD_CWORD_CWORD_CWORD,
2859 CWORD_CWORD_CWORD_CWORD,
2860 CWORD_CWORD_CWORD_CWORD,
2861 CWORD_CWORD_CWORD_CWORD,
2862 CWORD_CWORD_CWORD_CWORD,
2863 CWORD_CWORD_CWORD_CWORD,
2864 CWORD_CWORD_CWORD_CWORD,
2865 CWORD_CWORD_CWORD_CWORD,
2866 CWORD_CWORD_CWORD_CWORD,
2867 CWORD_CWORD_CWORD_CWORD,
2868 CWORD_CWORD_CWORD_CWORD,
2869 CWORD_CWORD_CWORD_CWORD,
2870 CWORD_CWORD_CWORD_CWORD,
2871 CWORD_CWORD_CWORD_CWORD,
2872 CWORD_CWORD_CWORD_CWORD,
2873 CWORD_CWORD_CWORD_CWORD,
2874 CWORD_CWORD_CWORD_CWORD,
2875 CWORD_CWORD_CWORD_CWORD,
2876 CWORD_CWORD_CWORD_CWORD,
2877 CWORD_CWORD_CWORD_CWORD,
2878 CWORD_CWORD_CWORD_CWORD,
2879 CWORD_CWORD_CWORD_CWORD,
2880 CWORD_CWORD_CWORD_CWORD,
2881 CWORD_CWORD_CWORD_CWORD,
2882 CWORD_CWORD_CWORD_CWORD,
2883 CWORD_CWORD_CWORD_CWORD,
2884 CWORD_CWORD_CWORD_CWORD,
2885 CWORD_CWORD_CWORD_CWORD,
2886 CWORD_CWORD_CWORD_CWORD,
2887 CWORD_CWORD_CWORD_CWORD,
2888 CWORD_CWORD_CWORD_CWORD,
2889 CWORD_CWORD_CWORD_CWORD,
2890 CWORD_CWORD_CWORD_CWORD,
2891 CWORD_CWORD_CWORD_CWORD,
2892 CWORD_CWORD_CWORD_CWORD,
2893 CWORD_CWORD_CWORD_CWORD,
2894 CWORD_CWORD_CWORD_CWORD,
2895 CWORD_CWORD_CWORD_CWORD,
2896 CWORD_CWORD_CWORD_CWORD,
2897 CWORD_CWORD_CWORD_CWORD,
2898 CWORD_CWORD_CWORD_CWORD,
2899 CWORD_CWORD_CWORD_CWORD,
2900 CWORD_CWORD_CWORD_CWORD,
2901 CWORD_CWORD_CWORD_CWORD,
2902 CWORD_CWORD_CWORD_CWORD,
2903 CWORD_CWORD_CWORD_CWORD,
2904 CWORD_CWORD_CWORD_CWORD,
2905 CWORD_CWORD_CWORD_CWORD,
2906 CWORD_CWORD_CWORD_CWORD,
2907 CWORD_CWORD_CWORD_CWORD,
2908 CWORD_CWORD_CWORD_CWORD,
2909 CWORD_CWORD_CWORD_CWORD,
2910 CWORD_CWORD_CWORD_CWORD,
2911 CWORD_CWORD_CWORD_CWORD,
2912 CWORD_CWORD_CWORD_CWORD,
2913 CWORD_CWORD_CWORD_CWORD,
2914 CWORD_CWORD_CWORD_CWORD,
2915 CWORD_CWORD_CWORD_CWORD,
2916 CWORD_CWORD_CWORD_CWORD,
2917 CWORD_CWORD_CWORD_CWORD,
2918 CWORD_CWORD_CWORD_CWORD,
2919 CWORD_CWORD_CWORD_CWORD,
2920 CWORD_CWORD_CWORD_CWORD,
2921 CWORD_CWORD_CWORD_CWORD,
2922 CWORD_CWORD_CWORD_CWORD,
2923 CWORD_CWORD_CWORD_CWORD,
2924 CWORD_CWORD_CWORD_CWORD,
2925 CWORD_CWORD_CWORD_CWORD,
2926 CWORD_CWORD_CWORD_CWORD,
2927 CWORD_CWORD_CWORD_CWORD,
2928 CWORD_CWORD_CWORD_CWORD,
2929 CWORD_CWORD_CWORD_CWORD,
2930 CWORD_CWORD_CWORD_CWORD,
2931 CWORD_CWORD_CWORD_CWORD,
2932 CWORD_CWORD_CWORD_CWORD,
2933 CWORD_CWORD_CWORD_CWORD,
2934 CWORD_CWORD_CWORD_CWORD,
2935 CWORD_CWORD_CWORD_CWORD,
2936 CWORD_CWORD_CWORD_CWORD,
2937 CWORD_CWORD_CWORD_CWORD,
2938 CWORD_CWORD_CWORD_CWORD,
2939 CWORD_CWORD_CWORD_CWORD,
2940 CWORD_CWORD_CWORD_CWORD,
2941 CWORD_CWORD_CWORD_CWORD,
2942 CWORD_CWORD_CWORD_CWORD,
2943 CWORD_CWORD_CWORD_CWORD,
2944 CWORD_CWORD_CWORD_CWORD,
2945 CWORD_CWORD_CWORD_CWORD,
2946 CWORD_CWORD_CWORD_CWORD,
2947 CWORD_CWORD_CWORD_CWORD,
2948 CWORD_CWORD_CWORD_CWORD,
2949 CWORD_CWORD_CWORD_CWORD,
2950 CWORD_CWORD_CWORD_CWORD,
2951 CWORD_CWORD_CWORD_CWORD,
2952 CWORD_CWORD_CWORD_CWORD,
2953 CWORD_CWORD_CWORD_CWORD,
2954 CWORD_CWORD_CWORD_CWORD,
2955 CWORD_CWORD_CWORD_CWORD,
2956 CWORD_CWORD_CWORD_CWORD,
2957 CWORD_CWORD_CWORD_CWORD,
2958 CWORD_CWORD_CWORD_CWORD,
2959 CWORD_CWORD_CWORD_CWORD,
2960 CWORD_CWORD_CWORD_CWORD,
2961 CWORD_CWORD_CWORD_CWORD,
2962 CWORD_CWORD_CWORD_CWORD,
2963 CWORD_CWORD_CWORD_CWORD,
2964 CWORD_CWORD_CWORD_CWORD,
2965 CWORD_CWORD_CWORD_CWORD,
2966 CWORD_CWORD_CWORD_CWORD,
2967 CWORD_CWORD_CWORD_CWORD,
2968 CWORD_CWORD_CWORD_CWORD,
2969 CWORD_CWORD_CWORD_CWORD,
2970 CWORD_CWORD_CWORD_CWORD,
2971 CWORD_CWORD_CWORD_CWORD,
2972 CWORD_CWORD_CWORD_CWORD,
2973 CWORD_CWORD_CWORD_CWORD,
2974 CWORD_CWORD_CWORD_CWORD,
2975 CWORD_CWORD_CWORD_CWORD,
2976 CENDFILE_CENDFILE_CENDFILE_CENDFILE,
2977# if ENABLE_ASH_ALIAS
2978 CSPCL_CIGN_CIGN_CIGN,
2979# endif
2980};
2981
2982# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
2983
2984#endif
2985
2986
2987
2988
2989#if ENABLE_ASH_ALIAS
2990
2991#define ALIASINUSE 1
2992#define ALIASDEAD 2
2993
2994struct alias {
2995 struct alias *next;
2996 char *name;
2997 char *val;
2998 int flag;
2999};
3000
3001
3002static struct alias **atab;
3003#define INIT_G_alias() do { \
3004 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3005} while (0)
3006
3007
3008static struct alias **
3009__lookupalias(const char *name) {
3010 unsigned int hashval;
3011 struct alias **app;
3012 const char *p;
3013 unsigned int ch;
3014
3015 p = name;
3016
3017 ch = (unsigned char)*p;
3018 hashval = ch << 4;
3019 while (ch) {
3020 hashval += ch;
3021 ch = (unsigned char)*++p;
3022 }
3023 app = &atab[hashval % ATABSIZE];
3024
3025 for (; *app; app = &(*app)->next) {
3026 if (strcmp(name, (*app)->name) == 0) {
3027 break;
3028 }
3029 }
3030
3031 return app;
3032}
3033
3034static struct alias *
3035lookupalias(const char *name, int check)
3036{
3037 struct alias *ap = *__lookupalias(name);
3038
3039 if (check && ap && (ap->flag & ALIASINUSE))
3040 return NULL;
3041 return ap;
3042}
3043
3044static struct alias *
3045freealias(struct alias *ap)
3046{
3047 struct alias *next;
3048
3049 if (ap->flag & ALIASINUSE) {
3050 ap->flag |= ALIASDEAD;
3051 return ap;
3052 }
3053
3054 next = ap->next;
3055 free(ap->name);
3056 free(ap->val);
3057 free(ap);
3058 return next;
3059}
3060
3061static void
3062setalias(const char *name, const char *val)
3063{
3064 struct alias *ap, **app;
3065
3066 app = __lookupalias(name);
3067 ap = *app;
3068 INT_OFF;
3069 if (ap) {
3070 if (!(ap->flag & ALIASINUSE)) {
3071 free(ap->val);
3072 }
3073 ap->val = ckstrdup(val);
3074 ap->flag &= ~ALIASDEAD;
3075 } else {
3076
3077 ap = ckzalloc(sizeof(struct alias));
3078 ap->name = ckstrdup(name);
3079 ap->val = ckstrdup(val);
3080
3081
3082 *app = ap;
3083 }
3084 INT_ON;
3085}
3086
3087static int
3088unalias(const char *name)
3089{
3090 struct alias **app;
3091
3092 app = __lookupalias(name);
3093
3094 if (*app) {
3095 INT_OFF;
3096 *app = freealias(*app);
3097 INT_ON;
3098 return 0;
3099 }
3100
3101 return 1;
3102}
3103
3104static void
3105rmaliases(void)
3106{
3107 struct alias *ap, **app;
3108 int i;
3109
3110 INT_OFF;
3111 for (i = 0; i < ATABSIZE; i++) {
3112 app = &atab[i];
3113 for (ap = *app; ap; ap = *app) {
3114 *app = freealias(*app);
3115 if (ap == *app) {
3116 app = &ap->next;
3117 }
3118 }
3119 }
3120 INT_ON;
3121}
3122
3123static void
3124printalias(const struct alias *ap)
3125{
3126 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3127}
3128
3129
3130
3131
3132static int FAST_FUNC
3133aliascmd(int argc UNUSED_PARAM, char **argv)
3134{
3135 char *n, *v;
3136 int ret = 0;
3137 struct alias *ap;
3138
3139 if (!argv[1]) {
3140 int i;
3141
3142 for (i = 0; i < ATABSIZE; i++) {
3143 for (ap = atab[i]; ap; ap = ap->next) {
3144 printalias(ap);
3145 }
3146 }
3147 return 0;
3148 }
3149 while ((n = *++argv) != NULL) {
3150 v = strchr(n+1, '=');
3151 if (v == NULL) {
3152 ap = *__lookupalias(n);
3153 if (ap == NULL) {
3154 fprintf(stderr, "%s: %s not found\n", "alias", n);
3155 ret = 1;
3156 } else
3157 printalias(ap);
3158 } else {
3159 *v++ = '\0';
3160 setalias(n, v);
3161 }
3162 }
3163
3164 return ret;
3165}
3166
3167static int FAST_FUNC
3168unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3169{
3170 int i;
3171
3172 while ((i = nextopt("a")) != '\0') {
3173 if (i == 'a') {
3174 rmaliases();
3175 return 0;
3176 }
3177 }
3178 for (i = 0; *argptr; argptr++) {
3179 if (unalias(*argptr)) {
3180 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
3181 i = 1;
3182 }
3183 }
3184
3185 return i;
3186}
3187
3188#endif
3189
3190
3191
3192
3193
3194#define FORK_FG 0
3195#define FORK_BG 1
3196#define FORK_NOJOB 2
3197
3198
3199#define SHOW_ONLY_PGID 0x01
3200#define SHOW_PIDS 0x02
3201#define SHOW_CHANGED 0x04
3202
3203
3204
3205
3206
3207
3208
3209struct procstat {
3210 pid_t ps_pid;
3211 int ps_status;
3212 char *ps_cmd;
3213};
3214
3215struct job {
3216 struct procstat ps0;
3217 struct procstat *ps;
3218#if JOBS
3219 int stopstatus;
3220#endif
3221 uint32_t
3222 nprocs: 16,
3223 state: 8,
3224#define JOBRUNNING 0
3225#define JOBSTOPPED 1
3226#define JOBDONE 2
3227#if JOBS
3228 sigint: 1,
3229 jobctl: 1,
3230#endif
3231 waited: 1,
3232 used: 1,
3233 changed: 1;
3234 struct job *prev_job;
3235};
3236
3237static struct job *makejob( int);
3238static int forkshell(struct job *, union node *, int);
3239static int waitforjob(struct job *);
3240
3241#if !JOBS
3242enum { doing_jobctl = 0 };
3243#define setjobctl(on) do {} while (0)
3244#else
3245static smallint doing_jobctl;
3246static void setjobctl(int);
3247#endif
3248
3249
3250
3251
3252static void
3253ignoresig(int signo)
3254{
3255
3256 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3257
3258 signal(signo, SIG_IGN);
3259 }
3260 sigmode[signo - 1] = S_HARD_IGN;
3261}
3262
3263
3264
3265
3266static void
3267signal_handler(int signo)
3268{
3269 gotsig[signo - 1] = 1;
3270
3271 if (signo == SIGINT && !trap[SIGINT]) {
3272 if (!suppress_int) {
3273 pending_sig = 0;
3274 raise_interrupt();
3275 }
3276 pending_int = 1;
3277 } else {
3278 pending_sig = signo;
3279 }
3280}
3281
3282
3283
3284
3285
3286static void
3287setsignal(int signo)
3288{
3289 char *t;
3290 char cur_act, new_act;
3291 struct sigaction act;
3292
3293 t = trap[signo];
3294 new_act = S_DFL;
3295 if (t != NULL) {
3296 new_act = S_CATCH;
3297 if (t[0] == '\0')
3298 new_act = S_IGN;
3299 }
3300
3301 if (rootshell && new_act == S_DFL) {
3302 switch (signo) {
3303 case SIGINT:
3304 if (iflag || minusc || sflag == 0)
3305 new_act = S_CATCH;
3306 break;
3307 case SIGQUIT:
3308#if DEBUG
3309 if (debug)
3310 break;
3311#endif
3312
3313
3314
3315
3316
3317 new_act = S_IGN;
3318 break;
3319 case SIGTERM:
3320 if (iflag)
3321 new_act = S_IGN;
3322 break;
3323#if JOBS
3324 case SIGTSTP:
3325 case SIGTTOU:
3326 if (mflag)
3327 new_act = S_IGN;
3328 break;
3329#endif
3330 }
3331 }
3332
3333
3334
3335
3336 t = &sigmode[signo - 1];
3337 cur_act = *t;
3338 if (cur_act == 0) {
3339
3340 if (sigaction(signo, NULL, &act)) {
3341
3342
3343
3344
3345 return;
3346 }
3347 if (act.sa_handler == SIG_IGN) {
3348 cur_act = S_HARD_IGN;
3349 if (mflag
3350 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3351 ) {
3352 cur_act = S_IGN;
3353 }
3354 }
3355 }
3356 if (cur_act == S_HARD_IGN || cur_act == new_act)
3357 return;
3358
3359 act.sa_handler = SIG_DFL;
3360 switch (new_act) {
3361 case S_CATCH:
3362 act.sa_handler = signal_handler;
3363 act.sa_flags = 0;
3364 sigfillset(&act.sa_mask);
3365 break;
3366 case S_IGN:
3367 act.sa_handler = SIG_IGN;
3368 break;
3369 }
3370 sigaction_set(signo, &act);
3371
3372 *t = new_act;
3373}
3374
3375
3376#define CUR_DELETE 2
3377#define CUR_RUNNING 1
3378#define CUR_STOPPED 0
3379
3380
3381#define DOWAIT_NONBLOCK WNOHANG
3382#define DOWAIT_BLOCK 0
3383
3384#if JOBS
3385
3386static int initialpgrp;
3387static int ttyfd = -1;
3388#endif
3389
3390static struct job *jobtab;
3391
3392static unsigned njobs;
3393
3394static struct job *curjob;
3395
3396static int jobless;
3397
3398static void
3399set_curjob(struct job *jp, unsigned mode)
3400{
3401 struct job *jp1;
3402 struct job **jpp, **curp;
3403
3404
3405 jpp = curp = &curjob;
3406 do {
3407 jp1 = *jpp;
3408 if (jp1 == jp)
3409 break;
3410 jpp = &jp1->prev_job;
3411 } while (1);
3412 *jpp = jp1->prev_job;
3413
3414
3415 jpp = curp;
3416 switch (mode) {
3417 default:
3418#if DEBUG
3419 abort();
3420#endif
3421 case CUR_DELETE:
3422
3423 break;
3424 case CUR_RUNNING:
3425
3426
3427 do {
3428 jp1 = *jpp;
3429#if JOBS
3430 if (!jp1 || jp1->state != JOBSTOPPED)
3431#endif
3432 break;
3433 jpp = &jp1->prev_job;
3434 } while (1);
3435
3436#if JOBS
3437 case CUR_STOPPED:
3438#endif
3439
3440 jp->prev_job = *jpp;
3441 *jpp = jp;
3442 break;
3443 }
3444}
3445
3446#if JOBS || DEBUG
3447static int
3448jobno(const struct job *jp)
3449{
3450 return jp - jobtab + 1;
3451}
3452#endif
3453
3454
3455
3456
3457#if !JOBS
3458#define getjob(name, getctl) getjob(name)
3459#endif
3460static struct job *
3461getjob(const char *name, int getctl)
3462{
3463 struct job *jp;
3464 struct job *found;
3465 const char *err_msg = "%s: no such job";
3466 unsigned num;
3467 int c;
3468 const char *p;
3469 char *(*match)(const char *, const char *);
3470
3471 jp = curjob;
3472 p = name;
3473 if (!p)
3474 goto currentjob;
3475
3476 if (*p != '%')
3477 goto err;
3478
3479 c = *++p;
3480 if (!c)
3481 goto currentjob;
3482
3483 if (!p[1]) {
3484 if (c == '+' || c == '%') {
3485 currentjob:
3486 err_msg = "No current job";
3487 goto check;
3488 }
3489 if (c == '-') {
3490 if (jp)
3491 jp = jp->prev_job;
3492 err_msg = "No previous job";
3493 check:
3494 if (!jp)
3495 goto err;
3496 goto gotit;
3497 }
3498 }
3499
3500 if (is_number(p)) {
3501 num = atoi(p);
3502 if (num < njobs) {
3503 jp = jobtab + num - 1;
3504 if (jp->used)
3505 goto gotit;
3506 goto err;
3507 }
3508 }
3509
3510 match = prefix;
3511 if (*p == '?') {
3512 match = strstr;
3513 p++;
3514 }
3515
3516 found = NULL;
3517 while (jp) {
3518 if (match(jp->ps[0].ps_cmd, p)) {
3519 if (found)
3520 goto err;
3521 found = jp;
3522 err_msg = "%s: ambiguous";
3523 }
3524 jp = jp->prev_job;
3525 }
3526 if (!found)
3527 goto err;
3528 jp = found;
3529
3530 gotit:
3531#if JOBS
3532 err_msg = "job %s not created under job control";
3533 if (getctl && jp->jobctl == 0)
3534 goto err;
3535#endif
3536 return jp;
3537 err:
3538 ash_msg_and_raise_error(err_msg, name);
3539}
3540
3541
3542
3543
3544static void
3545freejob(struct job *jp)
3546{
3547 struct procstat *ps;
3548 int i;
3549
3550 INT_OFF;
3551 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3552 if (ps->ps_cmd != nullstr)
3553 free(ps->ps_cmd);
3554 }
3555 if (jp->ps != &jp->ps0)
3556 free(jp->ps);
3557 jp->used = 0;
3558 set_curjob(jp, CUR_DELETE);
3559 INT_ON;
3560}
3561
3562#if JOBS
3563static void
3564xtcsetpgrp(int fd, pid_t pgrp)
3565{
3566 if (tcsetpgrp(fd, pgrp))
3567 ash_msg_and_raise_error("can't set tty process group (%m)");
3568}
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579static void
3580setjobctl(int on)
3581{
3582 int fd;
3583 int pgrp;
3584
3585 if (on == doing_jobctl || rootshell == 0)
3586 return;
3587 if (on) {
3588 int ofd;
3589 ofd = fd = open(_PATH_TTY, O_RDWR);
3590 if (fd < 0) {
3591
3592
3593
3594
3595 fd = 2;
3596 while (!isatty(fd))
3597 if (--fd < 0)
3598 goto out;
3599 }
3600 fd = fcntl(fd, F_DUPFD, 10);
3601 if (ofd >= 0)
3602 close(ofd);
3603 if (fd < 0)
3604 goto out;
3605
3606 close_on_exec_on(fd);
3607 do {
3608 pgrp = tcgetpgrp(fd);
3609 if (pgrp < 0) {
3610 out:
3611 ash_msg("can't access tty; job control turned off");
3612 mflag = on = 0;
3613 goto close;
3614 }
3615 if (pgrp == getpgrp())
3616 break;
3617 killpg(0, SIGTTIN);
3618 } while (1);
3619 initialpgrp = pgrp;
3620
3621 setsignal(SIGTSTP);
3622 setsignal(SIGTTOU);
3623 setsignal(SIGTTIN);
3624 pgrp = rootpid;
3625 setpgid(0, pgrp);
3626 xtcsetpgrp(fd, pgrp);
3627 } else {
3628
3629 fd = ttyfd;
3630 pgrp = initialpgrp;
3631
3632
3633 tcsetpgrp(fd, pgrp);
3634 setpgid(0, pgrp);
3635 setsignal(SIGTSTP);
3636 setsignal(SIGTTOU);
3637 setsignal(SIGTTIN);
3638 close:
3639 if (fd >= 0)
3640 close(fd);
3641 fd = -1;
3642 }
3643 ttyfd = fd;
3644 doing_jobctl = on;
3645}
3646
3647static int FAST_FUNC
3648killcmd(int argc, char **argv)
3649{
3650 int i = 1;
3651 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3652 do {
3653 if (argv[i][0] == '%') {
3654 struct job *jp = getjob(argv[i], 0);
3655 unsigned pid = jp->ps[0].ps_pid;
3656
3657 argv[i] = alloca(sizeof(int)*3 + 3);
3658
3659
3660
3661 sprintf(argv[i], " -%u", pid);
3662 }
3663 } while (argv[++i]);
3664 }
3665 return kill_main(argc, argv);
3666}
3667
3668static void
3669showpipe(struct job *jp )
3670{
3671 struct procstat *ps;
3672 struct procstat *psend;
3673
3674 psend = jp->ps + jp->nprocs;
3675 for (ps = jp->ps + 1; ps < psend; ps++)
3676 printf(" | %s", ps->ps_cmd);
3677 outcslow('\n', stdout);
3678 flush_stdout_stderr();
3679}
3680
3681
3682static int
3683restartjob(struct job *jp, int mode)
3684{
3685 struct procstat *ps;
3686 int i;
3687 int status;
3688 pid_t pgid;
3689
3690 INT_OFF;
3691 if (jp->state == JOBDONE)
3692 goto out;
3693 jp->state = JOBRUNNING;
3694 pgid = jp->ps[0].ps_pid;
3695 if (mode == FORK_FG)
3696 xtcsetpgrp(ttyfd, pgid);
3697 killpg(pgid, SIGCONT);
3698 ps = jp->ps;
3699 i = jp->nprocs;
3700 do {
3701 if (WIFSTOPPED(ps->ps_status)) {
3702 ps->ps_status = -1;
3703 }
3704 ps++;
3705 } while (--i);
3706 out:
3707 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3708 INT_ON;
3709 return status;
3710}
3711
3712static int FAST_FUNC
3713fg_bgcmd(int argc UNUSED_PARAM, char **argv)
3714{
3715 struct job *jp;
3716 int mode;
3717 int retval;
3718
3719 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3720 nextopt(nullstr);
3721 argv = argptr;
3722 do {
3723 jp = getjob(*argv, 1);
3724 if (mode == FORK_BG) {
3725 set_curjob(jp, CUR_RUNNING);
3726 printf("[%d] ", jobno(jp));
3727 }
3728 out1str(jp->ps[0].ps_cmd);
3729 showpipe(jp );
3730 retval = restartjob(jp, mode);
3731 } while (*argv && *++argv);
3732 return retval;
3733}
3734#endif
3735
3736static int
3737sprint_status(char *s, int status, int sigonly)
3738{
3739 int col;
3740 int st;
3741
3742 col = 0;
3743 if (!WIFEXITED(status)) {
3744#if JOBS
3745 if (WIFSTOPPED(status))
3746 st = WSTOPSIG(status);
3747 else
3748#endif
3749 st = WTERMSIG(status);
3750 if (sigonly) {
3751 if (st == SIGINT || st == SIGPIPE)
3752 goto out;
3753#if JOBS
3754 if (WIFSTOPPED(status))
3755 goto out;
3756#endif
3757 }
3758 st &= 0x7f;
3759 col = fmtstr(s, 32, strsignal(st));
3760 if (WCOREDUMP(status)) {
3761 col += fmtstr(s + col, 16, " (core dumped)");
3762 }
3763 } else if (!sigonly) {
3764 st = WEXITSTATUS(status);
3765 if (st)
3766 col = fmtstr(s, 16, "Done(%d)", st);
3767 else
3768 col = fmtstr(s, 16, "Done");
3769 }
3770 out:
3771 return col;
3772}
3773
3774static int
3775dowait(int wait_flags, struct job *job)
3776{
3777 int pid;
3778 int status;
3779 struct job *jp;
3780 struct job *thisjob;
3781 int state;
3782
3783 TRACE(("dowait(0x%x) called\n", wait_flags));
3784
3785
3786
3787
3788 if (doing_jobctl)
3789 wait_flags |= WUNTRACED;
3790 pid = waitpid(-1, &status, wait_flags);
3791 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3792 pid, status, errno, strerror(errno)));
3793 if (pid <= 0)
3794 return pid;
3795
3796 INT_OFF;
3797 thisjob = NULL;
3798 for (jp = curjob; jp; jp = jp->prev_job) {
3799 struct procstat *ps;
3800 struct procstat *psend;
3801 if (jp->state == JOBDONE)
3802 continue;
3803 state = JOBDONE;
3804 ps = jp->ps;
3805 psend = ps + jp->nprocs;
3806 do {
3807 if (ps->ps_pid == pid) {
3808 TRACE(("Job %d: changing status of proc %d "
3809 "from 0x%x to 0x%x\n",
3810 jobno(jp), pid, ps->ps_status, status));
3811 ps->ps_status = status;
3812 thisjob = jp;
3813 }
3814 if (ps->ps_status == -1)
3815 state = JOBRUNNING;
3816#if JOBS
3817 if (state == JOBRUNNING)
3818 continue;
3819 if (WIFSTOPPED(ps->ps_status)) {
3820 jp->stopstatus = ps->ps_status;
3821 state = JOBSTOPPED;
3822 }
3823#endif
3824 } while (++ps < psend);
3825 if (thisjob)
3826 goto gotjob;
3827 }
3828#if JOBS
3829 if (!WIFSTOPPED(status))
3830#endif
3831 jobless--;
3832 goto out;
3833
3834 gotjob:
3835 if (state != JOBRUNNING) {
3836 thisjob->changed = 1;
3837
3838 if (thisjob->state != state) {
3839 TRACE(("Job %d: changing state from %d to %d\n",
3840 jobno(thisjob), thisjob->state, state));
3841 thisjob->state = state;
3842#if JOBS
3843 if (state == JOBSTOPPED) {
3844 set_curjob(thisjob, CUR_STOPPED);
3845 }
3846#endif
3847 }
3848 }
3849
3850 out:
3851 INT_ON;
3852
3853 if (thisjob && thisjob == job) {
3854 char s[48 + 1];
3855 int len;
3856
3857 len = sprint_status(s, status, 1);
3858 if (len) {
3859 s[len] = '\n';
3860 s[len + 1] = '\0';
3861 out2str(s);
3862 }
3863 }
3864 return pid;
3865}
3866
3867static int
3868blocking_wait_with_raise_on_sig(void)
3869{
3870 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
3871 if (pid <= 0 && pending_sig)
3872 raise_exception(EXSIG);
3873 return pid;
3874}
3875
3876#if JOBS
3877static void
3878showjob(FILE *out, struct job *jp, int mode)
3879{
3880 struct procstat *ps;
3881 struct procstat *psend;
3882 int col;
3883 int indent_col;
3884 char s[80];
3885
3886 ps = jp->ps;
3887
3888 if (mode & SHOW_ONLY_PGID) {
3889
3890 fprintf(out, "%d\n", ps->ps_pid);
3891 return;
3892 }
3893
3894 col = fmtstr(s, 16, "[%d] ", jobno(jp));
3895 indent_col = col;
3896
3897 if (jp == curjob)
3898 s[col - 3] = '+';
3899 else if (curjob && jp == curjob->prev_job)
3900 s[col - 3] = '-';
3901
3902 if (mode & SHOW_PIDS)
3903 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
3904
3905 psend = ps + jp->nprocs;
3906
3907 if (jp->state == JOBRUNNING) {
3908 strcpy(s + col, "Running");
3909 col += sizeof("Running") - 1;
3910 } else {
3911 int status = psend[-1].ps_status;
3912 if (jp->state == JOBSTOPPED)
3913 status = jp->stopstatus;
3914 col += sprint_status(s + col, status, 0);
3915 }
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926 goto start;
3927 do {
3928
3929 s[0] = '\0';
3930 col = 33;
3931 if (mode & SHOW_PIDS)
3932 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
3933 start:
3934 fprintf(out, "%s%*c%s%s",
3935 s,
3936 33 - col >= 0 ? 33 - col : 0, ' ',
3937 ps == jp->ps ? "" : "| ",
3938 ps->ps_cmd
3939 );
3940 } while (++ps != psend);
3941 outcslow('\n', out);
3942
3943 jp->changed = 0;
3944
3945 if (jp->state == JOBDONE) {
3946 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3947 freejob(jp);
3948 }
3949}
3950
3951
3952
3953
3954
3955static void
3956showjobs(FILE *out, int mode)
3957{
3958 struct job *jp;
3959
3960 TRACE(("showjobs(0x%x) called\n", mode));
3961
3962
3963 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
3964 continue;
3965
3966 for (jp = curjob; jp; jp = jp->prev_job) {
3967 if (!(mode & SHOW_CHANGED) || jp->changed) {
3968 showjob(out, jp, mode);
3969 }
3970 }
3971}
3972
3973static int FAST_FUNC
3974jobscmd(int argc UNUSED_PARAM, char **argv)
3975{
3976 int mode, m;
3977
3978 mode = 0;
3979 while ((m = nextopt("lp")) != '\0') {
3980 if (m == 'l')
3981 mode |= SHOW_PIDS;
3982 else
3983 mode |= SHOW_ONLY_PGID;
3984 }
3985
3986 argv = argptr;
3987 if (*argv) {
3988 do
3989 showjob(stdout, getjob(*argv, 0), mode);
3990 while (*++argv);
3991 } else {
3992 showjobs(stdout, mode);
3993 }
3994
3995 return 0;
3996}
3997#endif
3998
3999
4000static int
4001getstatus(struct job *job)
4002{
4003 int status;
4004 int retval;
4005 struct procstat *ps;
4006
4007
4008 ps = job->ps + job->nprocs - 1;
4009 status = ps->ps_status;
4010 if (pipefail) {
4011
4012 while (status == 0 && --ps >= job->ps)
4013 status = ps->ps_status;
4014 }
4015
4016 retval = WEXITSTATUS(status);
4017 if (!WIFEXITED(status)) {
4018#if JOBS
4019 retval = WSTOPSIG(status);
4020 if (!WIFSTOPPED(status))
4021#endif
4022 {
4023
4024 retval = WTERMSIG(status);
4025#if JOBS
4026 if (retval == SIGINT)
4027 job->sigint = 1;
4028#endif
4029 }
4030 retval += 128;
4031 }
4032 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
4033 jobno(job), job->nprocs, status, retval));
4034 return retval;
4035}
4036
4037static int FAST_FUNC
4038waitcmd(int argc UNUSED_PARAM, char **argv)
4039{
4040 struct job *job;
4041 int retval;
4042 struct job *jp;
4043
4044 if (pending_sig)
4045 raise_exception(EXSIG);
4046
4047 nextopt(nullstr);
4048 retval = 0;
4049
4050 argv = argptr;
4051 if (!*argv) {
4052
4053 for (;;) {
4054 jp = curjob;
4055 while (1) {
4056 if (!jp)
4057 goto ret;
4058 if (jp->state == JOBRUNNING)
4059 break;
4060 jp->waited = 1;
4061 jp = jp->prev_job;
4062 }
4063 blocking_wait_with_raise_on_sig();
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076 if (pending_sig)
4077 raise_exception(EXSIG);
4078 }
4079 }
4080
4081 retval = 127;
4082 do {
4083 if (**argv != '%') {
4084 pid_t pid = number(*argv);
4085 job = curjob;
4086 while (1) {
4087 if (!job)
4088 goto repeat;
4089 if (job->ps[job->nprocs - 1].ps_pid == pid)
4090 break;
4091 job = job->prev_job;
4092 }
4093 } else
4094 job = getjob(*argv, 0);
4095
4096 while (job->state == JOBRUNNING)
4097 blocking_wait_with_raise_on_sig();
4098 job->waited = 1;
4099 retval = getstatus(job);
4100 repeat: ;
4101 } while (*++argv);
4102
4103 ret:
4104 return retval;
4105}
4106
4107static struct job *
4108growjobtab(void)
4109{
4110 size_t len;
4111 ptrdiff_t offset;
4112 struct job *jp, *jq;
4113
4114 len = njobs * sizeof(*jp);
4115 jq = jobtab;
4116 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4117
4118 offset = (char *)jp - (char *)jq;
4119 if (offset) {
4120
4121 size_t l = len;
4122
4123 jq = (struct job *)((char *)jq + l);
4124 while (l) {
4125 l -= sizeof(*jp);
4126 jq--;
4127#define joff(p) ((struct job *)((char *)(p) + l))
4128#define jmove(p) (p) = (void *)((char *)(p) + offset)
4129 if (joff(jp)->ps == &jq->ps0)
4130 jmove(joff(jp)->ps);
4131 if (joff(jp)->prev_job)
4132 jmove(joff(jp)->prev_job);
4133 }
4134 if (curjob)
4135 jmove(curjob);
4136#undef joff
4137#undef jmove
4138 }
4139
4140 njobs += 4;
4141 jobtab = jp;
4142 jp = (struct job *)((char *)jp + len);
4143 jq = jp + 3;
4144 do {
4145 jq->used = 0;
4146 } while (--jq >= jp);
4147 return jp;
4148}
4149
4150
4151
4152
4153
4154static struct job *
4155makejob( int nprocs)
4156{
4157 int i;
4158 struct job *jp;
4159
4160 for (i = njobs, jp = jobtab; ; jp++) {
4161 if (--i < 0) {
4162 jp = growjobtab();
4163 break;
4164 }
4165 if (jp->used == 0)
4166 break;
4167 if (jp->state != JOBDONE || !jp->waited)
4168 continue;
4169#if JOBS
4170 if (doing_jobctl)
4171 continue;
4172#endif
4173 freejob(jp);
4174 break;
4175 }
4176 memset(jp, 0, sizeof(*jp));
4177#if JOBS
4178
4179
4180 if (doing_jobctl)
4181 jp->jobctl = 1;
4182#endif
4183 jp->prev_job = curjob;
4184 curjob = jp;
4185 jp->used = 1;
4186 jp->ps = &jp->ps0;
4187 if (nprocs > 1) {
4188 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4189 }
4190 TRACE(("makejob(%d) returns %%%d\n", nprocs,
4191 jobno(jp)));
4192 return jp;
4193}
4194
4195#if JOBS
4196
4197
4198
4199
4200static char *cmdnextc;
4201
4202static void
4203cmdputs(const char *s)
4204{
4205 static const char vstype[VSTYPE + 1][3] = {
4206 "", "}", "-", "+", "?", "=",
4207 "%", "%%", "#", "##"
4208 IF_ASH_BASH_COMPAT(, ":", "/", "//")
4209 };
4210
4211 const char *p, *str;
4212 char cc[2];
4213 char *nextc;
4214 unsigned char c;
4215 unsigned char subtype = 0;
4216 int quoted = 0;
4217
4218 cc[1] = '\0';
4219 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4220 p = s;
4221 while ((c = *p++) != '\0') {
4222 str = NULL;
4223 switch (c) {
4224 case CTLESC:
4225 c = *p++;
4226 break;
4227 case CTLVAR:
4228 subtype = *p++;
4229 if ((subtype & VSTYPE) == VSLENGTH)
4230 str = "${#";
4231 else
4232 str = "${";
4233 if (!(subtype & VSQUOTE) == !(quoted & 1))
4234 goto dostr;
4235 quoted ^= 1;
4236 c = '"';
4237 break;
4238 case CTLENDVAR:
4239 str = "\"}" + !(quoted & 1);
4240 quoted >>= 1;
4241 subtype = 0;
4242 goto dostr;
4243 case CTLBACKQ:
4244 str = "$(...)";
4245 goto dostr;
4246 case CTLBACKQ+CTLQUOTE:
4247 str = "\"$(...)\"";
4248 goto dostr;
4249#if ENABLE_SH_MATH_SUPPORT
4250 case CTLARI:
4251 str = "$((";
4252 goto dostr;
4253 case CTLENDARI:
4254 str = "))";
4255 goto dostr;
4256#endif
4257 case CTLQUOTEMARK:
4258 quoted ^= 1;
4259 c = '"';
4260 break;
4261 case '=':
4262 if (subtype == 0)
4263 break;
4264 if ((subtype & VSTYPE) != VSNORMAL)
4265 quoted <<= 1;
4266 str = vstype[subtype & VSTYPE];
4267 if (subtype & VSNUL)
4268 c = ':';
4269 else
4270 goto checkstr;
4271 break;
4272 case '\'':
4273 case '\\':
4274 case '"':
4275 case '$':
4276
4277 cc[0] = c;
4278 str = cc;
4279 c = '\\';
4280 break;
4281 default:
4282 break;
4283 }
4284 USTPUTC(c, nextc);
4285 checkstr:
4286 if (!str)
4287 continue;
4288 dostr:
4289 while ((c = *str++) != '\0') {
4290 USTPUTC(c, nextc);
4291 }
4292 }
4293
4294 if (quoted & 1) {
4295 USTPUTC('"', nextc);
4296 }
4297 *nextc = 0;
4298 cmdnextc = nextc;
4299}
4300
4301
4302static void cmdtxt(union node *n);
4303
4304static void
4305cmdlist(union node *np, int sep)
4306{
4307 for (; np; np = np->narg.next) {
4308 if (!sep)
4309 cmdputs(" ");
4310 cmdtxt(np);
4311 if (sep && np->narg.next)
4312 cmdputs(" ");
4313 }
4314}
4315
4316static void
4317cmdtxt(union node *n)
4318{
4319 union node *np;
4320 struct nodelist *lp;
4321 const char *p;
4322
4323 if (!n)
4324 return;
4325 switch (n->type) {
4326 default:
4327#if DEBUG
4328 abort();
4329#endif
4330 case NPIPE:
4331 lp = n->npipe.cmdlist;
4332 for (;;) {
4333 cmdtxt(lp->n);
4334 lp = lp->next;
4335 if (!lp)
4336 break;
4337 cmdputs(" | ");
4338 }
4339 break;
4340 case NSEMI:
4341 p = "; ";
4342 goto binop;
4343 case NAND:
4344 p = " && ";
4345 goto binop;
4346 case NOR:
4347 p = " || ";
4348 binop:
4349 cmdtxt(n->nbinary.ch1);
4350 cmdputs(p);
4351 n = n->nbinary.ch2;
4352 goto donode;
4353 case NREDIR:
4354 case NBACKGND:
4355 n = n->nredir.n;
4356 goto donode;
4357 case NNOT:
4358 cmdputs("!");
4359 n = n->nnot.com;
4360 donode:
4361 cmdtxt(n);
4362 break;
4363 case NIF:
4364 cmdputs("if ");
4365 cmdtxt(n->nif.test);
4366 cmdputs("; then ");
4367 if (n->nif.elsepart) {
4368 cmdtxt(n->nif.ifpart);
4369 cmdputs("; else ");
4370 n = n->nif.elsepart;
4371 } else {
4372 n = n->nif.ifpart;
4373 }
4374 p = "; fi";
4375 goto dotail;
4376 case NSUBSHELL:
4377 cmdputs("(");
4378 n = n->nredir.n;
4379 p = ")";
4380 goto dotail;
4381 case NWHILE:
4382 p = "while ";
4383 goto until;
4384 case NUNTIL:
4385 p = "until ";
4386 until:
4387 cmdputs(p);
4388 cmdtxt(n->nbinary.ch1);
4389 n = n->nbinary.ch2;
4390 p = "; done";
4391 dodo:
4392 cmdputs("; do ");
4393 dotail:
4394 cmdtxt(n);
4395 goto dotail2;
4396 case NFOR:
4397 cmdputs("for ");
4398 cmdputs(n->nfor.var);
4399 cmdputs(" in ");
4400 cmdlist(n->nfor.args, 1);
4401 n = n->nfor.body;
4402 p = "; done";
4403 goto dodo;
4404 case NDEFUN:
4405 cmdputs(n->narg.text);
4406 p = "() { ... }";
4407 goto dotail2;
4408 case NCMD:
4409 cmdlist(n->ncmd.args, 1);
4410 cmdlist(n->ncmd.redirect, 0);
4411 break;
4412 case NARG:
4413 p = n->narg.text;
4414 dotail2:
4415 cmdputs(p);
4416 break;
4417 case NHERE:
4418 case NXHERE:
4419 p = "<<...";
4420 goto dotail2;
4421 case NCASE:
4422 cmdputs("case ");
4423 cmdputs(n->ncase.expr->narg.text);
4424 cmdputs(" in ");
4425 for (np = n->ncase.cases; np; np = np->nclist.next) {
4426 cmdtxt(np->nclist.pattern);
4427 cmdputs(") ");
4428 cmdtxt(np->nclist.body);
4429 cmdputs(";; ");
4430 }
4431 p = "esac";
4432 goto dotail2;
4433 case NTO:
4434 p = ">";
4435 goto redir;
4436 case NCLOBBER:
4437 p = ">|";
4438 goto redir;
4439 case NAPPEND:
4440 p = ">>";
4441 goto redir;
4442#if ENABLE_ASH_BASH_COMPAT
4443 case NTO2:
4444#endif
4445 case NTOFD:
4446 p = ">&";
4447 goto redir;
4448 case NFROM:
4449 p = "<";
4450 goto redir;
4451 case NFROMFD:
4452 p = "<&";
4453 goto redir;
4454 case NFROMTO:
4455 p = "<>";
4456 redir:
4457 cmdputs(utoa(n->nfile.fd));
4458 cmdputs(p);
4459 if (n->type == NTOFD || n->type == NFROMFD) {
4460 cmdputs(utoa(n->ndup.dupfd));
4461 break;
4462 }
4463 n = n->nfile.fname;
4464 goto donode;
4465 }
4466}
4467
4468static char *
4469commandtext(union node *n)
4470{
4471 char *name;
4472
4473 STARTSTACKSTR(cmdnextc);
4474 cmdtxt(n);
4475 name = stackblock();
4476 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4477 name, cmdnextc, cmdnextc));
4478 return ckstrdup(name);
4479}
4480#endif
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501static void
4502clear_traps(void)
4503{
4504 char **tp;
4505
4506 for (tp = trap; tp < &trap[NSIG]; tp++) {
4507 if (*tp && **tp) {
4508 INT_OFF;
4509 if (trap_ptr == trap)
4510 free(*tp);
4511
4512 *tp = NULL;
4513 if ((tp - trap) != 0)
4514 setsignal(tp - trap);
4515 INT_ON;
4516 }
4517 }
4518}
4519
4520
4521static void closescript(void);
4522
4523
4524static NOINLINE void
4525forkchild(struct job *jp, union node *n, int mode)
4526{
4527 int oldlvl;
4528
4529 TRACE(("Child shell %d\n", getpid()));
4530 oldlvl = shlvl;
4531 shlvl++;
4532
4533
4534
4535
4536
4537 closescript();
4538
4539 if (mode == FORK_NOJOB
4540 && n && n->type == NCMD
4541
4542 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
4543 && n->ncmd.args->narg.next == NULL
4544
4545 ) {
4546 TRACE(("Trap hack\n"));
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
4583
4584 }
4585 clear_traps();
4586#if JOBS
4587
4588 doing_jobctl = 0;
4589 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4590 pid_t pgrp;
4591
4592 if (jp->nprocs == 0)
4593 pgrp = getpid();
4594 else
4595 pgrp = jp->ps[0].ps_pid;
4596
4597 setpgid(0, pgrp);
4598 if (mode == FORK_FG)
4599 xtcsetpgrp(ttyfd, pgrp);
4600 setsignal(SIGTSTP);
4601 setsignal(SIGTTOU);
4602 } else
4603#endif
4604 if (mode == FORK_BG) {
4605
4606
4607 ignoresig(SIGINT);
4608 ignoresig(SIGQUIT);
4609 if (jp->nprocs == 0) {
4610 close(0);
4611 if (open(bb_dev_null, O_RDONLY) != 0)
4612 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
4613 }
4614 }
4615 if (!oldlvl) {
4616 if (iflag) {
4617 setsignal(SIGINT);
4618 setsignal(SIGTERM);
4619 }
4620
4621
4622
4623
4624
4625
4626 setsignal(SIGQUIT);
4627 }
4628#if JOBS
4629 if (n && n->type == NCMD
4630 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
4631 ) {
4632 TRACE(("Job hack\n"));
4633
4634
4635
4636
4637 freejob(curjob);
4638 return;
4639 }
4640#endif
4641 for (jp = curjob; jp; jp = jp->prev_job)
4642 freejob(jp);
4643 jobless = 0;
4644}
4645
4646
4647#if !JOBS
4648#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4649#endif
4650static void
4651forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4652{
4653 TRACE(("In parent shell: child = %d\n", pid));
4654 if (!jp) {
4655 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4656 continue;
4657 jobless++;
4658 return;
4659 }
4660#if JOBS
4661 if (mode != FORK_NOJOB && jp->jobctl) {
4662 int pgrp;
4663
4664 if (jp->nprocs == 0)
4665 pgrp = pid;
4666 else
4667 pgrp = jp->ps[0].ps_pid;
4668
4669 setpgid(pid, pgrp);
4670 }
4671#endif
4672 if (mode == FORK_BG) {
4673 backgndpid = pid;
4674 set_curjob(jp, CUR_RUNNING);
4675 }
4676 if (jp) {
4677 struct procstat *ps = &jp->ps[jp->nprocs++];
4678 ps->ps_pid = pid;
4679 ps->ps_status = -1;
4680 ps->ps_cmd = nullstr;
4681#if JOBS
4682 if (doing_jobctl && n)
4683 ps->ps_cmd = commandtext(n);
4684#endif
4685 }
4686}
4687
4688static int
4689forkshell(struct job *jp, union node *n, int mode)
4690{
4691 int pid;
4692
4693 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4694 pid = fork();
4695 if (pid < 0) {
4696 TRACE(("Fork failed, errno=%d", errno));
4697 if (jp)
4698 freejob(jp);
4699 ash_msg_and_raise_error("can't fork");
4700 }
4701 if (pid == 0) {
4702 CLEAR_RANDOM_T(&random_gen);
4703 forkchild(jp, n, mode);
4704 } else {
4705 forkparent(jp, n, mode, pid);
4706 }
4707 return pid;
4708}
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730static int
4731waitforjob(struct job *jp)
4732{
4733 int st;
4734
4735 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4736
4737 INT_OFF;
4738 while (jp->state == JOBRUNNING) {
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769 dowait(DOWAIT_BLOCK, jp);
4770 }
4771 INT_ON;
4772
4773 st = getstatus(jp);
4774#if JOBS
4775 if (jp->jobctl) {
4776 xtcsetpgrp(ttyfd, rootpid);
4777
4778
4779
4780
4781
4782
4783
4784
4785 if (jp->sigint)
4786 raise(SIGINT);
4787 }
4788 if (jp->state == JOBDONE)
4789#endif
4790 freejob(jp);
4791 return st;
4792}
4793
4794
4795
4796
4797static int
4798stoppedjobs(void)
4799{
4800 struct job *jp;
4801 int retval;
4802
4803 retval = 0;
4804 if (job_warning)
4805 goto out;
4806 jp = curjob;
4807 if (jp && jp->state == JOBSTOPPED) {
4808 out2str("You have stopped jobs.\n");
4809 job_warning = 2;
4810 retval++;
4811 }
4812 out:
4813 return retval;
4814}
4815
4816
4817
4818
4819
4820
4821
4822#define EMPTY -2
4823#define CLOSED -3
4824
4825
4826
4827
4828
4829static int
4830noclobberopen(const char *fname)
4831{
4832 int r, fd;
4833 struct stat finfo, finfo2;
4834
4835
4836
4837
4838
4839 r = stat(fname, &finfo);
4840 if (r == 0 && S_ISREG(finfo.st_mode)) {
4841 errno = EEXIST;
4842 return -1;
4843 }
4844
4845
4846
4847
4848
4849
4850
4851
4852 if (r != 0)
4853 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4854 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4855
4856
4857 if (fd < 0)
4858 return fd;
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4874 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4875 return fd;
4876
4877
4878 close(fd);
4879 errno = EEXIST;
4880 return -1;
4881}
4882
4883
4884
4885
4886
4887
4888
4889static void expandhere(union node *arg, int fd);
4890static int
4891openhere(union node *redir)
4892{
4893 int pip[2];
4894 size_t len = 0;
4895
4896 if (pipe(pip) < 0)
4897 ash_msg_and_raise_error("pipe call failed");
4898 if (redir->type == NHERE) {
4899 len = strlen(redir->nhere.doc->narg.text);
4900 if (len <= PIPE_BUF) {
4901 full_write(pip[1], redir->nhere.doc->narg.text, len);
4902 goto out;
4903 }
4904 }
4905 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4906
4907 close(pip[0]);
4908 ignoresig(SIGINT);
4909 ignoresig(SIGQUIT);
4910 ignoresig(SIGHUP);
4911 ignoresig(SIGTSTP);
4912 signal(SIGPIPE, SIG_DFL);
4913 if (redir->type == NHERE)
4914 full_write(pip[1], redir->nhere.doc->narg.text, len);
4915 else
4916 expandhere(redir->nhere.doc, pip[1]);
4917 _exit(EXIT_SUCCESS);
4918 }
4919 out:
4920 close(pip[1]);
4921 return pip[0];
4922}
4923
4924static int
4925openredirect(union node *redir)
4926{
4927 char *fname;
4928 int f;
4929
4930 switch (redir->nfile.type) {
4931 case NFROM:
4932 fname = redir->nfile.expfname;
4933 f = open(fname, O_RDONLY);
4934 if (f < 0)
4935 goto eopen;
4936 break;
4937 case NFROMTO:
4938 fname = redir->nfile.expfname;
4939 f = open(fname, O_RDWR|O_CREAT, 0666);
4940 if (f < 0)
4941 goto ecreate;
4942 break;
4943 case NTO:
4944#if ENABLE_ASH_BASH_COMPAT
4945 case NTO2:
4946#endif
4947
4948 if (Cflag) {
4949 fname = redir->nfile.expfname;
4950 f = noclobberopen(fname);
4951 if (f < 0)
4952 goto ecreate;
4953 break;
4954 }
4955
4956 case NCLOBBER:
4957 fname = redir->nfile.expfname;
4958 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4959 if (f < 0)
4960 goto ecreate;
4961 break;
4962 case NAPPEND:
4963 fname = redir->nfile.expfname;
4964 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4965 if (f < 0)
4966 goto ecreate;
4967 break;
4968 default:
4969#if DEBUG
4970 abort();
4971#endif
4972
4973
4974
4975
4976
4977
4978 case NHERE:
4979 case NXHERE:
4980 f = openhere(redir);
4981 break;
4982 }
4983
4984 return f;
4985 ecreate:
4986 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
4987 eopen:
4988 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
4989}
4990
4991
4992
4993
4994
4995
4996
4997
4998enum {
4999 COPYFD_EXACT = (int)~(INT_MAX),
5000 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5001};
5002static int
5003copyfd(int from, int to)
5004{
5005 int newfd;
5006
5007 if (to & COPYFD_EXACT) {
5008 to &= ~COPYFD_EXACT;
5009
5010 newfd = dup2(from, to);
5011 } else {
5012 newfd = fcntl(from, F_DUPFD, to);
5013 }
5014 if (newfd < 0) {
5015 if (errno == EMFILE)
5016 return EMPTY;
5017
5018 ash_msg_and_raise_error("%d: %m", from);
5019 }
5020 return newfd;
5021}
5022
5023
5024struct two_fd_t {
5025 int orig, copy;
5026};
5027struct redirtab {
5028 struct redirtab *next;
5029 int nullredirs;
5030 int pair_count;
5031 struct two_fd_t two_fd[];
5032};
5033#define redirlist (G_var.redirlist)
5034
5035static int need_to_remember(struct redirtab *rp, int fd)
5036{
5037 int i;
5038
5039 if (!rp)
5040 return 0;
5041
5042 for (i = 0; i < rp->pair_count; i++) {
5043 if (rp->two_fd[i].orig == fd) {
5044
5045 return 0;
5046 }
5047 }
5048 return 1;
5049}
5050
5051
5052static int is_hidden_fd(struct redirtab *rp, int fd)
5053{
5054 int i;
5055 struct parsefile *pf;
5056
5057 if (fd == -1)
5058 return 0;
5059
5060 pf = g_parsefile;
5061 while (pf) {
5062
5063
5064
5065
5066
5067
5068
5069
5070 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
5071 return 1;
5072 }
5073 pf = pf->prev;
5074 }
5075
5076 if (!rp)
5077 return 0;
5078
5079 fd |= COPYFD_RESTORE;
5080 for (i = 0; i < rp->pair_count; i++) {
5081 if (rp->two_fd[i].copy == fd) {
5082 return 1;
5083 }
5084 }
5085 return 0;
5086}
5087
5088
5089
5090
5091
5092
5093
5094#define REDIR_PUSH 01
5095#define REDIR_SAVEFD2 03
5096static void
5097redirect(union node *redir, int flags)
5098{
5099 struct redirtab *sv;
5100 int sv_pos;
5101 int i;
5102 int fd;
5103 int newfd;
5104 int copied_fd2 = -1;
5105
5106 g_nullredirs++;
5107 if (!redir) {
5108 return;
5109 }
5110
5111 sv = NULL;
5112 sv_pos = 0;
5113 INT_OFF;
5114 if (flags & REDIR_PUSH) {
5115 union node *tmp = redir;
5116 do {
5117 sv_pos++;
5118#if ENABLE_ASH_BASH_COMPAT
5119 if (tmp->nfile.type == NTO2)
5120 sv_pos++;
5121#endif
5122 tmp = tmp->nfile.next;
5123 } while (tmp);
5124 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
5125 sv->next = redirlist;
5126 sv->pair_count = sv_pos;
5127 redirlist = sv;
5128 sv->nullredirs = g_nullredirs - 1;
5129 g_nullredirs = 0;
5130 while (sv_pos > 0) {
5131 sv_pos--;
5132 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5133 }
5134 }
5135
5136 do {
5137 int right_fd = -1;
5138 fd = redir->nfile.fd;
5139 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
5140 right_fd = redir->ndup.dupfd;
5141
5142
5143 if (right_fd == fd)
5144 continue;
5145
5146 if (is_hidden_fd(sv, right_fd)) {
5147 errno = EBADF;
5148 ash_msg_and_raise_error("%d: %m", right_fd);
5149 }
5150 newfd = -1;
5151 } else {
5152 newfd = openredirect(redir);
5153 if (fd == newfd) {
5154
5155
5156 if (need_to_remember(sv, fd)) {
5157 goto remember_to_close;
5158 }
5159 continue;
5160 }
5161 }
5162#if ENABLE_ASH_BASH_COMPAT
5163 redirect_more:
5164#endif
5165 if (need_to_remember(sv, fd)) {
5166
5167
5168
5169 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5170 i = fcntl(fd, F_DUPFD, minfd);
5171
5172
5173
5174
5175 if (i == -1) {
5176 i = errno;
5177 if (i != EBADF) {
5178
5179 if (newfd >= 0)
5180 close(newfd);
5181 errno = i;
5182 ash_msg_and_raise_error("%d: %m", fd);
5183
5184 }
5185
5186 remember_to_close:
5187 i = CLOSED;
5188 } else {
5189
5190
5191
5192 if (is_hidden_fd(sv, fd))
5193 i |= COPYFD_RESTORE;
5194 }
5195 if (fd == 2)
5196 copied_fd2 = i;
5197 sv->two_fd[sv_pos].orig = fd;
5198 sv->two_fd[sv_pos].copy = i;
5199 sv_pos++;
5200 }
5201 if (newfd < 0) {
5202
5203 if (redir->ndup.dupfd < 0) {
5204
5205 if (fd != -1)
5206 close(fd);
5207 } else {
5208 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
5209 }
5210 } else if (fd != newfd) {
5211 copyfd(newfd, fd | COPYFD_EXACT);
5212#if ENABLE_ASH_BASH_COMPAT
5213 if (!(redir->nfile.type == NTO2 && fd == 2))
5214#endif
5215 close(newfd);
5216 }
5217#if ENABLE_ASH_BASH_COMPAT
5218 if (redir->nfile.type == NTO2 && fd == 1) {
5219
5220 newfd = 1;
5221 fd = 2;
5222 goto redirect_more;
5223 }
5224#endif
5225 } while ((redir = redir->nfile.next) != NULL);
5226
5227 INT_ON;
5228 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5229 preverrout_fd = copied_fd2;
5230}
5231
5232
5233
5234
5235static void
5236popredir(int drop, int restore)
5237{
5238 struct redirtab *rp;
5239 int i;
5240
5241 if (--g_nullredirs >= 0)
5242 return;
5243 INT_OFF;
5244 rp = redirlist;
5245 for (i = 0; i < rp->pair_count; i++) {
5246 int fd = rp->two_fd[i].orig;
5247 int copy = rp->two_fd[i].copy;
5248 if (copy == CLOSED) {
5249 if (!drop)
5250 close(fd);
5251 continue;
5252 }
5253 if (copy != EMPTY) {
5254 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
5255 copy &= ~COPYFD_RESTORE;
5256
5257 copyfd(copy, fd | COPYFD_EXACT);
5258 }
5259 close(copy & ~COPYFD_RESTORE);
5260 }
5261 }
5262 redirlist = rp->next;
5263 g_nullredirs = rp->nullredirs;
5264 free(rp);
5265 INT_ON;
5266}
5267
5268
5269
5270
5271
5272
5273
5274
5275static void
5276clearredir(int drop)
5277{
5278 for (;;) {
5279 g_nullredirs = 0;
5280 if (!redirlist)
5281 break;
5282 popredir(drop, 0);
5283 }
5284}
5285
5286static int
5287redirectsafe(union node *redir, int flags)
5288{
5289 int err;
5290 volatile int saveint;
5291 struct jmploc *volatile savehandler = exception_handler;
5292 struct jmploc jmploc;
5293
5294 SAVE_INT(saveint);
5295
5296 err = setjmp(jmploc.loc);
5297 if (!err) {
5298 exception_handler = &jmploc;
5299 redirect(redir, flags);
5300 }
5301 exception_handler = savehandler;
5302 if (err && exception_type != EXERROR)
5303 longjmp(exception_handler->loc, 1);
5304 RESTORE_INT(saveint);
5305 return err;
5306}
5307
5308
5309
5310
5311
5312
5313
5314#if ENABLE_SH_MATH_SUPPORT
5315static arith_t
5316ash_arith(const char *s)
5317{
5318 arith_eval_hooks_t math_hooks;
5319 arith_t result;
5320 int errcode = 0;
5321
5322 math_hooks.lookupvar = lookupvar;
5323 math_hooks.setvar = setvar2;
5324 math_hooks.endofname = endofname;
5325
5326 INT_OFF;
5327 result = arith(s, &errcode, &math_hooks);
5328 if (errcode < 0) {
5329 if (errcode == -3)
5330 ash_msg_and_raise_error("exponent less than 0");
5331 if (errcode == -2)
5332 ash_msg_and_raise_error("divide by zero");
5333 if (errcode == -5)
5334 ash_msg_and_raise_error("expression recursion loop detected");
5335 raise_error_syntax(s);
5336 }
5337 INT_ON;
5338
5339 return result;
5340}
5341#endif
5342
5343
5344
5345
5346#define EXP_FULL 0x1
5347#define EXP_TILDE 0x2
5348#define EXP_VARTILDE 0x4
5349#define EXP_REDIR 0x8
5350#define EXP_CASE 0x10
5351#define EXP_RECORD 0x20
5352#define EXP_VARTILDE2 0x40
5353#define EXP_WORD 0x80
5354#define EXP_QWORD 0x100
5355
5356
5357
5358#define RMESCAPE_ALLOC 0x1
5359#define RMESCAPE_GLOB 0x2
5360#define RMESCAPE_QUOTED 0x4
5361#define RMESCAPE_GROW 0x8
5362#define RMESCAPE_HEAP 0x10
5363
5364
5365
5366
5367
5368struct ifsregion {
5369 struct ifsregion *next;
5370 int begoff;
5371 int endoff;
5372 int nulonly;
5373};
5374
5375struct arglist {
5376 struct strlist *list;
5377 struct strlist **lastp;
5378};
5379
5380
5381static char *expdest;
5382
5383static struct nodelist *argbackq;
5384
5385static struct ifsregion ifsfirst;
5386
5387static struct ifsregion *ifslastp;
5388
5389static struct arglist exparg;
5390
5391
5392
5393
5394static int
5395cvtnum(arith_t num)
5396{
5397 int len;
5398
5399 expdest = makestrspace(32, expdest);
5400 len = fmtstr(expdest, 32, arith_t_fmt, num);
5401 STADJUST(len, expdest);
5402 return len;
5403}
5404
5405static size_t
5406esclen(const char *start, const char *p)
5407{
5408 size_t esc = 0;
5409
5410 while (p > start && (unsigned char)*--p == CTLESC) {
5411 esc++;
5412 }
5413 return esc;
5414}
5415
5416
5417
5418
5419static char *
5420rmescapes(char *str, int flag)
5421{
5422 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
5423
5424 char *p, *q, *r;
5425 unsigned inquotes;
5426 unsigned protect_against_glob;
5427 unsigned globbing;
5428
5429 p = strpbrk(str, qchars);
5430 if (!p)
5431 return str;
5432
5433 q = p;
5434 r = str;
5435 if (flag & RMESCAPE_ALLOC) {
5436 size_t len = p - str;
5437 size_t fulllen = len + strlen(p) + 1;
5438
5439 if (flag & RMESCAPE_GROW) {
5440 int strloc = str - (char *)stackblock();
5441 r = makestrspace(fulllen, expdest);
5442
5443 str = (char *)stackblock() + strloc;
5444 p = str + len;
5445 } else if (flag & RMESCAPE_HEAP) {
5446 r = ckmalloc(fulllen);
5447 } else {
5448 r = stalloc(fulllen);
5449 }
5450 q = r;
5451 if (len > 0) {
5452 q = (char *)memcpy(q, str, len) + len;
5453 }
5454 }
5455
5456 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5457 globbing = flag & RMESCAPE_GLOB;
5458 protect_against_glob = globbing;
5459 while (*p) {
5460 if ((unsigned char)*p == CTLQUOTEMARK) {
5461
5462
5463
5464
5465 inquotes = ~inquotes;
5466 p++;
5467 protect_against_glob = globbing;
5468 continue;
5469 }
5470 if (*p == '\\') {
5471
5472 protect_against_glob = 0;
5473 goto copy;
5474 }
5475 if ((unsigned char)*p == CTLESC) {
5476 p++;
5477 if (protect_against_glob && inquotes && *p != '/') {
5478 *q++ = '\\';
5479 }
5480 }
5481 protect_against_glob = globbing;
5482 copy:
5483 *q++ = *p++;
5484 }
5485 *q = '\0';
5486 if (flag & RMESCAPE_GROW) {
5487 expdest = r;
5488 STADJUST(q - r + 1, expdest);
5489 }
5490 return r;
5491}
5492#define pmatch(a, b) !fnmatch((a), (b), 0)
5493
5494
5495
5496
5497
5498
5499static char *
5500preglob(const char *pattern, int quoted, int flag)
5501{
5502 flag |= RMESCAPE_GLOB;
5503 if (quoted) {
5504 flag |= RMESCAPE_QUOTED;
5505 }
5506 return rmescapes((char *)pattern, flag);
5507}
5508
5509
5510
5511
5512static void
5513memtodest(const char *p, size_t len, int syntax, int quotes)
5514{
5515 char *q = expdest;
5516
5517 q = makestrspace(quotes ? len * 2 : len, q);
5518
5519 while (len--) {
5520 unsigned char c = *p++;
5521 if (c == '\0')
5522 continue;
5523 if (quotes) {
5524 int n = SIT(c, syntax);
5525 if (n == CCTL || n == CBACK)
5526 USTPUTC(CTLESC, q);
5527 }
5528 USTPUTC(c, q);
5529 }
5530
5531 expdest = q;
5532}
5533
5534static void
5535strtodest(const char *p, int syntax, int quotes)
5536{
5537 memtodest(p, strlen(p), syntax, quotes);
5538}
5539
5540
5541
5542
5543
5544static void
5545recordregion(int start, int end, int nulonly)
5546{
5547 struct ifsregion *ifsp;
5548
5549 if (ifslastp == NULL) {
5550 ifsp = &ifsfirst;
5551 } else {
5552 INT_OFF;
5553 ifsp = ckzalloc(sizeof(*ifsp));
5554
5555 ifslastp->next = ifsp;
5556 INT_ON;
5557 }
5558 ifslastp = ifsp;
5559 ifslastp->begoff = start;
5560 ifslastp->endoff = end;
5561 ifslastp->nulonly = nulonly;
5562}
5563
5564static void
5565removerecordregions(int endoff)
5566{
5567 if (ifslastp == NULL)
5568 return;
5569
5570 if (ifsfirst.endoff > endoff) {
5571 while (ifsfirst.next != NULL) {
5572 struct ifsregion *ifsp;
5573 INT_OFF;
5574 ifsp = ifsfirst.next->next;
5575 free(ifsfirst.next);
5576 ifsfirst.next = ifsp;
5577 INT_ON;
5578 }
5579 if (ifsfirst.begoff > endoff)
5580 ifslastp = NULL;
5581 else {
5582 ifslastp = &ifsfirst;
5583 ifsfirst.endoff = endoff;
5584 }
5585 return;
5586 }
5587
5588 ifslastp = &ifsfirst;
5589 while (ifslastp->next && ifslastp->next->begoff < endoff)
5590 ifslastp=ifslastp->next;
5591 while (ifslastp->next != NULL) {
5592 struct ifsregion *ifsp;
5593 INT_OFF;
5594 ifsp = ifslastp->next->next;
5595 free(ifslastp->next);
5596 ifslastp->next = ifsp;
5597 INT_ON;
5598 }
5599 if (ifslastp->endoff > endoff)
5600 ifslastp->endoff = endoff;
5601}
5602
5603static char *
5604exptilde(char *startp, char *p, int flags)
5605{
5606 unsigned char c;
5607 char *name;
5608 struct passwd *pw;
5609 const char *home;
5610 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
5611 int startloc;
5612
5613 name = p + 1;
5614
5615 while ((c = *++p) != '\0') {
5616 switch (c) {
5617 case CTLESC:
5618 return startp;
5619 case CTLQUOTEMARK:
5620 return startp;
5621 case ':':
5622 if (flags & EXP_VARTILDE)
5623 goto done;
5624 break;
5625 case '/':
5626 case CTLENDVAR:
5627 goto done;
5628 }
5629 }
5630 done:
5631 *p = '\0';
5632 if (*name == '\0') {
5633 home = lookupvar("HOME");
5634 } else {
5635 pw = getpwnam(name);
5636 if (pw == NULL)
5637 goto lose;
5638 home = pw->pw_dir;
5639 }
5640 if (!home || !*home)
5641 goto lose;
5642 *p = c;
5643 startloc = expdest - (char *)stackblock();
5644 strtodest(home, SQSYNTAX, quotes);
5645 recordregion(startloc, expdest - (char *)stackblock(), 0);
5646 return p;
5647 lose:
5648 *p = c;
5649 return startp;
5650}
5651
5652
5653
5654
5655
5656
5657
5658struct backcmd {
5659 int fd;
5660 int nleft;
5661 char *buf;
5662 struct job *jp;
5663};
5664
5665
5666static uint8_t back_exitstatus;
5667#define EV_EXIT 01
5668static void evaltree(union node *, int);
5669
5670static void FAST_FUNC
5671evalbackcmd(union node *n, struct backcmd *result)
5672{
5673 int saveherefd;
5674
5675 result->fd = -1;
5676 result->buf = NULL;
5677 result->nleft = 0;
5678 result->jp = NULL;
5679 if (n == NULL)
5680 goto out;
5681
5682 saveherefd = herefd;
5683 herefd = -1;
5684
5685 {
5686 int pip[2];
5687 struct job *jp;
5688
5689 if (pipe(pip) < 0)
5690 ash_msg_and_raise_error("pipe call failed");
5691 jp = makejob( 1);
5692 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5693 FORCE_INT_ON;
5694 close(pip[0]);
5695 if (pip[1] != 1) {
5696
5697 copyfd(pip[1], 1 | COPYFD_EXACT);
5698 close(pip[1]);
5699 }
5700 eflag = 0;
5701 evaltree(n, EV_EXIT);
5702
5703 }
5704 close(pip[1]);
5705 result->fd = pip[0];
5706 result->jp = jp;
5707 }
5708 herefd = saveherefd;
5709 out:
5710 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5711 result->fd, result->buf, result->nleft, result->jp));
5712}
5713
5714
5715
5716
5717static void
5718expbackq(union node *cmd, int quoted, int quotes)
5719{
5720 struct backcmd in;
5721 int i;
5722 char buf[128];
5723 char *p;
5724 char *dest;
5725 int startloc;
5726 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
5727 struct stackmark smark;
5728
5729 INT_OFF;
5730 setstackmark(&smark);
5731 dest = expdest;
5732 startloc = dest - (char *)stackblock();
5733 grabstackstr(dest);
5734 evalbackcmd(cmd, &in);
5735 popstackmark(&smark);
5736
5737 p = in.buf;
5738 i = in.nleft;
5739 if (i == 0)
5740 goto read;
5741 for (;;) {
5742 memtodest(p, i, syntax, quotes);
5743 read:
5744 if (in.fd < 0)
5745 break;
5746 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
5747 TRACE(("expbackq: read returns %d\n", i));
5748 if (i <= 0)
5749 break;
5750 p = buf;
5751 }
5752
5753 free(in.buf);
5754 if (in.fd >= 0) {
5755 close(in.fd);
5756 back_exitstatus = waitforjob(in.jp);
5757 }
5758 INT_ON;
5759
5760
5761 dest = expdest;
5762 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5763 STUNPUTC(dest);
5764 expdest = dest;
5765
5766 if (quoted == 0)
5767 recordregion(startloc, dest - (char *)stackblock(), 0);
5768 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5769 (dest - (char *)stackblock()) - startloc,
5770 (dest - (char *)stackblock()) - startloc,
5771 stackblock() + startloc));
5772}
5773
5774#if ENABLE_SH_MATH_SUPPORT
5775
5776
5777
5778
5779static void
5780expari(int quotes)
5781{
5782 char *p, *start;
5783 int begoff;
5784 int flag;
5785 int len;
5786
5787
5788
5789
5790
5791
5792
5793
5794 start = stackblock();
5795 p = expdest - 1;
5796 *p = '\0';
5797 p--;
5798 do {
5799 int esc;
5800
5801 while ((unsigned char)*p != CTLARI) {
5802 p--;
5803#if DEBUG
5804 if (p < start) {
5805 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5806 }
5807#endif
5808 }
5809
5810 esc = esclen(start, p);
5811 if (!(esc % 2)) {
5812 break;
5813 }
5814
5815 p -= esc + 1;
5816 } while (1);
5817
5818 begoff = p - start;
5819
5820 removerecordregions(begoff);
5821
5822 flag = p[1];
5823
5824 expdest = p;
5825
5826 if (quotes)
5827 rmescapes(p + 2, 0);
5828
5829 len = cvtnum(ash_arith(p + 2));
5830
5831 if (flag != '"')
5832 recordregion(begoff, begoff + len, 0);
5833}
5834#endif
5835
5836
5837static char *evalvar(char *p, int flags, struct strlist *var_str_list);
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848static void
5849argstr(char *p, int flags, struct strlist *var_str_list)
5850{
5851 static const char spclchars[] ALIGN1 = {
5852 '=',
5853 ':',
5854 CTLQUOTEMARK,
5855 CTLENDVAR,
5856 CTLESC,
5857 CTLVAR,
5858 CTLBACKQ,
5859 CTLBACKQ | CTLQUOTE,
5860#if ENABLE_SH_MATH_SUPPORT
5861 CTLENDARI,
5862#endif
5863 '\0'
5864 };
5865 const char *reject = spclchars;
5866 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
5867 int breakall = flags & EXP_WORD;
5868 int inquotes;
5869 size_t length;
5870 int startloc;
5871
5872 if (!(flags & EXP_VARTILDE)) {
5873 reject += 2;
5874 } else if (flags & EXP_VARTILDE2) {
5875 reject++;
5876 }
5877 inquotes = 0;
5878 length = 0;
5879 if (flags & EXP_TILDE) {
5880 char *q;
5881
5882 flags &= ~EXP_TILDE;
5883 tilde:
5884 q = p;
5885 if (*q == CTLESC && (flags & EXP_QWORD))
5886 q++;
5887 if (*q == '~')
5888 p = exptilde(p, q, flags);
5889 }
5890 start:
5891 startloc = expdest - (char *)stackblock();
5892 for (;;) {
5893 unsigned char c;
5894
5895 length += strcspn(p + length, reject);
5896 c = p[length];
5897 if (c) {
5898 if (!(c & 0x80)
5899#if ENABLE_SH_MATH_SUPPORT
5900 || c == CTLENDARI
5901#endif
5902 ) {
5903
5904 length++;
5905 }
5906 }
5907 if (length > 0) {
5908 int newloc;
5909 expdest = stack_nputstr(p, length, expdest);
5910 newloc = expdest - (char *)stackblock();
5911 if (breakall && !inquotes && newloc > startloc) {
5912 recordregion(startloc, newloc, 0);
5913 }
5914 startloc = newloc;
5915 }
5916 p += length + 1;
5917 length = 0;
5918
5919 switch (c) {
5920 case '\0':
5921 goto breakloop;
5922 case '=':
5923 if (flags & EXP_VARTILDE2) {
5924 p--;
5925 continue;
5926 }
5927 flags |= EXP_VARTILDE2;
5928 reject++;
5929
5930 case ':':
5931
5932
5933
5934
5935 if (*--p == '~') {
5936 goto tilde;
5937 }
5938 continue;
5939 }
5940
5941 switch (c) {
5942 case CTLENDVAR:
5943 goto breakloop;
5944 case CTLQUOTEMARK:
5945
5946 if (!inquotes
5947 && memcmp(p, dolatstr, 4) == 0
5948 && ( p[4] == CTLQUOTEMARK
5949 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
5950 )
5951 ) {
5952 p = evalvar(p + 1, flags, NULL) + 1;
5953 goto start;
5954 }
5955 inquotes = !inquotes;
5956 addquote:
5957 if (quotes) {
5958 p--;
5959 length++;
5960 startloc++;
5961 }
5962 break;
5963 case CTLESC:
5964 startloc++;
5965 length++;
5966 goto addquote;
5967 case CTLVAR:
5968 p = evalvar(p, flags, var_str_list);
5969 goto start;
5970 case CTLBACKQ:
5971 c = '\0';
5972 case CTLBACKQ|CTLQUOTE:
5973 expbackq(argbackq->n, c, quotes);
5974 argbackq = argbackq->next;
5975 goto start;
5976#if ENABLE_SH_MATH_SUPPORT
5977 case CTLENDARI:
5978 p--;
5979 expari(quotes);
5980 goto start;
5981#endif
5982 }
5983 }
5984 breakloop:
5985 ;
5986}
5987
5988static char *
5989scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
5990 int zero)
5991{
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018 char *loc, *loc2;
6019 char c;
6020
6021 loc = startp;
6022 loc2 = rmesc;
6023 do {
6024 int match;
6025 const char *s = loc2;
6026
6027 c = *loc2;
6028 if (zero) {
6029 *loc2 = '\0';
6030 s = rmesc;
6031 }
6032 match = pmatch(str, s);
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048 *loc2 = c;
6049 if (match)
6050 return loc;
6051 if (quotes && (unsigned char)*loc == CTLESC)
6052 loc++;
6053 loc++;
6054 loc2++;
6055 } while (c);
6056 return 0;
6057}
6058
6059static char *
6060scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start)
6061{
6062#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6063 int try2optimize = match_at_start;
6064#endif
6065 int esc = 0;
6066 char *loc;
6067 char *loc2;
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
6080 int match;
6081 char c = *loc2;
6082 const char *s = loc2;
6083 if (match_at_start) {
6084 *loc2 = '\0';
6085 s = rmesc;
6086 }
6087 match = pmatch(pattern, s);
6088
6089 *loc2 = c;
6090 if (match)
6091 return loc;
6092#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6093 if (try2optimize) {
6094
6095
6096
6097
6098
6099 unsigned plen = strlen(pattern);
6100
6101 if (plen != 0 && pattern[--plen] == '*') {
6102
6103
6104
6105
6106 int slashes = 0;
6107 while (plen != 0 && pattern[--plen] == '\\')
6108 slashes++;
6109 if (!(slashes & 1))
6110 break;
6111 }
6112 try2optimize = 0;
6113 }
6114#endif
6115 loc--;
6116 if (quotes) {
6117 if (--esc < 0) {
6118 esc = esclen(startp, loc);
6119 }
6120 if (esc % 2) {
6121 esc--;
6122 loc--;
6123 }
6124 }
6125 }
6126 return 0;
6127}
6128
6129static void varunset(const char *, const char *, const char *, int) NORETURN;
6130static void
6131varunset(const char *end, const char *var, const char *umsg, int varflags)
6132{
6133 const char *msg;
6134 const char *tail;
6135
6136 tail = nullstr;
6137 msg = "parameter not set";
6138 if (umsg) {
6139 if ((unsigned char)*end == CTLENDVAR) {
6140 if (varflags & VSNUL)
6141 tail = " or null";
6142 } else {
6143 msg = umsg;
6144 }
6145 }
6146 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6147}
6148
6149#if ENABLE_ASH_BASH_COMPAT
6150static char *
6151parse_sub_pattern(char *arg, int inquotes)
6152{
6153 char *idx, *repl = NULL;
6154 unsigned char c;
6155
6156 idx = arg;
6157 while (1) {
6158 c = *arg;
6159 if (!c)
6160 break;
6161 if (c == '/') {
6162
6163 if (!repl) {
6164 repl = idx + 1;
6165 c = '\0';
6166 }
6167 }
6168 *idx++ = c;
6169 if (!inquotes && c == '\\' && arg[1] == '\\')
6170 arg++;
6171 arg++;
6172 }
6173 *idx = c;
6174
6175 return repl;
6176}
6177#endif
6178
6179static const char *
6180subevalvar(char *p, char *str, int strloc, int subtype,
6181 int startloc, int varflags, int quotes, struct strlist *var_str_list)
6182{
6183 struct nodelist *saveargbackq = argbackq;
6184 char *startp;
6185 char *loc;
6186 char *rmesc, *rmescend;
6187 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
6188 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
6189 int saveherefd = herefd;
6190 int amount, workloc, resetloc;
6191 int zero;
6192 char *(*scan)(char*, char*, char*, char*, int, int);
6193
6194 herefd = -1;
6195 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6196 var_str_list);
6197 STPUTC('\0', expdest);
6198 herefd = saveherefd;
6199 argbackq = saveargbackq;
6200 startp = (char *)stackblock() + startloc;
6201
6202 switch (subtype) {
6203 case VSASSIGN:
6204 setvar(str, startp, 0);
6205 amount = startp - expdest;
6206 STADJUST(amount, expdest);
6207 return startp;
6208
6209#if ENABLE_ASH_BASH_COMPAT
6210 case VSSUBSTR:
6211 loc = str = stackblock() + strloc;
6212
6213 pos = atoi(loc);
6214 len = str - startp - 1;
6215
6216
6217 if (quotes) {
6218 char *ptr;
6219
6220
6221 for (ptr = startp; ptr < (str - 1); ptr++) {
6222 if ((unsigned char)*ptr == CTLESC) {
6223 len--;
6224 ptr++;
6225 }
6226 }
6227 }
6228 orig_len = len;
6229
6230 if (*loc++ == ':') {
6231
6232 len = number(loc);
6233 } else {
6234
6235 len = orig_len;
6236 while (*loc && *loc != ':') {
6237
6238
6239
6240
6241
6242 loc++;
6243 }
6244 if (*loc++ == ':') {
6245 len = number(loc);
6246 }
6247 }
6248 if (pos >= orig_len) {
6249 pos = 0;
6250 len = 0;
6251 }
6252 if (len > (orig_len - pos))
6253 len = orig_len - pos;
6254
6255 for (str = startp; pos; str++, pos--) {
6256 if (quotes && (unsigned char)*str == CTLESC)
6257 str++;
6258 }
6259 for (loc = startp; len; len--) {
6260 if (quotes && (unsigned char)*str == CTLESC)
6261 *loc++ = *str++;
6262 *loc++ = *str++;
6263 }
6264 *loc = '\0';
6265 amount = loc - expdest;
6266 STADJUST(amount, expdest);
6267 return loc;
6268#endif
6269
6270 case VSQUESTION:
6271 varunset(p, str, startp, varflags);
6272
6273 }
6274 resetloc = expdest - (char *)stackblock();
6275
6276
6277
6278
6279
6280
6281 IF_ASH_BASH_COMPAT(restart:)
6282
6283 amount = expdest - ((char *)stackblock() + resetloc);
6284 STADJUST(-amount, expdest);
6285 startp = (char *)stackblock() + startloc;
6286
6287 rmesc = startp;
6288 rmescend = (char *)stackblock() + strloc;
6289 if (quotes) {
6290 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6291 if (rmesc != startp) {
6292 rmescend = expdest;
6293 startp = (char *)stackblock() + startloc;
6294 }
6295 }
6296 rmescend--;
6297 str = (char *)stackblock() + strloc;
6298 preglob(str, varflags & VSQUOTE, 0);
6299 workloc = expdest - (char *)stackblock();
6300
6301#if ENABLE_ASH_BASH_COMPAT
6302 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6303 char *idx, *end;
6304
6305 if (!repl) {
6306 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6307 if (!repl)
6308 repl = nullstr;
6309 }
6310
6311
6312 if (str[0] == '\0')
6313 return 0;
6314
6315 len = 0;
6316 idx = startp;
6317 end = str - 1;
6318 while (idx < end) {
6319 try_to_match:
6320 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6321 if (!loc) {
6322
6323 char *restart_detect = stackblock();
6324 skip_matching:
6325 STPUTC(*idx, expdest);
6326 if (quotes && (unsigned char)*idx == CTLESC) {
6327 idx++;
6328 len++;
6329 STPUTC(*idx, expdest);
6330 }
6331 if (stackblock() != restart_detect)
6332 goto restart;
6333 idx++;
6334 len++;
6335 rmesc++;
6336
6337 if (idx >= end)
6338 break;
6339 if (str[0] == '*') {
6340
6341
6342
6343 goto skip_matching;
6344 }
6345 goto try_to_match;
6346 }
6347
6348 if (subtype == VSREPLACEALL) {
6349 while (idx < loc) {
6350 if (quotes && (unsigned char)*idx == CTLESC)
6351 idx++;
6352 idx++;
6353 rmesc++;
6354 }
6355 } else {
6356 idx = loc;
6357 }
6358
6359 for (loc = (char*)repl; *loc; loc++) {
6360 char *restart_detect = stackblock();
6361 if (quotes && *loc == '\\') {
6362 STPUTC(CTLESC, expdest);
6363 len++;
6364 }
6365 STPUTC(*loc, expdest);
6366 if (stackblock() != restart_detect)
6367 goto restart;
6368 len++;
6369 }
6370
6371 if (subtype == VSREPLACE) {
6372 while (*idx) {
6373 char *restart_detect = stackblock();
6374 if (quotes && *idx == '\\') {
6375 STPUTC(CTLESC, expdest);
6376 len++;
6377 }
6378 STPUTC(*idx, expdest);
6379 if (stackblock() != restart_detect)
6380 goto restart;
6381 len++;
6382 idx++;
6383 }
6384 break;
6385 }
6386 }
6387
6388
6389
6390
6391 STPUTC('\0', expdest);
6392 startp = (char *)stackblock() + startloc;
6393 memmove(startp, (char *)stackblock() + workloc, len + 1);
6394 amount = expdest - (startp + len);
6395 STADJUST(-amount, expdest);
6396 return startp;
6397 }
6398#endif
6399
6400 subtype -= VSTRIMRIGHT;
6401#if DEBUG
6402 if (subtype < 0 || subtype > 7)
6403 abort();
6404#endif
6405
6406 zero = subtype >> 1;
6407
6408 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6409
6410 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6411 if (loc) {
6412 if (zero) {
6413 memmove(startp, loc, str - loc);
6414 loc = startp + (str - loc) - 1;
6415 }
6416 *loc = '\0';
6417 amount = loc - expdest;
6418 STADJUST(amount, expdest);
6419 }
6420 return loc;
6421}
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436static NOINLINE ssize_t
6437varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6438{
6439 const char *p;
6440 int num;
6441 int i;
6442 int sepq = 0;
6443 ssize_t len = 0;
6444 int subtype = varflags & VSTYPE;
6445 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
6446 int quoted = varflags & VSQUOTE;
6447 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
6448
6449 switch (*name) {
6450 case '$':
6451 num = rootpid;
6452 goto numvar;
6453 case '?':
6454 num = exitstatus;
6455 goto numvar;
6456 case '#':
6457 num = shellparam.nparam;
6458 goto numvar;
6459 case '!':
6460 num = backgndpid;
6461 if (num == 0)
6462 return -1;
6463 numvar:
6464 len = cvtnum(num);
6465 goto check_1char_name;
6466 case '-':
6467 expdest = makestrspace(NOPTS, expdest);
6468 for (i = NOPTS - 1; i >= 0; i--) {
6469 if (optlist[i]) {
6470 USTPUTC(optletters(i), expdest);
6471 len++;
6472 }
6473 }
6474 check_1char_name:
6475#if 0
6476
6477 if (name[2] != '\0')
6478 raise_error_syntax("bad substitution");
6479#endif
6480 break;
6481 case '@': {
6482 char **ap;
6483 int sep;
6484
6485 if (quoted && (flags & EXP_FULL)) {
6486
6487 sep = 1 << CHAR_BIT;
6488 goto param;
6489 }
6490
6491 case '*':
6492 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
6493 i = SIT(sep, syntax);
6494 if (quotes && (i == CCTL || i == CBACK))
6495 sepq = 1;
6496 param:
6497 ap = shellparam.p;
6498 if (!ap)
6499 return -1;
6500 while ((p = *ap++) != NULL) {
6501 size_t partlen;
6502
6503 partlen = strlen(p);
6504 len += partlen;
6505
6506 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6507 memtodest(p, partlen, syntax, quotes);
6508
6509 if (*ap && sep) {
6510 char *q;
6511
6512 len++;
6513 if (subtype == VSPLUS || subtype == VSLENGTH) {
6514 continue;
6515 }
6516 q = expdest;
6517 if (sepq)
6518 STPUTC(CTLESC, q);
6519
6520
6521 STPUTC(sep, q);
6522 expdest = q;
6523 }
6524 }
6525 return len;
6526 }
6527 case '0':
6528 case '1':
6529 case '2':
6530 case '3':
6531 case '4':
6532 case '5':
6533 case '6':
6534 case '7':
6535 case '8':
6536 case '9':
6537 num = atoi(name);
6538 if (num < 0 || num > shellparam.nparam)
6539 return -1;
6540 p = num ? shellparam.p[num - 1] : arg0;
6541 goto value;
6542 default:
6543
6544
6545
6546
6547 if (var_str_list) {
6548 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6549 p = NULL;
6550 do {
6551 char *str, *eq;
6552 str = var_str_list->text;
6553 eq = strchr(str, '=');
6554 if (!eq)
6555 break;
6556 eq++;
6557 if (name_len == (unsigned)(eq - str)
6558 && strncmp(str, name, name_len) == 0
6559 ) {
6560 p = eq;
6561
6562
6563 }
6564 var_str_list = var_str_list->next;
6565 } while (var_str_list);
6566 if (p)
6567 goto value;
6568 }
6569 p = lookupvar(name);
6570 value:
6571 if (!p)
6572 return -1;
6573
6574 len = strlen(p);
6575 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6576 memtodest(p, len, syntax, quotes);
6577 return len;
6578 }
6579
6580 if (subtype == VSPLUS || subtype == VSLENGTH)
6581 STADJUST(-len, expdest);
6582 return len;
6583}
6584
6585
6586
6587
6588
6589static char *
6590evalvar(char *p, int flags, struct strlist *var_str_list)
6591{
6592 char varflags;
6593 char subtype;
6594 char quoted;
6595 char easy;
6596 char *var;
6597 int patloc;
6598 int startloc;
6599 ssize_t varlen;
6600
6601 varflags = (unsigned char) *p++;
6602 subtype = varflags & VSTYPE;
6603 quoted = varflags & VSQUOTE;
6604 var = p;
6605 easy = (!quoted || (*var == '@' && shellparam.nparam));
6606 startloc = expdest - (char *)stackblock();
6607 p = strchr(p, '=') + 1;
6608
6609 again:
6610 varlen = varvalue(var, varflags, flags, var_str_list);
6611 if (varflags & VSNUL)
6612 varlen--;
6613
6614 if (subtype == VSPLUS) {
6615 varlen = -1 - varlen;
6616 goto vsplus;
6617 }
6618
6619 if (subtype == VSMINUS) {
6620 vsplus:
6621 if (varlen < 0) {
6622 argstr(
6623 p, flags | EXP_TILDE |
6624 (quoted ? EXP_QWORD : EXP_WORD),
6625 var_str_list
6626 );
6627 goto end;
6628 }
6629 if (easy)
6630 goto record;
6631 goto end;
6632 }
6633
6634 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6635 if (varlen < 0) {
6636 if (subevalvar(p, var, 0,
6637 subtype, startloc, varflags,
6638 0,
6639 var_str_list)
6640 ) {
6641 varflags &= ~VSNUL;
6642
6643
6644
6645
6646 removerecordregions(startloc);
6647 goto again;
6648 }
6649 goto end;
6650 }
6651 if (easy)
6652 goto record;
6653 goto end;
6654 }
6655
6656 if (varlen < 0 && uflag)
6657 varunset(p, var, 0, 0);
6658
6659 if (subtype == VSLENGTH) {
6660 cvtnum(varlen > 0 ? varlen : 0);
6661 goto record;
6662 }
6663
6664 if (subtype == VSNORMAL) {
6665 if (easy)
6666 goto record;
6667 goto end;
6668 }
6669
6670#if DEBUG
6671 switch (subtype) {
6672 case VSTRIMLEFT:
6673 case VSTRIMLEFTMAX:
6674 case VSTRIMRIGHT:
6675 case VSTRIMRIGHTMAX:
6676#if ENABLE_ASH_BASH_COMPAT
6677 case VSSUBSTR:
6678 case VSREPLACE:
6679 case VSREPLACEALL:
6680#endif
6681 break;
6682 default:
6683 abort();
6684 }
6685#endif
6686
6687 if (varlen >= 0) {
6688
6689
6690
6691
6692 STPUTC('\0', expdest);
6693 patloc = expdest - (char *)stackblock();
6694 if (NULL == subevalvar(p, NULL, patloc, subtype,
6695 startloc, varflags,
6696
6697 flags & (EXP_FULL | EXP_CASE),
6698 var_str_list)
6699 ) {
6700 int amount = expdest - (
6701 (char *)stackblock() + patloc - 1
6702 );
6703 STADJUST(-amount, expdest);
6704 }
6705
6706 removerecordregions(startloc);
6707 record:
6708 recordregion(startloc, expdest - (char *)stackblock(), quoted);
6709 }
6710
6711 end:
6712 if (subtype != VSNORMAL) {
6713 int nesting = 1;
6714 for (;;) {
6715 unsigned char c = *p++;
6716 if (c == CTLESC)
6717 p++;
6718 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6719 if (varlen >= 0)
6720 argbackq = argbackq->next;
6721 } else if (c == CTLVAR) {
6722 if ((*p++ & VSTYPE) != VSNORMAL)
6723 nesting++;
6724 } else if (c == CTLENDVAR) {
6725 if (--nesting == 0)
6726 break;
6727 }
6728 }
6729 }
6730 return p;
6731}
6732
6733
6734
6735
6736
6737
6738static void
6739ifsbreakup(char *string, struct arglist *arglist)
6740{
6741 struct ifsregion *ifsp;
6742 struct strlist *sp;
6743 char *start;
6744 char *p;
6745 char *q;
6746 const char *ifs, *realifs;
6747 int ifsspc;
6748 int nulonly;
6749
6750 start = string;
6751 if (ifslastp != NULL) {
6752 ifsspc = 0;
6753 nulonly = 0;
6754 realifs = ifsset() ? ifsval() : defifs;
6755 ifsp = &ifsfirst;
6756 do {
6757 p = string + ifsp->begoff;
6758 nulonly = ifsp->nulonly;
6759 ifs = nulonly ? nullstr : realifs;
6760 ifsspc = 0;
6761 while (p < string + ifsp->endoff) {
6762 q = p;
6763 if ((unsigned char)*p == CTLESC)
6764 p++;
6765 if (!strchr(ifs, *p)) {
6766 p++;
6767 continue;
6768 }
6769 if (!nulonly)
6770 ifsspc = (strchr(defifs, *p) != NULL);
6771
6772 if (q == start && ifsspc) {
6773 p++;
6774 start = p;
6775 continue;
6776 }
6777 *q = '\0';
6778 sp = stzalloc(sizeof(*sp));
6779 sp->text = start;
6780 *arglist->lastp = sp;
6781 arglist->lastp = &sp->next;
6782 p++;
6783 if (!nulonly) {
6784 for (;;) {
6785 if (p >= string + ifsp->endoff) {
6786 break;
6787 }
6788 q = p;
6789 if ((unsigned char)*p == CTLESC)
6790 p++;
6791 if (strchr(ifs, *p) == NULL) {
6792 p = q;
6793 break;
6794 }
6795 if (strchr(defifs, *p) == NULL) {
6796 if (ifsspc) {
6797 p++;
6798 ifsspc = 0;
6799 } else {
6800 p = q;
6801 break;
6802 }
6803 } else
6804 p++;
6805 }
6806 }
6807 start = p;
6808 }
6809 ifsp = ifsp->next;
6810 } while (ifsp != NULL);
6811 if (nulonly)
6812 goto add;
6813 }
6814
6815 if (!*start)
6816 return;
6817
6818 add:
6819 sp = stzalloc(sizeof(*sp));
6820 sp->text = start;
6821 *arglist->lastp = sp;
6822 arglist->lastp = &sp->next;
6823}
6824
6825static void
6826ifsfree(void)
6827{
6828 struct ifsregion *p;
6829
6830 INT_OFF;
6831 p = ifsfirst.next;
6832 do {
6833 struct ifsregion *ifsp;
6834 ifsp = p->next;
6835 free(p);
6836 p = ifsp;
6837 } while (p);
6838 ifslastp = NULL;
6839 ifsfirst.next = NULL;
6840 INT_ON;
6841}
6842
6843
6844
6845
6846static void
6847addfname(const char *name)
6848{
6849 struct strlist *sp;
6850
6851 sp = stzalloc(sizeof(*sp));
6852 sp->text = ststrdup(name);
6853 *exparg.lastp = sp;
6854 exparg.lastp = &sp->next;
6855}
6856
6857
6858
6859
6860static void
6861expmeta(char *expdir, char *enddir, char *name)
6862{
6863 char *p;
6864 const char *cp;
6865 char *start;
6866 char *endname;
6867 int metaflag;
6868 struct stat statb;
6869 DIR *dirp;
6870 struct dirent *dp;
6871 int atend;
6872 int matchdot;
6873
6874 metaflag = 0;
6875 start = name;
6876 for (p = name; *p; p++) {
6877 if (*p == '*' || *p == '?')
6878 metaflag = 1;
6879 else if (*p == '[') {
6880 char *q = p + 1;
6881 if (*q == '!')
6882 q++;
6883 for (;;) {
6884 if (*q == '\\')
6885 q++;
6886 if (*q == '/' || *q == '\0')
6887 break;
6888 if (*++q == ']') {
6889 metaflag = 1;
6890 break;
6891 }
6892 }
6893 } else if (*p == '\\')
6894 p++;
6895 else if (*p == '/') {
6896 if (metaflag)
6897 goto out;
6898 start = p + 1;
6899 }
6900 }
6901 out:
6902 if (metaflag == 0) {
6903 if (enddir != expdir)
6904 metaflag++;
6905 p = name;
6906 do {
6907 if (*p == '\\')
6908 p++;
6909 *enddir++ = *p;
6910 } while (*p++);
6911 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6912 addfname(expdir);
6913 return;
6914 }
6915 endname = p;
6916 if (name < start) {
6917 p = name;
6918 do {
6919 if (*p == '\\')
6920 p++;
6921 *enddir++ = *p++;
6922 } while (p < start);
6923 }
6924 if (enddir == expdir) {
6925 cp = ".";
6926 } else if (enddir == expdir + 1 && *expdir == '/') {
6927 cp = "/";
6928 } else {
6929 cp = expdir;
6930 enddir[-1] = '\0';
6931 }
6932 dirp = opendir(cp);
6933 if (dirp == NULL)
6934 return;
6935 if (enddir != expdir)
6936 enddir[-1] = '/';
6937 if (*endname == 0) {
6938 atend = 1;
6939 } else {
6940 atend = 0;
6941 *endname++ = '\0';
6942 }
6943 matchdot = 0;
6944 p = start;
6945 if (*p == '\\')
6946 p++;
6947 if (*p == '.')
6948 matchdot++;
6949 while (!pending_int && (dp = readdir(dirp)) != NULL) {
6950 if (dp->d_name[0] == '.' && !matchdot)
6951 continue;
6952 if (pmatch(start, dp->d_name)) {
6953 if (atend) {
6954 strcpy(enddir, dp->d_name);
6955 addfname(expdir);
6956 } else {
6957 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6958 continue;
6959 p[-1] = '/';
6960 expmeta(expdir, p, endname);
6961 }
6962 }
6963 }
6964 closedir(dirp);
6965 if (!atend)
6966 endname[-1] = '/';
6967}
6968
6969static struct strlist *
6970msort(struct strlist *list, int len)
6971{
6972 struct strlist *p, *q = NULL;
6973 struct strlist **lpp;
6974 int half;
6975 int n;
6976
6977 if (len <= 1)
6978 return list;
6979 half = len >> 1;
6980 p = list;
6981 for (n = half; --n >= 0;) {
6982 q = p;
6983 p = p->next;
6984 }
6985 q->next = NULL;
6986 q = msort(list, half);
6987 p = msort(p, len - half);
6988 lpp = &list;
6989 for (;;) {
6990#if ENABLE_LOCALE_SUPPORT
6991 if (strcoll(p->text, q->text) < 0)
6992#else
6993 if (strcmp(p->text, q->text) < 0)
6994#endif
6995 {
6996 *lpp = p;
6997 lpp = &p->next;
6998 p = *lpp;
6999 if (p == NULL) {
7000 *lpp = q;
7001 break;
7002 }
7003 } else {
7004 *lpp = q;
7005 lpp = &q->next;
7006 q = *lpp;
7007 if (q == NULL) {
7008 *lpp = p;
7009 break;
7010 }
7011 }
7012 }
7013 return list;
7014}
7015
7016
7017
7018
7019
7020
7021static struct strlist *
7022expsort(struct strlist *str)
7023{
7024 int len;
7025 struct strlist *sp;
7026
7027 len = 0;
7028 for (sp = str; sp; sp = sp->next)
7029 len++;
7030 return msort(str, len);
7031}
7032
7033static void
7034expandmeta(struct strlist *str )
7035{
7036 static const char metachars[] ALIGN1 = {
7037 '*', '?', '[', 0
7038 };
7039
7040
7041 while (str) {
7042 char *expdir;
7043 struct strlist **savelastp;
7044 struct strlist *sp;
7045 char *p;
7046
7047 if (fflag)
7048 goto nometa;
7049 if (!strpbrk(str->text, metachars))
7050 goto nometa;
7051 savelastp = exparg.lastp;
7052
7053 INT_OFF;
7054 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7055 {
7056 int i = strlen(str->text);
7057 expdir = ckmalloc(i < 2048 ? 2048 : i);
7058 }
7059 expmeta(expdir, expdir, p);
7060 free(expdir);
7061 if (p != str->text)
7062 free(p);
7063 INT_ON;
7064 if (exparg.lastp == savelastp) {
7065
7066
7067
7068 nometa:
7069 *exparg.lastp = str;
7070 rmescapes(str->text, 0);
7071 exparg.lastp = &str->next;
7072 } else {
7073 *exparg.lastp = NULL;
7074 *savelastp = sp = expsort(*savelastp);
7075 while (sp->next != NULL)
7076 sp = sp->next;
7077 exparg.lastp = &sp->next;
7078 }
7079 str = str->next;
7080 }
7081}
7082
7083
7084
7085
7086
7087
7088
7089static void
7090expandarg(union node *arg, struct arglist *arglist, int flag)
7091{
7092 struct strlist *sp;
7093 char *p;
7094
7095 argbackq = arg->narg.backquote;
7096 STARTSTACKSTR(expdest);
7097 ifsfirst.next = NULL;
7098 ifslastp = NULL;
7099 argstr(arg->narg.text, flag,
7100 arglist ? arglist->list : NULL);
7101 p = _STPUTC('\0', expdest);
7102 expdest = p - 1;
7103 if (arglist == NULL) {
7104 return;
7105 }
7106 p = grabstackstr(p);
7107 exparg.lastp = &exparg.list;
7108
7109
7110
7111 if (flag & EXP_FULL) {
7112 ifsbreakup(p, &exparg);
7113 *exparg.lastp = NULL;
7114 exparg.lastp = &exparg.list;
7115 expandmeta(exparg.list );
7116 } else {
7117 if (flag & EXP_REDIR)
7118 rmescapes(p, 0);
7119 sp = stzalloc(sizeof(*sp));
7120 sp->text = p;
7121 *exparg.lastp = sp;
7122 exparg.lastp = &sp->next;
7123 }
7124 if (ifsfirst.next)
7125 ifsfree();
7126 *exparg.lastp = NULL;
7127 if (exparg.list) {
7128 *arglist->lastp = exparg.list;
7129 arglist->lastp = exparg.lastp;
7130 }
7131}
7132
7133
7134
7135
7136static void
7137expandhere(union node *arg, int fd)
7138{
7139 herefd = fd;
7140 expandarg(arg, (struct arglist *)NULL, 0);
7141 full_write(fd, stackblock(), expdest - (char *)stackblock());
7142}
7143
7144
7145
7146
7147static int
7148patmatch(char *pattern, const char *string)
7149{
7150 return pmatch(preglob(pattern, 0, 0), string);
7151}
7152
7153
7154
7155
7156static int
7157casematch(union node *pattern, char *val)
7158{
7159 struct stackmark smark;
7160 int result;
7161
7162 setstackmark(&smark);
7163 argbackq = pattern->narg.backquote;
7164 STARTSTACKSTR(expdest);
7165 ifslastp = NULL;
7166 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7167 NULL);
7168 STACKSTRNUL(expdest);
7169 result = patmatch(stackblock(), val);
7170 popstackmark(&smark);
7171 return result;
7172}
7173
7174
7175
7176
7177struct builtincmd {
7178 const char *name;
7179 int (*builtin)(int, char **) FAST_FUNC;
7180
7181};
7182#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
7183
7184
7185#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
7186#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
7187
7188struct cmdentry {
7189 smallint cmdtype;
7190 union param {
7191 int index;
7192
7193
7194
7195
7196 const struct builtincmd *cmd;
7197 struct funcnode *func;
7198 } u;
7199};
7200
7201#define CMDUNKNOWN -1
7202#define CMDNORMAL 0
7203#define CMDFUNCTION 1
7204#define CMDBUILTIN 2
7205
7206
7207#define DO_ERR 0x01
7208#define DO_ABS 0x02
7209#define DO_NOFUNC 0x04
7210#define DO_ALTPATH 0x08
7211#define DO_ALTBLTIN 0x20
7212
7213static void find_command(char *, struct cmdentry *, int, const char *);
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227struct tblentry {
7228 struct tblentry *next;
7229 union param param;
7230 smallint cmdtype;
7231 char rehash;
7232 char cmdname[1];
7233};
7234
7235static struct tblentry **cmdtable;
7236#define INIT_G_cmdtable() do { \
7237 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7238} while (0)
7239
7240static int builtinloc = -1;
7241
7242
7243static void
7244tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
7245{
7246 int repeated = 0;
7247
7248#if ENABLE_FEATURE_SH_STANDALONE
7249 if (applet_no >= 0) {
7250 if (APPLET_IS_NOEXEC(applet_no)) {
7251 clearenv();
7252 while (*envp)
7253 putenv(*envp++);
7254 run_applet_no_and_exit(applet_no, argv);
7255 }
7256
7257 execve(bb_busybox_exec_path, argv, envp);
7258
7259
7260 }
7261#endif
7262
7263 repeat:
7264#ifdef SYSV
7265 do {
7266 execve(cmd, argv, envp);
7267 } while (errno == EINTR);
7268#else
7269 execve(cmd, argv, envp);
7270#endif
7271 if (repeated) {
7272 free(argv);
7273 return;
7274 }
7275 if (errno == ENOEXEC) {
7276 char **ap;
7277 char **new;
7278
7279 for (ap = argv; *ap; ap++)
7280 continue;
7281 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
7282 ap[1] = cmd;
7283 ap[0] = cmd = (char *)DEFAULT_SHELL;
7284 ap += 2;
7285 argv++;
7286 while ((*ap++ = *argv++) != NULL)
7287 continue;
7288 argv = new;
7289 repeated++;
7290 goto repeat;
7291 }
7292}
7293
7294
7295
7296
7297
7298static void shellexec(char **, const char *, int) NORETURN;
7299static void
7300shellexec(char **argv, const char *path, int idx)
7301{
7302 char *cmdname;
7303 int e;
7304 char **envp;
7305 int exerrno;
7306#if ENABLE_FEATURE_SH_STANDALONE
7307 int applet_no = -1;
7308#endif
7309
7310 clearredir( 1);
7311 envp = listvars(VEXPORT, VUNSET, NULL);
7312 if (strchr(argv[0], '/') != NULL
7313#if ENABLE_FEATURE_SH_STANDALONE
7314 || (applet_no = find_applet_by_name(argv[0])) >= 0
7315#endif
7316 ) {
7317 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
7318 e = errno;
7319 } else {
7320 e = ENOENT;
7321 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
7322 if (--idx < 0 && pathopt == NULL) {
7323 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
7324 if (errno != ENOENT && errno != ENOTDIR)
7325 e = errno;
7326 }
7327 stunalloc(cmdname);
7328 }
7329 }
7330
7331
7332 switch (e) {
7333 case EACCES:
7334 exerrno = 126;
7335 break;
7336 case ENOENT:
7337 exerrno = 127;
7338 break;
7339 default:
7340 exerrno = 2;
7341 break;
7342 }
7343 exitstatus = exerrno;
7344 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7345 argv[0], e, suppress_int));
7346 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7347
7348}
7349
7350static void
7351printentry(struct tblentry *cmdp)
7352{
7353 int idx;
7354 const char *path;
7355 char *name;
7356
7357 idx = cmdp->param.index;
7358 path = pathval();
7359 do {
7360 name = path_advance(&path, cmdp->cmdname);
7361 stunalloc(name);
7362 } while (--idx >= 0);
7363 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7364}
7365
7366
7367
7368
7369
7370static void
7371clearcmdentry(int firstchange)
7372{
7373 struct tblentry **tblp;
7374 struct tblentry **pp;
7375 struct tblentry *cmdp;
7376
7377 INT_OFF;
7378 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7379 pp = tblp;
7380 while ((cmdp = *pp) != NULL) {
7381 if ((cmdp->cmdtype == CMDNORMAL &&
7382 cmdp->param.index >= firstchange)
7383 || (cmdp->cmdtype == CMDBUILTIN &&
7384 builtinloc >= firstchange)
7385 ) {
7386 *pp = cmdp->next;
7387 free(cmdp);
7388 } else {
7389 pp = &cmdp->next;
7390 }
7391 }
7392 }
7393 INT_ON;
7394}
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405static struct tblentry **lastcmdentry;
7406
7407static struct tblentry *
7408cmdlookup(const char *name, int add)
7409{
7410 unsigned int hashval;
7411 const char *p;
7412 struct tblentry *cmdp;
7413 struct tblentry **pp;
7414
7415 p = name;
7416 hashval = (unsigned char)*p << 4;
7417 while (*p)
7418 hashval += (unsigned char)*p++;
7419 hashval &= 0x7FFF;
7420 pp = &cmdtable[hashval % CMDTABLESIZE];
7421 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7422 if (strcmp(cmdp->cmdname, name) == 0)
7423 break;
7424 pp = &cmdp->next;
7425 }
7426 if (add && cmdp == NULL) {
7427 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7428 + strlen(name)
7429
7430);
7431
7432 cmdp->cmdtype = CMDUNKNOWN;
7433 strcpy(cmdp->cmdname, name);
7434 }
7435 lastcmdentry = pp;
7436 return cmdp;
7437}
7438
7439
7440
7441
7442static void
7443delete_cmd_entry(void)
7444{
7445 struct tblentry *cmdp;
7446
7447 INT_OFF;
7448 cmdp = *lastcmdentry;
7449 *lastcmdentry = cmdp->next;
7450 if (cmdp->cmdtype == CMDFUNCTION)
7451 freefunc(cmdp->param.func);
7452 free(cmdp);
7453 INT_ON;
7454}
7455
7456
7457
7458
7459
7460static void
7461addcmdentry(char *name, struct cmdentry *entry)
7462{
7463 struct tblentry *cmdp;
7464
7465 cmdp = cmdlookup(name, 1);
7466 if (cmdp->cmdtype == CMDFUNCTION) {
7467 freefunc(cmdp->param.func);
7468 }
7469 cmdp->cmdtype = entry->cmdtype;
7470 cmdp->param = entry->u;
7471 cmdp->rehash = 0;
7472}
7473
7474static int FAST_FUNC
7475hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
7476{
7477 struct tblentry **pp;
7478 struct tblentry *cmdp;
7479 int c;
7480 struct cmdentry entry;
7481 char *name;
7482
7483 if (nextopt("r") != '\0') {
7484 clearcmdentry(0);
7485 return 0;
7486 }
7487
7488 if (*argptr == NULL) {
7489 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7490 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7491 if (cmdp->cmdtype == CMDNORMAL)
7492 printentry(cmdp);
7493 }
7494 }
7495 return 0;
7496 }
7497
7498 c = 0;
7499 while ((name = *argptr) != NULL) {
7500 cmdp = cmdlookup(name, 0);
7501 if (cmdp != NULL
7502 && (cmdp->cmdtype == CMDNORMAL
7503 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7504 ) {
7505 delete_cmd_entry();
7506 }
7507 find_command(name, &entry, DO_ERR, pathval());
7508 if (entry.cmdtype == CMDUNKNOWN)
7509 c = 1;
7510 argptr++;
7511 }
7512 return c;
7513}
7514
7515
7516
7517
7518
7519static void
7520hashcd(void)
7521{
7522 struct tblentry **pp;
7523 struct tblentry *cmdp;
7524
7525 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7526 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7527 if (cmdp->cmdtype == CMDNORMAL
7528 || (cmdp->cmdtype == CMDBUILTIN
7529 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7530 && builtinloc > 0)
7531 ) {
7532 cmdp->rehash = 1;
7533 }
7534 }
7535 }
7536}
7537
7538
7539
7540
7541
7542
7543
7544static void FAST_FUNC
7545changepath(const char *new)
7546{
7547 const char *old;
7548 int firstchange;
7549 int idx;
7550 int idx_bltin;
7551
7552 old = pathval();
7553 firstchange = 9999;
7554 idx = 0;
7555 idx_bltin = -1;
7556 for (;;) {
7557 if (*old != *new) {
7558 firstchange = idx;
7559 if ((*old == '\0' && *new == ':')
7560 || (*old == ':' && *new == '\0')
7561 ) {
7562 firstchange++;
7563 }
7564 old = new;
7565 }
7566 if (*new == '\0')
7567 break;
7568 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7569 idx_bltin = idx;
7570 if (*new == ':')
7571 idx++;
7572 new++;
7573 old++;
7574 }
7575 if (builtinloc < 0 && idx_bltin >= 0)
7576 builtinloc = idx_bltin;
7577 if (builtinloc >= 0 && idx_bltin < 0)
7578 firstchange = 0;
7579 clearcmdentry(firstchange);
7580 builtinloc = idx_bltin;
7581}
7582
7583#define TEOF 0
7584#define TNL 1
7585#define TREDIR 2
7586#define TWORD 3
7587#define TSEMI 4
7588#define TBACKGND 5
7589#define TAND 6
7590#define TOR 7
7591#define TPIPE 8
7592#define TLP 9
7593#define TRP 10
7594#define TENDCASE 11
7595#define TENDBQUOTE 12
7596#define TNOT 13
7597#define TCASE 14
7598#define TDO 15
7599#define TDONE 16
7600#define TELIF 17
7601#define TELSE 18
7602#define TESAC 19
7603#define TFI 20
7604#define TFOR 21
7605#define TIF 22
7606#define TIN 23
7607#define TTHEN 24
7608#define TUNTIL 25
7609#define TWHILE 26
7610#define TBEGIN 27
7611#define TEND 28
7612typedef smallint token_id_t;
7613
7614
7615static const char *const tokname_array[] = {
7616 "\1end of file",
7617 "\0newline",
7618 "\0redirection",
7619 "\0word",
7620 "\0;",
7621 "\0&",
7622 "\0&&",
7623 "\0||",
7624 "\0|",
7625 "\0(",
7626 "\1)",
7627 "\1;;",
7628 "\1`",
7629#define KWDOFFSET 13
7630
7631 "\0!",
7632 "\0case",
7633 "\1do",
7634 "\1done",
7635 "\1elif",
7636 "\1else",
7637 "\1esac",
7638 "\1fi",
7639 "\0for",
7640 "\0if",
7641 "\0in",
7642 "\1then",
7643 "\0until",
7644 "\0while",
7645 "\0{",
7646 "\1}",
7647};
7648
7649
7650static int
7651pstrcmp(const void *a, const void *b)
7652{
7653 return strcmp((char*) a, (*(char**) b) + 1);
7654}
7655
7656static const char *const *
7657findkwd(const char *s)
7658{
7659 return bsearch(s, tokname_array + KWDOFFSET,
7660 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7661 sizeof(tokname_array[0]), pstrcmp);
7662}
7663
7664
7665
7666
7667static int
7668describe_command(char *command, int describe_command_verbose)
7669{
7670 struct cmdentry entry;
7671 struct tblentry *cmdp;
7672#if ENABLE_ASH_ALIAS
7673 const struct alias *ap;
7674#endif
7675 const char *path = pathval();
7676
7677 if (describe_command_verbose) {
7678 out1str(command);
7679 }
7680
7681
7682 if (findkwd(command)) {
7683 out1str(describe_command_verbose ? " is a shell keyword" : command);
7684 goto out;
7685 }
7686
7687#if ENABLE_ASH_ALIAS
7688
7689 ap = lookupalias(command, 0);
7690 if (ap != NULL) {
7691 if (!describe_command_verbose) {
7692 out1str("alias ");
7693 printalias(ap);
7694 return 0;
7695 }
7696 out1fmt(" is an alias for %s", ap->val);
7697 goto out;
7698 }
7699#endif
7700
7701 cmdp = cmdlookup(command, 0);
7702 if (cmdp != NULL) {
7703 entry.cmdtype = cmdp->cmdtype;
7704 entry.u = cmdp->param;
7705 } else {
7706
7707 find_command(command, &entry, DO_ABS, path);
7708 }
7709
7710 switch (entry.cmdtype) {
7711 case CMDNORMAL: {
7712 int j = entry.u.index;
7713 char *p;
7714 if (j < 0) {
7715 p = command;
7716 } else {
7717 do {
7718 p = path_advance(&path, command);
7719 stunalloc(p);
7720 } while (--j >= 0);
7721 }
7722 if (describe_command_verbose) {
7723 out1fmt(" is%s %s",
7724 (cmdp ? " a tracked alias for" : nullstr), p
7725 );
7726 } else {
7727 out1str(p);
7728 }
7729 break;
7730 }
7731
7732 case CMDFUNCTION:
7733 if (describe_command_verbose) {
7734 out1str(" is a shell function");
7735 } else {
7736 out1str(command);
7737 }
7738 break;
7739
7740 case CMDBUILTIN:
7741 if (describe_command_verbose) {
7742 out1fmt(" is a %sshell builtin",
7743 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7744 "special " : nullstr
7745 );
7746 } else {
7747 out1str(command);
7748 }
7749 break;
7750
7751 default:
7752 if (describe_command_verbose) {
7753 out1str(": not found\n");
7754 }
7755 return 127;
7756 }
7757 out:
7758 out1str("\n");
7759 return 0;
7760}
7761
7762static int FAST_FUNC
7763typecmd(int argc UNUSED_PARAM, char **argv)
7764{
7765 int i = 1;
7766 int err = 0;
7767 int verbose = 1;
7768
7769
7770 if (argv[1] && argv[1][0] == '-') {
7771 i++;
7772 verbose = 0;
7773 }
7774 while (argv[i]) {
7775 err |= describe_command(argv[i++], verbose);
7776 }
7777 return err;
7778}
7779
7780#if ENABLE_ASH_CMDCMD
7781static int FAST_FUNC
7782commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
7783{
7784 int c;
7785 enum {
7786 VERIFY_BRIEF = 1,
7787 VERIFY_VERBOSE = 2,
7788 } verify = 0;
7789
7790 while ((c = nextopt("pvV")) != '\0')
7791 if (c == 'V')
7792 verify |= VERIFY_VERBOSE;
7793 else if (c == 'v')
7794 verify |= VERIFY_BRIEF;
7795#if DEBUG
7796 else if (c != 'p')
7797 abort();
7798#endif
7799
7800 if (verify && (*argptr != NULL)) {
7801 return describe_command(*argptr, verify - VERIFY_BRIEF);
7802 }
7803
7804 return 0;
7805}
7806#endif
7807
7808
7809
7810
7811static int funcblocksize;
7812static int funcstringsize;
7813static void *funcblock;
7814static char *funcstring;
7815
7816
7817#define EV_EXIT 01
7818#define EV_TESTED 02
7819#define EV_BACKCMD 04
7820
7821static const uint8_t nodesize[N_NUMBER] = {
7822 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7823 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7824 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7825 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7826 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7827 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7828 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7829 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7830 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7831 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7832 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7833 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7834 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7835 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7836 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7837 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7838 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7839#if ENABLE_ASH_BASH_COMPAT
7840 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
7841#endif
7842 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7843 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7844 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7845 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7846 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7847 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7848 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7849 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7850 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
7851};
7852
7853static void calcsize(union node *n);
7854
7855static void
7856sizenodelist(struct nodelist *lp)
7857{
7858 while (lp) {
7859 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7860 calcsize(lp->n);
7861 lp = lp->next;
7862 }
7863}
7864
7865static void
7866calcsize(union node *n)
7867{
7868 if (n == NULL)
7869 return;
7870 funcblocksize += nodesize[n->type];
7871 switch (n->type) {
7872 case NCMD:
7873 calcsize(n->ncmd.redirect);
7874 calcsize(n->ncmd.args);
7875 calcsize(n->ncmd.assign);
7876 break;
7877 case NPIPE:
7878 sizenodelist(n->npipe.cmdlist);
7879 break;
7880 case NREDIR:
7881 case NBACKGND:
7882 case NSUBSHELL:
7883 calcsize(n->nredir.redirect);
7884 calcsize(n->nredir.n);
7885 break;
7886 case NAND:
7887 case NOR:
7888 case NSEMI:
7889 case NWHILE:
7890 case NUNTIL:
7891 calcsize(n->nbinary.ch2);
7892 calcsize(n->nbinary.ch1);
7893 break;
7894 case NIF:
7895 calcsize(n->nif.elsepart);
7896 calcsize(n->nif.ifpart);
7897 calcsize(n->nif.test);
7898 break;
7899 case NFOR:
7900 funcstringsize += strlen(n->nfor.var) + 1;
7901 calcsize(n->nfor.body);
7902 calcsize(n->nfor.args);
7903 break;
7904 case NCASE:
7905 calcsize(n->ncase.cases);
7906 calcsize(n->ncase.expr);
7907 break;
7908 case NCLIST:
7909 calcsize(n->nclist.body);
7910 calcsize(n->nclist.pattern);
7911 calcsize(n->nclist.next);
7912 break;
7913 case NDEFUN:
7914 case NARG:
7915 sizenodelist(n->narg.backquote);
7916 funcstringsize += strlen(n->narg.text) + 1;
7917 calcsize(n->narg.next);
7918 break;
7919 case NTO:
7920#if ENABLE_ASH_BASH_COMPAT
7921 case NTO2:
7922#endif
7923 case NCLOBBER:
7924 case NFROM:
7925 case NFROMTO:
7926 case NAPPEND:
7927 calcsize(n->nfile.fname);
7928 calcsize(n->nfile.next);
7929 break;
7930 case NTOFD:
7931 case NFROMFD:
7932 calcsize(n->ndup.vname);
7933 calcsize(n->ndup.next);
7934 break;
7935 case NHERE:
7936 case NXHERE:
7937 calcsize(n->nhere.doc);
7938 calcsize(n->nhere.next);
7939 break;
7940 case NNOT:
7941 calcsize(n->nnot.com);
7942 break;
7943 };
7944}
7945
7946static char *
7947nodeckstrdup(char *s)
7948{
7949 char *rtn = funcstring;
7950
7951 strcpy(funcstring, s);
7952 funcstring += strlen(s) + 1;
7953 return rtn;
7954}
7955
7956static union node *copynode(union node *);
7957
7958static struct nodelist *
7959copynodelist(struct nodelist *lp)
7960{
7961 struct nodelist *start;
7962 struct nodelist **lpp;
7963
7964 lpp = &start;
7965 while (lp) {
7966 *lpp = funcblock;
7967 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7968 (*lpp)->n = copynode(lp->n);
7969 lp = lp->next;
7970 lpp = &(*lpp)->next;
7971 }
7972 *lpp = NULL;
7973 return start;
7974}
7975
7976static union node *
7977copynode(union node *n)
7978{
7979 union node *new;
7980
7981 if (n == NULL)
7982 return NULL;
7983 new = funcblock;
7984 funcblock = (char *) funcblock + nodesize[n->type];
7985
7986 switch (n->type) {
7987 case NCMD:
7988 new->ncmd.redirect = copynode(n->ncmd.redirect);
7989 new->ncmd.args = copynode(n->ncmd.args);
7990 new->ncmd.assign = copynode(n->ncmd.assign);
7991 break;
7992 case NPIPE:
7993 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7994 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
7995 break;
7996 case NREDIR:
7997 case NBACKGND:
7998 case NSUBSHELL:
7999 new->nredir.redirect = copynode(n->nredir.redirect);
8000 new->nredir.n = copynode(n->nredir.n);
8001 break;
8002 case NAND:
8003 case NOR:
8004 case NSEMI:
8005 case NWHILE:
8006 case NUNTIL:
8007 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8008 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8009 break;
8010 case NIF:
8011 new->nif.elsepart = copynode(n->nif.elsepart);
8012 new->nif.ifpart = copynode(n->nif.ifpart);
8013 new->nif.test = copynode(n->nif.test);
8014 break;
8015 case NFOR:
8016 new->nfor.var = nodeckstrdup(n->nfor.var);
8017 new->nfor.body = copynode(n->nfor.body);
8018 new->nfor.args = copynode(n->nfor.args);
8019 break;
8020 case NCASE:
8021 new->ncase.cases = copynode(n->ncase.cases);
8022 new->ncase.expr = copynode(n->ncase.expr);
8023 break;
8024 case NCLIST:
8025 new->nclist.body = copynode(n->nclist.body);
8026 new->nclist.pattern = copynode(n->nclist.pattern);
8027 new->nclist.next = copynode(n->nclist.next);
8028 break;
8029 case NDEFUN:
8030 case NARG:
8031 new->narg.backquote = copynodelist(n->narg.backquote);
8032 new->narg.text = nodeckstrdup(n->narg.text);
8033 new->narg.next = copynode(n->narg.next);
8034 break;
8035 case NTO:
8036#if ENABLE_ASH_BASH_COMPAT
8037 case NTO2:
8038#endif
8039 case NCLOBBER:
8040 case NFROM:
8041 case NFROMTO:
8042 case NAPPEND:
8043 new->nfile.fname = copynode(n->nfile.fname);
8044 new->nfile.fd = n->nfile.fd;
8045 new->nfile.next = copynode(n->nfile.next);
8046 break;
8047 case NTOFD:
8048 case NFROMFD:
8049 new->ndup.vname = copynode(n->ndup.vname);
8050 new->ndup.dupfd = n->ndup.dupfd;
8051 new->ndup.fd = n->ndup.fd;
8052 new->ndup.next = copynode(n->ndup.next);
8053 break;
8054 case NHERE:
8055 case NXHERE:
8056 new->nhere.doc = copynode(n->nhere.doc);
8057 new->nhere.fd = n->nhere.fd;
8058 new->nhere.next = copynode(n->nhere.next);
8059 break;
8060 case NNOT:
8061 new->nnot.com = copynode(n->nnot.com);
8062 break;
8063 };
8064 new->type = n->type;
8065 return new;
8066}
8067
8068
8069
8070
8071static struct funcnode *
8072copyfunc(union node *n)
8073{
8074 struct funcnode *f;
8075 size_t blocksize;
8076
8077 funcblocksize = offsetof(struct funcnode, n);
8078 funcstringsize = 0;
8079 calcsize(n);
8080 blocksize = funcblocksize;
8081 f = ckmalloc(blocksize + funcstringsize);
8082 funcblock = (char *) f + offsetof(struct funcnode, n);
8083 funcstring = (char *) f + blocksize;
8084 copynode(n);
8085 f->count = 0;
8086 return f;
8087}
8088
8089
8090
8091
8092static void
8093defun(char *name, union node *func)
8094{
8095 struct cmdentry entry;
8096
8097 INT_OFF;
8098 entry.cmdtype = CMDFUNCTION;
8099 entry.u.func = copyfunc(func);
8100 addcmdentry(name, &entry);
8101 INT_ON;
8102}
8103
8104
8105#define SKIPBREAK (1 << 0)
8106#define SKIPCONT (1 << 1)
8107#define SKIPFUNC (1 << 2)
8108#define SKIPFILE (1 << 3)
8109#define SKIPEVAL (1 << 4)
8110static smallint evalskip;
8111static int skipcount;
8112static int funcnest;
8113static int loopnest;
8114
8115
8116static int evalstring(char *s, int mask);
8117
8118
8119
8120
8121
8122
8123
8124
8125static int
8126dotrap(void)
8127{
8128 uint8_t *g;
8129 int sig;
8130 uint8_t savestatus;
8131
8132 savestatus = exitstatus;
8133 pending_sig = 0;
8134 xbarrier();
8135
8136 TRACE(("dotrap entered\n"));
8137 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8138 int want_exexit;
8139 char *t;
8140
8141 if (*g == 0)
8142 continue;
8143 t = trap[sig];
8144
8145
8146 if (sig == SIGINT && !t)
8147 continue;
8148
8149 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
8150 *g = 0;
8151 if (!t)
8152 continue;
8153 want_exexit = evalstring(t, SKIPEVAL);
8154 exitstatus = savestatus;
8155 if (want_exexit) {
8156 TRACE(("dotrap returns %d\n", want_exexit));
8157 return want_exexit;
8158 }
8159 }
8160
8161 TRACE(("dotrap returns 0\n"));
8162 return 0;
8163}
8164
8165
8166static void evalloop(union node *, int);
8167static void evalfor(union node *, int);
8168static void evalcase(union node *, int);
8169static void evalsubshell(union node *, int);
8170static void expredir(union node *);
8171static void evalpipe(union node *, int);
8172static void evalcommand(union node *, int);
8173static int evalbltin(const struct builtincmd *, int, char **);
8174static void prehash(union node *);
8175
8176
8177
8178
8179
8180static void
8181evaltree(union node *n, int flags)
8182{
8183 struct jmploc *volatile savehandler = exception_handler;
8184 struct jmploc jmploc;
8185 int checkexit = 0;
8186 void (*evalfn)(union node *, int);
8187 int status;
8188 int int_level;
8189
8190 SAVE_INT(int_level);
8191
8192 if (n == NULL) {
8193 TRACE(("evaltree(NULL) called\n"));
8194 goto out1;
8195 }
8196 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
8197
8198 exception_handler = &jmploc;
8199 {
8200 int err = setjmp(jmploc.loc);
8201 if (err) {
8202
8203 if (exception_type == EXSIG) {
8204 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8205 exception_type, err));
8206 goto out;
8207 }
8208
8209 TRACE(("exception %d in evaltree, propagating err=%d\n",
8210 exception_type, err));
8211 exception_handler = savehandler;
8212 longjmp(exception_handler->loc, err);
8213 }
8214 }
8215
8216 switch (n->type) {
8217 default:
8218#if DEBUG
8219 out1fmt("Node type = %d\n", n->type);
8220 fflush_all();
8221 break;
8222#endif
8223 case NNOT:
8224 evaltree(n->nnot.com, EV_TESTED);
8225 status = !exitstatus;
8226 goto setstatus;
8227 case NREDIR:
8228 expredir(n->nredir.redirect);
8229 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8230 if (!status) {
8231 evaltree(n->nredir.n, flags & EV_TESTED);
8232 status = exitstatus;
8233 }
8234 popredir( 0, 0 );
8235 goto setstatus;
8236 case NCMD:
8237 evalfn = evalcommand;
8238 checkexit:
8239 if (eflag && !(flags & EV_TESTED))
8240 checkexit = ~0;
8241 goto calleval;
8242 case NFOR:
8243 evalfn = evalfor;
8244 goto calleval;
8245 case NWHILE:
8246 case NUNTIL:
8247 evalfn = evalloop;
8248 goto calleval;
8249 case NSUBSHELL:
8250 case NBACKGND:
8251 evalfn = evalsubshell;
8252 goto calleval;
8253 case NPIPE:
8254 evalfn = evalpipe;
8255 goto checkexit;
8256 case NCASE:
8257 evalfn = evalcase;
8258 goto calleval;
8259 case NAND:
8260 case NOR:
8261 case NSEMI: {
8262
8263#if NAND + 1 != NOR
8264#error NAND + 1 != NOR
8265#endif
8266#if NOR + 1 != NSEMI
8267#error NOR + 1 != NSEMI
8268#endif
8269 unsigned is_or = n->type - NAND;
8270 evaltree(
8271 n->nbinary.ch1,
8272 (flags | ((is_or >> 1) - 1)) & EV_TESTED
8273 );
8274 if (!exitstatus == is_or)
8275 break;
8276 if (!evalskip) {
8277 n = n->nbinary.ch2;
8278 evaln:
8279 evalfn = evaltree;
8280 calleval:
8281 evalfn(n, flags);
8282 break;
8283 }
8284 break;
8285 }
8286 case NIF:
8287 evaltree(n->nif.test, EV_TESTED);
8288 if (evalskip)
8289 break;
8290 if (exitstatus == 0) {
8291 n = n->nif.ifpart;
8292 goto evaln;
8293 }
8294 if (n->nif.elsepart) {
8295 n = n->nif.elsepart;
8296 goto evaln;
8297 }
8298 goto success;
8299 case NDEFUN:
8300 defun(n->narg.text, n->narg.next);
8301 success:
8302 status = 0;
8303 setstatus:
8304 exitstatus = status;
8305 break;
8306 }
8307
8308 out:
8309 exception_handler = savehandler;
8310 out1:
8311 if (checkexit & exitstatus)
8312 evalskip |= SKIPEVAL;
8313 else if (pending_sig && dotrap())
8314 goto exexit;
8315
8316 if (flags & EV_EXIT) {
8317 exexit:
8318 raise_exception(EXEXIT);
8319 }
8320
8321 RESTORE_INT(int_level);
8322 TRACE(("leaving evaltree (no interrupts)\n"));
8323}
8324
8325#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8326static
8327#endif
8328void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8329
8330static void
8331evalloop(union node *n, int flags)
8332{
8333 int status;
8334
8335 loopnest++;
8336 status = 0;
8337 flags &= EV_TESTED;
8338 for (;;) {
8339 int i;
8340
8341 evaltree(n->nbinary.ch1, EV_TESTED);
8342 if (evalskip) {
8343 skipping:
8344 if (evalskip == SKIPCONT && --skipcount <= 0) {
8345 evalskip = 0;
8346 continue;
8347 }
8348 if (evalskip == SKIPBREAK && --skipcount <= 0)
8349 evalskip = 0;
8350 break;
8351 }
8352 i = exitstatus;
8353 if (n->type != NWHILE)
8354 i = !i;
8355 if (i != 0)
8356 break;
8357 evaltree(n->nbinary.ch2, flags);
8358 status = exitstatus;
8359 if (evalskip)
8360 goto skipping;
8361 }
8362 loopnest--;
8363 exitstatus = status;
8364}
8365
8366static void
8367evalfor(union node *n, int flags)
8368{
8369 struct arglist arglist;
8370 union node *argp;
8371 struct strlist *sp;
8372 struct stackmark smark;
8373
8374 setstackmark(&smark);
8375 arglist.list = NULL;
8376 arglist.lastp = &arglist.list;
8377 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
8378 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
8379
8380 if (evalskip)
8381 goto out;
8382 }
8383 *arglist.lastp = NULL;
8384
8385 exitstatus = 0;
8386 loopnest++;
8387 flags &= EV_TESTED;
8388 for (sp = arglist.list; sp; sp = sp->next) {
8389 setvar(n->nfor.var, sp->text, 0);
8390 evaltree(n->nfor.body, flags);
8391 if (evalskip) {
8392 if (evalskip == SKIPCONT && --skipcount <= 0) {
8393 evalskip = 0;
8394 continue;
8395 }
8396 if (evalskip == SKIPBREAK && --skipcount <= 0)
8397 evalskip = 0;
8398 break;
8399 }
8400 }
8401 loopnest--;
8402 out:
8403 popstackmark(&smark);
8404}
8405
8406static void
8407evalcase(union node *n, int flags)
8408{
8409 union node *cp;
8410 union node *patp;
8411 struct arglist arglist;
8412 struct stackmark smark;
8413
8414 setstackmark(&smark);
8415 arglist.list = NULL;
8416 arglist.lastp = &arglist.list;
8417 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
8418 exitstatus = 0;
8419 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8420 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
8421 if (casematch(patp, arglist.list->text)) {
8422 if (evalskip == 0) {
8423 evaltree(cp->nclist.body, flags);
8424 }
8425 goto out;
8426 }
8427 }
8428 }
8429 out:
8430 popstackmark(&smark);
8431}
8432
8433
8434
8435
8436static void
8437evalsubshell(union node *n, int flags)
8438{
8439 struct job *jp;
8440 int backgnd = (n->type == NBACKGND);
8441 int status;
8442
8443 expredir(n->nredir.redirect);
8444 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
8445 goto nofork;
8446 INT_OFF;
8447 jp = makejob( 1);
8448 if (forkshell(jp, n, backgnd) == 0) {
8449
8450 INT_ON;
8451 flags |= EV_EXIT;
8452 if (backgnd)
8453 flags &= ~EV_TESTED;
8454 nofork:
8455 redirect(n->nredir.redirect, 0);
8456 evaltreenr(n->nredir.n, flags);
8457
8458 }
8459 status = 0;
8460 if (!backgnd)
8461 status = waitforjob(jp);
8462 exitstatus = status;
8463 INT_ON;
8464}
8465
8466
8467
8468
8469static void fixredir(union node *, const char *, int);
8470static void
8471expredir(union node *n)
8472{
8473 union node *redir;
8474
8475 for (redir = n; redir; redir = redir->nfile.next) {
8476 struct arglist fn;
8477
8478 fn.list = NULL;
8479 fn.lastp = &fn.list;
8480 switch (redir->type) {
8481 case NFROMTO:
8482 case NFROM:
8483 case NTO:
8484#if ENABLE_ASH_BASH_COMPAT
8485 case NTO2:
8486#endif
8487 case NCLOBBER:
8488 case NAPPEND:
8489 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8490#if ENABLE_ASH_BASH_COMPAT
8491 store_expfname:
8492#endif
8493 redir->nfile.expfname = fn.list->text;
8494 break;
8495 case NFROMFD:
8496 case NTOFD:
8497 if (redir->ndup.vname) {
8498 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
8499 if (fn.list == NULL)
8500 ash_msg_and_raise_error("redir error");
8501#if ENABLE_ASH_BASH_COMPAT
8502
8503 if (!isdigit_str9(fn.list->text)) {
8504
8505 if (redir->nfile.fd != 1)
8506 ash_msg_and_raise_error("redir error");
8507 redir->type = NTO2;
8508 goto store_expfname;
8509 }
8510#endif
8511 fixredir(redir, fn.list->text, 1);
8512 }
8513 break;
8514 }
8515 }
8516}
8517
8518
8519
8520
8521
8522
8523
8524static void
8525evalpipe(union node *n, int flags)
8526{
8527 struct job *jp;
8528 struct nodelist *lp;
8529 int pipelen;
8530 int prevfd;
8531 int pip[2];
8532
8533 TRACE(("evalpipe(0x%lx) called\n", (long)n));
8534 pipelen = 0;
8535 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
8536 pipelen++;
8537 flags |= EV_EXIT;
8538 INT_OFF;
8539 jp = makejob( pipelen);
8540 prevfd = -1;
8541 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
8542 prehash(lp->n);
8543 pip[1] = -1;
8544 if (lp->next) {
8545 if (pipe(pip) < 0) {
8546 close(prevfd);
8547 ash_msg_and_raise_error("pipe call failed");
8548 }
8549 }
8550 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
8551 INT_ON;
8552 if (pip[1] >= 0) {
8553 close(pip[0]);
8554 }
8555 if (prevfd > 0) {
8556 dup2(prevfd, 0);
8557 close(prevfd);
8558 }
8559 if (pip[1] > 1) {
8560 dup2(pip[1], 1);
8561 close(pip[1]);
8562 }
8563 evaltreenr(lp->n, flags);
8564
8565 }
8566 if (prevfd >= 0)
8567 close(prevfd);
8568 prevfd = pip[0];
8569
8570 if (pip[1] != -1)
8571 close(pip[1]);
8572 }
8573 if (n->npipe.pipe_backgnd == 0) {
8574 exitstatus = waitforjob(jp);
8575 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
8576 }
8577 INT_ON;
8578}
8579
8580
8581
8582
8583static void
8584setinteractive(int on)
8585{
8586 static smallint is_interactive;
8587
8588 if (++on == is_interactive)
8589 return;
8590 is_interactive = on;
8591 setsignal(SIGINT);
8592 setsignal(SIGQUIT);
8593 setsignal(SIGTERM);
8594#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8595 if (is_interactive > 1) {
8596
8597 static smallint did_banner;
8598
8599 if (!did_banner) {
8600
8601 out1fmt("\n\n%s %s\n"
8602 "Enter 'help' for a list of built-in commands."
8603 "\n\n",
8604 bb_banner,
8605 "built-in shell (ash)"
8606 );
8607 did_banner = 1;
8608 }
8609 }
8610#endif
8611}
8612
8613static void
8614optschanged(void)
8615{
8616#if DEBUG
8617 opentrace();
8618#endif
8619 setinteractive(iflag);
8620 setjobctl(mflag);
8621#if ENABLE_FEATURE_EDITING_VI
8622 if (viflag)
8623 line_input_state->flags |= VI_MODE;
8624 else
8625 line_input_state->flags &= ~VI_MODE;
8626#else
8627 viflag = 0;
8628#endif
8629}
8630
8631static struct localvar *localvars;
8632
8633
8634
8635
8636
8637static void
8638poplocalvars(void)
8639{
8640 struct localvar *lvp;
8641 struct var *vp;
8642
8643 while ((lvp = localvars) != NULL) {
8644 localvars = lvp->next;
8645 vp = lvp->vp;
8646 TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
8647 if (vp == NULL) {
8648 memcpy(optlist, lvp->text, sizeof(optlist));
8649 free((char*)lvp->text);
8650 optschanged();
8651 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8652 unsetvar(vp->var_text);
8653 } else {
8654 if (vp->var_func)
8655 vp->var_func(var_end(lvp->text));
8656 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8657 free((char*)vp->var_text);
8658 vp->flags = lvp->flags;
8659 vp->var_text = lvp->text;
8660 }
8661 free(lvp);
8662 }
8663}
8664
8665static int
8666evalfun(struct funcnode *func, int argc, char **argv, int flags)
8667{
8668 volatile struct shparam saveparam;
8669 struct localvar *volatile savelocalvars;
8670 struct jmploc *volatile savehandler;
8671 struct jmploc jmploc;
8672 int e;
8673
8674 saveparam = shellparam;
8675 savelocalvars = localvars;
8676 e = setjmp(jmploc.loc);
8677 if (e) {
8678 goto funcdone;
8679 }
8680 INT_OFF;
8681 savehandler = exception_handler;
8682 exception_handler = &jmploc;
8683 localvars = NULL;
8684 shellparam.malloced = 0;
8685 func->count++;
8686 funcnest++;
8687 INT_ON;
8688 shellparam.nparam = argc - 1;
8689 shellparam.p = argv + 1;
8690#if ENABLE_ASH_GETOPTS
8691 shellparam.optind = 1;
8692 shellparam.optoff = -1;
8693#endif
8694 evaltree(&func->n, flags & EV_TESTED);
8695 funcdone:
8696 INT_OFF;
8697 funcnest--;
8698 freefunc(func);
8699 poplocalvars();
8700 localvars = savelocalvars;
8701 freeparam(&shellparam);
8702 shellparam = saveparam;
8703 exception_handler = savehandler;
8704 INT_ON;
8705 evalskip &= ~SKIPFUNC;
8706 return e;
8707}
8708
8709#if ENABLE_ASH_CMDCMD
8710static char **
8711parse_command_args(char **argv, const char **path)
8712{
8713 char *cp, c;
8714
8715 for (;;) {
8716 cp = *++argv;
8717 if (!cp)
8718 return 0;
8719 if (*cp++ != '-')
8720 break;
8721 c = *cp++;
8722 if (!c)
8723 break;
8724 if (c == '-' && !*cp) {
8725 argv++;
8726 break;
8727 }
8728 do {
8729 switch (c) {
8730 case 'p':
8731 *path = bb_default_path;
8732 break;
8733 default:
8734
8735 return 0;
8736 }
8737 c = *cp++;
8738 } while (c);
8739 }
8740 return argv;
8741}
8742#endif
8743
8744
8745
8746
8747
8748
8749
8750static void
8751mklocal(char *name)
8752{
8753 struct localvar *lvp;
8754 struct var **vpp;
8755 struct var *vp;
8756
8757 INT_OFF;
8758 lvp = ckzalloc(sizeof(struct localvar));
8759 if (LONE_DASH(name)) {
8760 char *p;
8761 p = ckmalloc(sizeof(optlist));
8762 lvp->text = memcpy(p, optlist, sizeof(optlist));
8763 vp = NULL;
8764 } else {
8765 char *eq;
8766
8767 vpp = hashvar(name);
8768 vp = *findvar(vpp, name);
8769 eq = strchr(name, '=');
8770 if (vp == NULL) {
8771 if (eq)
8772 setvareq(name, VSTRFIXED);
8773 else
8774 setvar(name, NULL, VSTRFIXED);
8775 vp = *vpp;
8776 lvp->flags = VUNSET;
8777 } else {
8778 lvp->text = vp->var_text;
8779 lvp->flags = vp->flags;
8780 vp->flags |= VSTRFIXED|VTEXTFIXED;
8781 if (eq)
8782 setvareq(name, 0);
8783 }
8784 }
8785 lvp->vp = vp;
8786 lvp->next = localvars;
8787 localvars = lvp;
8788 INT_ON;
8789}
8790
8791
8792
8793
8794static int FAST_FUNC
8795localcmd(int argc UNUSED_PARAM, char **argv)
8796{
8797 char *name;
8798
8799 argv = argptr;
8800 while ((name = *argv++) != NULL) {
8801 mklocal(name);
8802 }
8803 return 0;
8804}
8805
8806static int FAST_FUNC
8807falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8808{
8809 return 1;
8810}
8811
8812static int FAST_FUNC
8813truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8814{
8815 return 0;
8816}
8817
8818static int FAST_FUNC
8819execcmd(int argc UNUSED_PARAM, char **argv)
8820{
8821 if (argv[1]) {
8822 iflag = 0;
8823 mflag = 0;
8824 optschanged();
8825 shellexec(argv + 1, pathval(), 0);
8826 }
8827 return 0;
8828}
8829
8830
8831
8832
8833static int FAST_FUNC
8834returncmd(int argc UNUSED_PARAM, char **argv)
8835{
8836
8837
8838
8839
8840 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8841 return argv[1] ? number(argv[1]) : exitstatus;
8842}
8843
8844
8845static int breakcmd(int, char **) FAST_FUNC;
8846static int dotcmd(int, char **) FAST_FUNC;
8847static int evalcmd(int, char **) FAST_FUNC;
8848static int exitcmd(int, char **) FAST_FUNC;
8849static int exportcmd(int, char **) FAST_FUNC;
8850#if ENABLE_ASH_GETOPTS
8851static int getoptscmd(int, char **) FAST_FUNC;
8852#endif
8853#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8854static int helpcmd(int, char **) FAST_FUNC;
8855#endif
8856#if ENABLE_SH_MATH_SUPPORT
8857static int letcmd(int, char **) FAST_FUNC;
8858#endif
8859static int readcmd(int, char **) FAST_FUNC;
8860static int setcmd(int, char **) FAST_FUNC;
8861static int shiftcmd(int, char **) FAST_FUNC;
8862static int timescmd(int, char **) FAST_FUNC;
8863static int trapcmd(int, char **) FAST_FUNC;
8864static int umaskcmd(int, char **) FAST_FUNC;
8865static int unsetcmd(int, char **) FAST_FUNC;
8866static int ulimitcmd(int, char **) FAST_FUNC;
8867
8868#define BUILTIN_NOSPEC "0"
8869#define BUILTIN_SPECIAL "1"
8870#define BUILTIN_REGULAR "2"
8871#define BUILTIN_SPEC_REG "3"
8872#define BUILTIN_ASSIGN "4"
8873#define BUILTIN_SPEC_ASSG "5"
8874#define BUILTIN_REG_ASSG "6"
8875#define BUILTIN_SPEC_REG_ASSG "7"
8876
8877
8878#if ENABLE_ASH_BUILTIN_ECHO
8879static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
8880#endif
8881#if ENABLE_ASH_BUILTIN_PRINTF
8882static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
8883#endif
8884#if ENABLE_ASH_BUILTIN_TEST
8885static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
8886#endif
8887
8888
8889static const struct builtincmd builtintab[] = {
8890 { BUILTIN_SPEC_REG "." , dotcmd },
8891 { BUILTIN_SPEC_REG ":" , truecmd },
8892#if ENABLE_ASH_BUILTIN_TEST
8893 { BUILTIN_REGULAR "[" , testcmd },
8894#if ENABLE_ASH_BASH_COMPAT
8895 { BUILTIN_REGULAR "[[" , testcmd },
8896#endif
8897#endif
8898#if ENABLE_ASH_ALIAS
8899 { BUILTIN_REG_ASSG "alias" , aliascmd },
8900#endif
8901#if JOBS
8902 { BUILTIN_REGULAR "bg" , fg_bgcmd },
8903#endif
8904 { BUILTIN_SPEC_REG "break" , breakcmd },
8905 { BUILTIN_REGULAR "cd" , cdcmd },
8906 { BUILTIN_NOSPEC "chdir" , cdcmd },
8907#if ENABLE_ASH_CMDCMD
8908 { BUILTIN_REGULAR "command" , commandcmd },
8909#endif
8910 { BUILTIN_SPEC_REG "continue", breakcmd },
8911#if ENABLE_ASH_BUILTIN_ECHO
8912 { BUILTIN_REGULAR "echo" , echocmd },
8913#endif
8914 { BUILTIN_SPEC_REG "eval" , evalcmd },
8915 { BUILTIN_SPEC_REG "exec" , execcmd },
8916 { BUILTIN_SPEC_REG "exit" , exitcmd },
8917 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
8918 { BUILTIN_REGULAR "false" , falsecmd },
8919#if JOBS
8920 { BUILTIN_REGULAR "fg" , fg_bgcmd },
8921#endif
8922#if ENABLE_ASH_GETOPTS
8923 { BUILTIN_REGULAR "getopts" , getoptscmd },
8924#endif
8925 { BUILTIN_NOSPEC "hash" , hashcmd },
8926#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8927 { BUILTIN_NOSPEC "help" , helpcmd },
8928#endif
8929#if JOBS
8930 { BUILTIN_REGULAR "jobs" , jobscmd },
8931 { BUILTIN_REGULAR "kill" , killcmd },
8932#endif
8933#if ENABLE_SH_MATH_SUPPORT
8934 { BUILTIN_NOSPEC "let" , letcmd },
8935#endif
8936 { BUILTIN_ASSIGN "local" , localcmd },
8937#if ENABLE_ASH_BUILTIN_PRINTF
8938 { BUILTIN_REGULAR "printf" , printfcmd },
8939#endif
8940 { BUILTIN_NOSPEC "pwd" , pwdcmd },
8941 { BUILTIN_REGULAR "read" , readcmd },
8942 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8943 { BUILTIN_SPEC_REG "return" , returncmd },
8944 { BUILTIN_SPEC_REG "set" , setcmd },
8945 { BUILTIN_SPEC_REG "shift" , shiftcmd },
8946#if ENABLE_ASH_BASH_COMPAT
8947 { BUILTIN_SPEC_REG "source" , dotcmd },
8948#endif
8949#if ENABLE_ASH_BUILTIN_TEST
8950 { BUILTIN_REGULAR "test" , testcmd },
8951#endif
8952 { BUILTIN_SPEC_REG "times" , timescmd },
8953 { BUILTIN_SPEC_REG "trap" , trapcmd },
8954 { BUILTIN_REGULAR "true" , truecmd },
8955 { BUILTIN_NOSPEC "type" , typecmd },
8956 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
8957 { BUILTIN_REGULAR "umask" , umaskcmd },
8958#if ENABLE_ASH_ALIAS
8959 { BUILTIN_REGULAR "unalias" , unaliascmd },
8960#endif
8961 { BUILTIN_SPEC_REG "unset" , unsetcmd },
8962 { BUILTIN_REGULAR "wait" , waitcmd },
8963};
8964
8965
8966#define COMMANDCMD (builtintab + \
8967 2 + \
8968 1 * ENABLE_ASH_BUILTIN_TEST + \
8969 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8970 1 * ENABLE_ASH_ALIAS + \
8971 1 * ENABLE_ASH_JOB_CONTROL + \
8972 3)
8973#define EXECCMD (builtintab + \
8974 2 + \
8975 1 * ENABLE_ASH_BUILTIN_TEST + \
8976 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8977 1 * ENABLE_ASH_ALIAS + \
8978 1 * ENABLE_ASH_JOB_CONTROL + \
8979 3 + \
8980 1 * ENABLE_ASH_CMDCMD + \
8981 1 + \
8982 ENABLE_ASH_BUILTIN_ECHO + \
8983 1)
8984
8985
8986
8987
8988static struct builtincmd *
8989find_builtin(const char *name)
8990{
8991 struct builtincmd *bp;
8992
8993 bp = bsearch(
8994 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
8995 pstrcmp
8996 );
8997 return bp;
8998}
8999
9000
9001
9002
9003static int
9004isassignment(const char *p)
9005{
9006 const char *q = endofname(p);
9007 if (p == q)
9008 return 0;
9009 return *q == '=';
9010}
9011static int FAST_FUNC
9012bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9013{
9014
9015
9016 return back_exitstatus;
9017}
9018static void
9019evalcommand(union node *cmd, int flags)
9020{
9021 static const struct builtincmd null_bltin = {
9022 "\0\0", bltincmd
9023 };
9024 struct stackmark smark;
9025 union node *argp;
9026 struct arglist arglist;
9027 struct arglist varlist;
9028 char **argv;
9029 int argc;
9030 const struct strlist *sp;
9031 struct cmdentry cmdentry;
9032 struct job *jp;
9033 char *lastarg;
9034 const char *path;
9035 int spclbltin;
9036 int status;
9037 char **nargv;
9038 struct builtincmd *bcmd;
9039 smallint cmd_is_exec;
9040 smallint pseudovarflag = 0;
9041
9042
9043 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9044 setstackmark(&smark);
9045 back_exitstatus = 0;
9046
9047 cmdentry.cmdtype = CMDBUILTIN;
9048 cmdentry.u.cmd = &null_bltin;
9049 varlist.lastp = &varlist.list;
9050 *varlist.lastp = NULL;
9051 arglist.lastp = &arglist.list;
9052 *arglist.lastp = NULL;
9053
9054 argc = 0;
9055 if (cmd->ncmd.args) {
9056 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9057 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9058 }
9059
9060 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9061 struct strlist **spp;
9062
9063 spp = arglist.lastp;
9064 if (pseudovarflag && isassignment(argp->narg.text))
9065 expandarg(argp, &arglist, EXP_VARTILDE);
9066 else
9067 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9068
9069 for (sp = *spp; sp; sp = sp->next)
9070 argc++;
9071 }
9072
9073 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
9074 for (sp = arglist.list; sp; sp = sp->next) {
9075 TRACE(("evalcommand arg: %s\n", sp->text));
9076 *nargv++ = sp->text;
9077 }
9078 *nargv = NULL;
9079
9080 lastarg = NULL;
9081 if (iflag && funcnest == 0 && argc > 0)
9082 lastarg = nargv[-1];
9083
9084 preverrout_fd = 2;
9085 expredir(cmd->ncmd.redirect);
9086 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
9087
9088 path = vpath.var_text;
9089 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9090 struct strlist **spp;
9091 char *p;
9092
9093 spp = varlist.lastp;
9094 expandarg(argp, &varlist, EXP_VARTILDE);
9095
9096
9097
9098
9099
9100 p = (*spp)->text;
9101 if (varcmp(p, path) == 0)
9102 path = p;
9103 }
9104
9105
9106 if (xflag) {
9107 int n;
9108 const char *p = " %s" + 1;
9109
9110 fdprintf(preverrout_fd, p, expandstr(ps4val()));
9111 sp = varlist.list;
9112 for (n = 0; n < 2; n++) {
9113 while (sp) {
9114 fdprintf(preverrout_fd, p, sp->text);
9115 sp = sp->next;
9116 p = " %s";
9117 }
9118 sp = arglist.list;
9119 }
9120 safe_write(preverrout_fd, "\n", 1);
9121 }
9122
9123 cmd_is_exec = 0;
9124 spclbltin = -1;
9125
9126
9127 if (argc) {
9128 const char *oldpath;
9129 int cmd_flag = DO_ERR;
9130
9131 path += 5;
9132 oldpath = path;
9133 for (;;) {
9134 find_command(argv[0], &cmdentry, cmd_flag, path);
9135 if (cmdentry.cmdtype == CMDUNKNOWN) {
9136 flush_stdout_stderr();
9137 status = 127;
9138 goto bail;
9139 }
9140
9141
9142 if (cmdentry.cmdtype != CMDBUILTIN)
9143 break;
9144 if (spclbltin < 0)
9145 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9146 if (cmdentry.u.cmd == EXECCMD)
9147 cmd_is_exec = 1;
9148#if ENABLE_ASH_CMDCMD
9149 if (cmdentry.u.cmd == COMMANDCMD) {
9150 path = oldpath;
9151 nargv = parse_command_args(argv, &path);
9152 if (!nargv)
9153 break;
9154 argc -= nargv - argv;
9155 argv = nargv;
9156 cmd_flag |= DO_NOFUNC;
9157 } else
9158#endif
9159 break;
9160 }
9161 }
9162
9163 if (status) {
9164
9165 if (spclbltin > 0)
9166 raise_exception(EXERROR);
9167 bail:
9168 exitstatus = status;
9169 goto out;
9170 }
9171
9172
9173 switch (cmdentry.cmdtype) {
9174 default: {
9175
9176#if ENABLE_FEATURE_SH_NOFORK
9177
9178
9179
9180
9181
9182
9183 int applet_no = (- cmdentry.u.index - 2);
9184 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
9185 listsetvar(varlist.list, VEXPORT|VSTACK);
9186
9187 exitstatus = run_nofork_applet(applet_no, argv);
9188 break;
9189 }
9190#endif
9191
9192
9193
9194
9195 if (!(flags & EV_EXIT) || may_have_traps) {
9196
9197 INT_OFF;
9198 jp = makejob( 1);
9199 if (forkshell(jp, cmd, FORK_FG) != 0) {
9200
9201 exitstatus = waitforjob(jp);
9202 INT_ON;
9203 TRACE(("forked child exited with %d\n", exitstatus));
9204 break;
9205 }
9206
9207 FORCE_INT_ON;
9208
9209 }
9210 listsetvar(varlist.list, VEXPORT|VSTACK);
9211 shellexec(argv, path, cmdentry.u.index);
9212
9213 }
9214 case CMDBUILTIN:
9215 cmdenviron = varlist.list;
9216 if (cmdenviron) {
9217 struct strlist *list = cmdenviron;
9218 int i = VNOSET;
9219 if (spclbltin > 0 || argc == 0) {
9220 i = 0;
9221 if (cmd_is_exec && argc > 1)
9222 i = VEXPORT;
9223 }
9224 listsetvar(list, i);
9225 }
9226
9227
9228
9229
9230 dowait(DOWAIT_NONBLOCK, NULL);
9231
9232 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9233 int exit_status;
9234 int i = exception_type;
9235 if (i == EXEXIT)
9236 goto raise;
9237 exit_status = 2;
9238 if (i == EXINT)
9239 exit_status = 128 + SIGINT;
9240 if (i == EXSIG)
9241 exit_status = 128 + pending_sig;
9242 exitstatus = exit_status;
9243 if (i == EXINT || spclbltin > 0) {
9244 raise:
9245 longjmp(exception_handler->loc, 1);
9246 }
9247 FORCE_INT_ON;
9248 }
9249 break;
9250
9251 case CMDFUNCTION:
9252 listsetvar(varlist.list, 0);
9253
9254 dowait(DOWAIT_NONBLOCK, NULL);
9255 if (evalfun(cmdentry.u.func, argc, argv, flags))
9256 goto raise;
9257 break;
9258
9259 }
9260
9261 out:
9262 popredir( cmd_is_exec, cmd_is_exec);
9263 if (lastarg) {
9264
9265
9266
9267
9268 setvar("_", lastarg, 0);
9269 }
9270 popstackmark(&smark);
9271}
9272
9273static int
9274evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9275{
9276 char *volatile savecmdname;
9277 struct jmploc *volatile savehandler;
9278 struct jmploc jmploc;
9279 int i;
9280
9281 savecmdname = commandname;
9282 i = setjmp(jmploc.loc);
9283 if (i)
9284 goto cmddone;
9285 savehandler = exception_handler;
9286 exception_handler = &jmploc;
9287 commandname = argv[0];
9288 argptr = argv + 1;
9289 optptr = NULL;
9290 exitstatus = (*cmd->builtin)(argc, argv);
9291 flush_stdout_stderr();
9292 cmddone:
9293 exitstatus |= ferror(stdout);
9294 clearerr(stdout);
9295 commandname = savecmdname;
9296 exception_handler = savehandler;
9297
9298 return i;
9299}
9300
9301static int
9302goodname(const char *p)
9303{
9304 return !*endofname(p);
9305}
9306
9307
9308
9309
9310
9311
9312
9313
9314static void
9315prehash(union node *n)
9316{
9317 struct cmdentry entry;
9318
9319 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9320 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
9321}
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340static int FAST_FUNC
9341breakcmd(int argc UNUSED_PARAM, char **argv)
9342{
9343 int n = argv[1] ? number(argv[1]) : 1;
9344
9345 if (n <= 0)
9346 ash_msg_and_raise_error(msg_illnum, argv[1]);
9347 if (n > loopnest)
9348 n = loopnest;
9349 if (n > 0) {
9350 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
9351 skipcount = n;
9352 }
9353 return 0;
9354}
9355
9356
9357
9358
9359
9360
9361
9362enum {
9363 INPUT_PUSH_FILE = 1,
9364 INPUT_NOFILE_OK = 2,
9365};
9366
9367static smallint checkkwd;
9368
9369#define CHKALIAS 0x1
9370#define CHKKWD 0x2
9371#define CHKNL 0x4
9372
9373
9374
9375
9376
9377#if !ENABLE_ASH_ALIAS
9378#define pushstring(s, ap) pushstring(s)
9379#endif
9380static void
9381pushstring(char *s, struct alias *ap)
9382{
9383 struct strpush *sp;
9384 int len;
9385
9386 len = strlen(s);
9387 INT_OFF;
9388 if (g_parsefile->strpush) {
9389 sp = ckzalloc(sizeof(*sp));
9390 sp->prev = g_parsefile->strpush;
9391 } else {
9392 sp = &(g_parsefile->basestrpush);
9393 }
9394 g_parsefile->strpush = sp;
9395 sp->prev_string = g_parsefile->next_to_pgetc;
9396 sp->prev_left_in_line = g_parsefile->left_in_line;
9397#if ENABLE_ASH_ALIAS
9398 sp->ap = ap;
9399 if (ap) {
9400 ap->flag |= ALIASINUSE;
9401 sp->string = s;
9402 }
9403#endif
9404 g_parsefile->next_to_pgetc = s;
9405 g_parsefile->left_in_line = len;
9406 INT_ON;
9407}
9408
9409static void
9410popstring(void)
9411{
9412 struct strpush *sp = g_parsefile->strpush;
9413
9414 INT_OFF;
9415#if ENABLE_ASH_ALIAS
9416 if (sp->ap) {
9417 if (g_parsefile->next_to_pgetc[-1] == ' '
9418 || g_parsefile->next_to_pgetc[-1] == '\t'
9419 ) {
9420 checkkwd |= CHKALIAS;
9421 }
9422 if (sp->string != sp->ap->val) {
9423 free(sp->string);
9424 }
9425 sp->ap->flag &= ~ALIASINUSE;
9426 if (sp->ap->flag & ALIASDEAD) {
9427 unalias(sp->ap->name);
9428 }
9429 }
9430#endif
9431 g_parsefile->next_to_pgetc = sp->prev_string;
9432 g_parsefile->left_in_line = sp->prev_left_in_line;
9433 g_parsefile->strpush = sp->prev;
9434 if (sp != &(g_parsefile->basestrpush))
9435 free(sp);
9436 INT_ON;
9437}
9438
9439
9440
9441
9442
9443
9444static int
9445preadfd(void)
9446{
9447 int nr;
9448 char *buf = g_parsefile->buf;
9449
9450 g_parsefile->next_to_pgetc = buf;
9451#if ENABLE_FEATURE_EDITING
9452 retry:
9453 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9454 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
9455 else {
9456#if ENABLE_FEATURE_TAB_COMPLETION
9457 line_input_state->path_lookup = pathval();
9458#endif
9459 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
9460 if (nr == 0) {
9461
9462 if (trap[SIGINT]) {
9463 buf[0] = '\n';
9464 buf[1] = '\0';
9465 raise(SIGINT);
9466 return 1;
9467 }
9468 goto retry;
9469 }
9470 if (nr < 0 && errno == 0) {
9471
9472 nr = 0;
9473 }
9474 }
9475#else
9476 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
9477#endif
9478
9479#if 0
9480
9481 if (nr < 0) {
9482 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
9483 int flags = fcntl(0, F_GETFL);
9484 if (flags >= 0 && (flags & O_NONBLOCK)) {
9485 flags &= ~O_NONBLOCK;
9486 if (fcntl(0, F_SETFL, flags) >= 0) {
9487 out2str("sh: turning off NDELAY mode\n");
9488 goto retry;
9489 }
9490 }
9491 }
9492 }
9493#endif
9494 return nr;
9495}
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508#define pgetc_debug(...) ((void)0)
9509static int
9510preadbuffer(void)
9511{
9512 char *q;
9513 int more;
9514
9515 while (g_parsefile->strpush) {
9516#if ENABLE_ASH_ALIAS
9517 if (g_parsefile->left_in_line == -1
9518 && g_parsefile->strpush->ap
9519 && g_parsefile->next_to_pgetc[-1] != ' '
9520 && g_parsefile->next_to_pgetc[-1] != '\t'
9521 ) {
9522 pgetc_debug("preadbuffer PEOA");
9523 return PEOA;
9524 }
9525#endif
9526 popstring();
9527
9528 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9529 g_parsefile->left_in_line,
9530 g_parsefile->next_to_pgetc,
9531 g_parsefile->next_to_pgetc);
9532 if (--g_parsefile->left_in_line >= 0)
9533 return (unsigned char)(*g_parsefile->next_to_pgetc++);
9534 }
9535
9536
9537
9538
9539
9540
9541
9542
9543 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
9544 pgetc_debug("preadbuffer PEOF1");
9545
9546
9547
9548
9549 g_parsefile->next_to_pgetc++;
9550 return PEOF;
9551 }
9552
9553 more = g_parsefile->left_in_buffer;
9554 if (more <= 0) {
9555 flush_stdout_stderr();
9556 again:
9557 more = preadfd();
9558 if (more <= 0) {
9559
9560 g_parsefile->left_in_line = -99;
9561 pgetc_debug("preadbuffer PEOF2");
9562 g_parsefile->next_to_pgetc++;
9563 return PEOF;
9564 }
9565 }
9566
9567
9568
9569
9570
9571
9572 q = g_parsefile->next_to_pgetc;
9573 for (;;) {
9574 char c;
9575
9576 more--;
9577
9578 c = *q;
9579 if (c == '\0') {
9580 memmove(q, q + 1, more);
9581 } else {
9582 q++;
9583 if (c == '\n') {
9584 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9585 break;
9586 }
9587 }
9588
9589 if (more <= 0) {
9590 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9591 if (g_parsefile->left_in_line < 0)
9592 goto again;
9593 break;
9594 }
9595 }
9596 g_parsefile->left_in_buffer = more;
9597
9598 if (vflag) {
9599 char save = *q;
9600 *q = '\0';
9601 out2str(g_parsefile->next_to_pgetc);
9602 *q = save;
9603 }
9604
9605 pgetc_debug("preadbuffer at %d:%p'%s'",
9606 g_parsefile->left_in_line,
9607 g_parsefile->next_to_pgetc,
9608 g_parsefile->next_to_pgetc);
9609 return (unsigned char)*g_parsefile->next_to_pgetc++;
9610}
9611
9612#define pgetc_as_macro() \
9613 (--g_parsefile->left_in_line >= 0 \
9614 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
9615 : preadbuffer() \
9616 )
9617
9618static int
9619pgetc(void)
9620{
9621 pgetc_debug("pgetc_fast at %d:%p'%s'",
9622 g_parsefile->left_in_line,
9623 g_parsefile->next_to_pgetc,
9624 g_parsefile->next_to_pgetc);
9625 return pgetc_as_macro();
9626}
9627
9628#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9629# define pgetc_fast() pgetc()
9630#else
9631# define pgetc_fast() pgetc_as_macro()
9632#endif
9633
9634#if ENABLE_ASH_ALIAS
9635static int
9636pgetc_without_PEOA(void)
9637{
9638 int c;
9639 do {
9640 pgetc_debug("pgetc_fast at %d:%p'%s'",
9641 g_parsefile->left_in_line,
9642 g_parsefile->next_to_pgetc,
9643 g_parsefile->next_to_pgetc);
9644 c = pgetc_fast();
9645 } while (c == PEOA);
9646 return c;
9647}
9648#else
9649# define pgetc_without_PEOA() pgetc()
9650#endif
9651
9652
9653
9654
9655static char *
9656pfgets(char *line, int len)
9657{
9658 char *p = line;
9659 int nleft = len;
9660 int c;
9661
9662 while (--nleft > 0) {
9663 c = pgetc_without_PEOA();
9664 if (c == PEOF) {
9665 if (p == line)
9666 return NULL;
9667 break;
9668 }
9669 *p++ = c;
9670 if (c == '\n')
9671 break;
9672 }
9673 *p = '\0';
9674 return line;
9675}
9676
9677
9678
9679
9680
9681static void
9682pungetc(void)
9683{
9684 g_parsefile->left_in_line++;
9685 g_parsefile->next_to_pgetc--;
9686 pgetc_debug("pushed back to %d:%p'%s'",
9687 g_parsefile->left_in_line,
9688 g_parsefile->next_to_pgetc,
9689 g_parsefile->next_to_pgetc);
9690}
9691
9692
9693
9694
9695
9696static void
9697pushfile(void)
9698{
9699 struct parsefile *pf;
9700
9701 pf = ckzalloc(sizeof(*pf));
9702 pf->prev = g_parsefile;
9703 pf->pf_fd = -1;
9704
9705
9706 g_parsefile = pf;
9707}
9708
9709static void
9710popfile(void)
9711{
9712 struct parsefile *pf = g_parsefile;
9713
9714 INT_OFF;
9715 if (pf->pf_fd >= 0)
9716 close(pf->pf_fd);
9717 free(pf->buf);
9718 while (pf->strpush)
9719 popstring();
9720 g_parsefile = pf->prev;
9721 free(pf);
9722 INT_ON;
9723}
9724
9725
9726
9727
9728static void
9729popallfiles(void)
9730{
9731 while (g_parsefile != &basepf)
9732 popfile();
9733}
9734
9735
9736
9737
9738
9739static void
9740closescript(void)
9741{
9742 popallfiles();
9743 if (g_parsefile->pf_fd > 0) {
9744 close(g_parsefile->pf_fd);
9745 g_parsefile->pf_fd = 0;
9746 }
9747}
9748
9749
9750
9751
9752
9753static void
9754setinputfd(int fd, int push)
9755{
9756 close_on_exec_on(fd);
9757 if (push) {
9758 pushfile();
9759 g_parsefile->buf = NULL;
9760 }
9761 g_parsefile->pf_fd = fd;
9762 if (g_parsefile->buf == NULL)
9763 g_parsefile->buf = ckmalloc(IBUFSIZ);
9764 g_parsefile->left_in_buffer = 0;
9765 g_parsefile->left_in_line = 0;
9766 g_parsefile->linno = 1;
9767}
9768
9769
9770
9771
9772
9773static int
9774setinputfile(const char *fname, int flags)
9775{
9776 int fd;
9777 int fd2;
9778
9779 INT_OFF;
9780 fd = open(fname, O_RDONLY);
9781 if (fd < 0) {
9782 if (flags & INPUT_NOFILE_OK)
9783 goto out;
9784 ash_msg_and_raise_error("can't open '%s'", fname);
9785 }
9786 if (fd < 10) {
9787 fd2 = copyfd(fd, 10);
9788 close(fd);
9789 if (fd2 < 0)
9790 ash_msg_and_raise_error("out of file descriptors");
9791 fd = fd2;
9792 }
9793 setinputfd(fd, flags & INPUT_PUSH_FILE);
9794 out:
9795 INT_ON;
9796 return fd;
9797}
9798
9799
9800
9801
9802static void
9803setinputstring(char *string)
9804{
9805 INT_OFF;
9806 pushfile();
9807 g_parsefile->next_to_pgetc = string;
9808 g_parsefile->left_in_line = strlen(string);
9809 g_parsefile->buf = NULL;
9810 g_parsefile->linno = 1;
9811 INT_ON;
9812}
9813
9814
9815
9816
9817
9818
9819
9820#if ENABLE_ASH_MAIL
9821
9822#define MAXMBOXES 10
9823
9824
9825static time_t mailtime[MAXMBOXES];
9826
9827static smallint mail_var_path_changed;
9828
9829
9830
9831
9832
9833
9834
9835static void
9836chkmail(void)
9837{
9838 const char *mpath;
9839 char *p;
9840 char *q;
9841 time_t *mtp;
9842 struct stackmark smark;
9843 struct stat statb;
9844
9845 setstackmark(&smark);
9846 mpath = mpathset() ? mpathval() : mailval();
9847 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
9848 p = path_advance(&mpath, nullstr);
9849 if (p == NULL)
9850 break;
9851 if (*p == '\0')
9852 continue;
9853 for (q = p; *q; q++)
9854 continue;
9855#if DEBUG
9856 if (q[-1] != '/')
9857 abort();
9858#endif
9859 q[-1] = '\0';
9860 if (stat(p, &statb) < 0) {
9861 *mtp = 0;
9862 continue;
9863 }
9864 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9865 fprintf(
9866 stderr, "%s\n",
9867 pathopt ? pathopt : "you have mail"
9868 );
9869 }
9870 *mtp = statb.st_mtime;
9871 }
9872 mail_var_path_changed = 0;
9873 popstackmark(&smark);
9874}
9875
9876static void FAST_FUNC
9877changemail(const char *val UNUSED_PARAM)
9878{
9879 mail_var_path_changed = 1;
9880}
9881
9882#endif
9883
9884
9885
9886
9887
9888
9889
9890static void
9891setparam(char **argv)
9892{
9893 char **newparam;
9894 char **ap;
9895 int nparam;
9896
9897 for (nparam = 0; argv[nparam]; nparam++)
9898 continue;
9899 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9900 while (*argv) {
9901 *ap++ = ckstrdup(*argv++);
9902 }
9903 *ap = NULL;
9904 freeparam(&shellparam);
9905 shellparam.malloced = 1;
9906 shellparam.nparam = nparam;
9907 shellparam.p = newparam;
9908#if ENABLE_ASH_GETOPTS
9909 shellparam.optind = 1;
9910 shellparam.optoff = -1;
9911#endif
9912}
9913
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935static int
9936plus_minus_o(char *name, int val)
9937{
9938 int i;
9939
9940 if (name) {
9941 for (i = 0; i < NOPTS; i++) {
9942 if (strcmp(name, optnames(i)) == 0) {
9943 optlist[i] = val;
9944 return 0;
9945 }
9946 }
9947 ash_msg("illegal option %co %s", val ? '-' : '+', name);
9948 return 1;
9949 }
9950 for (i = 0; i < NOPTS; i++) {
9951 if (val) {
9952 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9953 } else {
9954 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9955 }
9956 }
9957 return 0;
9958}
9959static void
9960setoption(int flag, int val)
9961{
9962 int i;
9963
9964 for (i = 0; i < NOPTS; i++) {
9965 if (optletters(i) == flag) {
9966 optlist[i] = val;
9967 return;
9968 }
9969 }
9970 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
9971
9972}
9973static int
9974options(int cmdline)
9975{
9976 char *p;
9977 int val;
9978 int c;
9979
9980 if (cmdline)
9981 minusc = NULL;
9982 while ((p = *argptr) != NULL) {
9983 c = *p++;
9984 if (c != '-' && c != '+')
9985 break;
9986 argptr++;
9987 val = 0;
9988 if (c == '-') {
9989 val = 1;
9990 if (p[0] == '\0' || LONE_DASH(p)) {
9991 if (!cmdline) {
9992
9993 if (p[0] == '\0')
9994 xflag = vflag = 0;
9995
9996 else if (*argptr == NULL)
9997 setparam(argptr);
9998 }
9999 break;
10000 }
10001 }
10002
10003 while ((c = *p++) != '\0') {
10004
10005 if (c == 'c' && cmdline) {
10006 minusc = p;
10007 } else if (c == 'o') {
10008 if (plus_minus_o(*argptr, val)) {
10009
10010 return 1;
10011 }
10012 if (*argptr)
10013 argptr++;
10014 } else if (cmdline && (c == 'l')) {
10015 isloginsh = 1;
10016
10017 } else if (cmdline && val && (c == '-')) {
10018 if (strcmp(p, "login") == 0)
10019 isloginsh = 1;
10020 break;
10021 } else {
10022 setoption(c, val);
10023 }
10024 }
10025 }
10026 return 0;
10027}
10028
10029
10030
10031
10032static int FAST_FUNC
10033shiftcmd(int argc UNUSED_PARAM, char **argv)
10034{
10035 int n;
10036 char **ap1, **ap2;
10037
10038 n = 1;
10039 if (argv[1])
10040 n = number(argv[1]);
10041 if (n > shellparam.nparam)
10042 n = 0;
10043 INT_OFF;
10044 shellparam.nparam -= n;
10045 for (ap1 = shellparam.p; --n >= 0; ap1++) {
10046 if (shellparam.malloced)
10047 free(*ap1);
10048 }
10049 ap2 = shellparam.p;
10050 while ((*ap2++ = *ap1++) != NULL)
10051 continue;
10052#if ENABLE_ASH_GETOPTS
10053 shellparam.optind = 1;
10054 shellparam.optoff = -1;
10055#endif
10056 INT_ON;
10057 return 0;
10058}
10059
10060
10061
10062
10063
10064
10065
10066
10067static int
10068showvars(const char *sep_prefix, int on, int off)
10069{
10070 const char *sep;
10071 char **ep, **epend;
10072
10073 ep = listvars(on, off, &epend);
10074 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10075
10076 sep = *sep_prefix ? " " : sep_prefix;
10077
10078 for (; ep < epend; ep++) {
10079 const char *p;
10080 const char *q;
10081
10082 p = strchrnul(*ep, '=');
10083 q = nullstr;
10084 if (*p)
10085 q = single_quote(++p);
10086 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10087 }
10088 return 0;
10089}
10090
10091
10092
10093
10094static int FAST_FUNC
10095setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10096{
10097 int retval;
10098
10099 if (!argv[1])
10100 return showvars(nullstr, 0, VUNSET);
10101 INT_OFF;
10102 retval = 1;
10103 if (!options(0)) {
10104 retval = 0;
10105 optschanged();
10106 if (*argptr != NULL) {
10107 setparam(argptr);
10108 }
10109 }
10110 INT_ON;
10111 return retval;
10112}
10113
10114#if ENABLE_ASH_RANDOM_SUPPORT
10115static void FAST_FUNC
10116change_random(const char *value)
10117{
10118 uint32_t t;
10119
10120 if (value == NULL) {
10121
10122 t = next_random(&random_gen);
10123
10124 setvar(vrandom.var_text, utoa(t), VNOFUNC);
10125 vrandom.flags &= ~VNOFUNC;
10126 } else {
10127
10128 t = strtoul(value, NULL, 10);
10129 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
10130 }
10131}
10132#endif
10133
10134#if ENABLE_ASH_GETOPTS
10135static int
10136getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
10137{
10138 char *p, *q;
10139 char c = '?';
10140 int done = 0;
10141 int err = 0;
10142 char s[12];
10143 char **optnext;
10144
10145 if (*param_optind < 1)
10146 return 1;
10147 optnext = optfirst + *param_optind - 1;
10148
10149 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
10150 p = NULL;
10151 else
10152 p = optnext[-1] + *optoff;
10153 if (p == NULL || *p == '\0') {
10154
10155 p = *optnext;
10156 if (p == NULL || *p != '-' || *++p == '\0') {
10157 atend:
10158 p = NULL;
10159 done = 1;
10160 goto out;
10161 }
10162 optnext++;
10163 if (LONE_DASH(p))
10164 goto atend;
10165 }
10166
10167 c = *p++;
10168 for (q = optstr; *q != c;) {
10169 if (*q == '\0') {
10170 if (optstr[0] == ':') {
10171 s[0] = c;
10172 s[1] = '\0';
10173 err |= setvarsafe("OPTARG", s, 0);
10174 } else {
10175 fprintf(stderr, "Illegal option -%c\n", c);
10176 unsetvar("OPTARG");
10177 }
10178 c = '?';
10179 goto out;
10180 }
10181 if (*++q == ':')
10182 q++;
10183 }
10184
10185 if (*++q == ':') {
10186 if (*p == '\0' && (p = *optnext) == NULL) {
10187 if (optstr[0] == ':') {
10188 s[0] = c;
10189 s[1] = '\0';
10190 err |= setvarsafe("OPTARG", s, 0);
10191 c = ':';
10192 } else {
10193 fprintf(stderr, "No arg for -%c option\n", c);
10194 unsetvar("OPTARG");
10195 c = '?';
10196 }
10197 goto out;
10198 }
10199
10200 if (p == *optnext)
10201 optnext++;
10202 err |= setvarsafe("OPTARG", p, 0);
10203 p = NULL;
10204 } else
10205 err |= setvarsafe("OPTARG", nullstr, 0);
10206 out:
10207 *optoff = p ? p - *(optnext - 1) : -1;
10208 *param_optind = optnext - optfirst + 1;
10209 fmtstr(s, sizeof(s), "%d", *param_optind);
10210 err |= setvarsafe("OPTIND", s, VNOFUNC);
10211 s[0] = c;
10212 s[1] = '\0';
10213 err |= setvarsafe(optvar, s, 0);
10214 if (err) {
10215 *param_optind = 1;
10216 *optoff = -1;
10217 flush_stdout_stderr();
10218 raise_exception(EXERROR);
10219 }
10220 return done;
10221}
10222
10223
10224
10225
10226
10227
10228
10229static int FAST_FUNC
10230getoptscmd(int argc, char **argv)
10231{
10232 char **optbase;
10233
10234 if (argc < 3)
10235 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
10236 if (argc == 3) {
10237 optbase = shellparam.p;
10238 if (shellparam.optind > shellparam.nparam + 1) {
10239 shellparam.optind = 1;
10240 shellparam.optoff = -1;
10241 }
10242 } else {
10243 optbase = &argv[3];
10244 if (shellparam.optind > argc - 2) {
10245 shellparam.optind = 1;
10246 shellparam.optoff = -1;
10247 }
10248 }
10249
10250 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
10251 &shellparam.optoff);
10252}
10253#endif
10254
10255
10256
10257
10258struct heredoc {
10259 struct heredoc *next;
10260 union node *here;
10261 char *eofmark;
10262 smallint striptabs;
10263};
10264
10265static smallint tokpushback;
10266static smallint parsebackquote;
10267static smallint quoteflag;
10268static token_id_t lasttoken;
10269static struct heredoc *heredoclist;
10270static char *wordtext;
10271static struct nodelist *backquotelist;
10272static union node *redirnode;
10273static struct heredoc *heredoc;
10274
10275static const char *
10276tokname(char *buf, int tok)
10277{
10278 if (tok < TSEMI)
10279 return tokname_array[tok] + 1;
10280 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10281 return buf;
10282}
10283
10284
10285
10286
10287
10288
10289static void raise_error_unexpected_syntax(int) NORETURN;
10290static void
10291raise_error_unexpected_syntax(int token)
10292{
10293 char msg[64];
10294 char buf[16];
10295 int l;
10296
10297 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
10298 if (token >= 0)
10299 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
10300 raise_error_syntax(msg);
10301
10302}
10303
10304#define EOFMARKLEN 79
10305
10306
10307static union node *andor(void);
10308static union node *pipeline(void);
10309static union node *parse_command(void);
10310static void parseheredoc(void);
10311static char peektoken(void);
10312static int readtoken(void);
10313
10314static union node *
10315list(int nlflag)
10316{
10317 union node *n1, *n2, *n3;
10318 int tok;
10319
10320 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10321 if (nlflag == 2 && peektoken())
10322 return NULL;
10323 n1 = NULL;
10324 for (;;) {
10325 n2 = andor();
10326 tok = readtoken();
10327 if (tok == TBACKGND) {
10328 if (n2->type == NPIPE) {
10329 n2->npipe.pipe_backgnd = 1;
10330 } else {
10331 if (n2->type != NREDIR) {
10332 n3 = stzalloc(sizeof(struct nredir));
10333 n3->nredir.n = n2;
10334
10335 n2 = n3;
10336 }
10337 n2->type = NBACKGND;
10338 }
10339 }
10340 if (n1 == NULL) {
10341 n1 = n2;
10342 } else {
10343 n3 = stzalloc(sizeof(struct nbinary));
10344 n3->type = NSEMI;
10345 n3->nbinary.ch1 = n1;
10346 n3->nbinary.ch2 = n2;
10347 n1 = n3;
10348 }
10349 switch (tok) {
10350 case TBACKGND:
10351 case TSEMI:
10352 tok = readtoken();
10353
10354 case TNL:
10355 if (tok == TNL) {
10356 parseheredoc();
10357 if (nlflag == 1)
10358 return n1;
10359 } else {
10360 tokpushback = 1;
10361 }
10362 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10363 if (peektoken())
10364 return n1;
10365 break;
10366 case TEOF:
10367 if (heredoclist)
10368 parseheredoc();
10369 else
10370 pungetc();
10371 return n1;
10372 default:
10373 if (nlflag == 1)
10374 raise_error_unexpected_syntax(-1);
10375 tokpushback = 1;
10376 return n1;
10377 }
10378 }
10379}
10380
10381static union node *
10382andor(void)
10383{
10384 union node *n1, *n2, *n3;
10385 int t;
10386
10387 n1 = pipeline();
10388 for (;;) {
10389 t = readtoken();
10390 if (t == TAND) {
10391 t = NAND;
10392 } else if (t == TOR) {
10393 t = NOR;
10394 } else {
10395 tokpushback = 1;
10396 return n1;
10397 }
10398 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10399 n2 = pipeline();
10400 n3 = stzalloc(sizeof(struct nbinary));
10401 n3->type = t;
10402 n3->nbinary.ch1 = n1;
10403 n3->nbinary.ch2 = n2;
10404 n1 = n3;
10405 }
10406}
10407
10408static union node *
10409pipeline(void)
10410{
10411 union node *n1, *n2, *pipenode;
10412 struct nodelist *lp, *prev;
10413 int negate;
10414
10415 negate = 0;
10416 TRACE(("pipeline: entered\n"));
10417 if (readtoken() == TNOT) {
10418 negate = !negate;
10419 checkkwd = CHKKWD | CHKALIAS;
10420 } else
10421 tokpushback = 1;
10422 n1 = parse_command();
10423 if (readtoken() == TPIPE) {
10424 pipenode = stzalloc(sizeof(struct npipe));
10425 pipenode->type = NPIPE;
10426
10427 lp = stzalloc(sizeof(struct nodelist));
10428 pipenode->npipe.cmdlist = lp;
10429 lp->n = n1;
10430 do {
10431 prev = lp;
10432 lp = stzalloc(sizeof(struct nodelist));
10433 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10434 lp->n = parse_command();
10435 prev->next = lp;
10436 } while (readtoken() == TPIPE);
10437 lp->next = NULL;
10438 n1 = pipenode;
10439 }
10440 tokpushback = 1;
10441 if (negate) {
10442 n2 = stzalloc(sizeof(struct nnot));
10443 n2->type = NNOT;
10444 n2->nnot.com = n1;
10445 return n2;
10446 }
10447 return n1;
10448}
10449
10450static union node *
10451makename(void)
10452{
10453 union node *n;
10454
10455 n = stzalloc(sizeof(struct narg));
10456 n->type = NARG;
10457
10458 n->narg.text = wordtext;
10459 n->narg.backquote = backquotelist;
10460 return n;
10461}
10462
10463static void
10464fixredir(union node *n, const char *text, int err)
10465{
10466 int fd;
10467
10468 TRACE(("Fix redir %s %d\n", text, err));
10469 if (!err)
10470 n->ndup.vname = NULL;
10471
10472 fd = bb_strtou(text, NULL, 10);
10473 if (!errno && fd >= 0)
10474 n->ndup.dupfd = fd;
10475 else if (LONE_DASH(text))
10476 n->ndup.dupfd = -1;
10477 else {
10478 if (err)
10479 raise_error_syntax("bad fd number");
10480 n->ndup.vname = makename();
10481 }
10482}
10483
10484
10485
10486
10487
10488static int
10489noexpand(const char *text)
10490{
10491 unsigned char c;
10492
10493 while ((c = *text++) != '\0') {
10494 if (c == CTLQUOTEMARK)
10495 continue;
10496 if (c == CTLESC)
10497 text++;
10498 else if (SIT(c, BASESYNTAX) == CCTL)
10499 return 0;
10500 }
10501 return 1;
10502}
10503
10504static void
10505parsefname(void)
10506{
10507 union node *n = redirnode;
10508
10509 if (readtoken() != TWORD)
10510 raise_error_unexpected_syntax(-1);
10511 if (n->type == NHERE) {
10512 struct heredoc *here = heredoc;
10513 struct heredoc *p;
10514 int i;
10515
10516 if (quoteflag == 0)
10517 n->type = NXHERE;
10518 TRACE(("Here document %d\n", n->type));
10519 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10520 raise_error_syntax("illegal eof marker for << redirection");
10521 rmescapes(wordtext, 0);
10522 here->eofmark = wordtext;
10523 here->next = NULL;
10524 if (heredoclist == NULL)
10525 heredoclist = here;
10526 else {
10527 for (p = heredoclist; p->next; p = p->next)
10528 continue;
10529 p->next = here;
10530 }
10531 } else if (n->type == NTOFD || n->type == NFROMFD) {
10532 fixredir(n, wordtext, 0);
10533 } else {
10534 n->nfile.fname = makename();
10535 }
10536}
10537
10538static union node *
10539simplecmd(void)
10540{
10541 union node *args, **app;
10542 union node *n = NULL;
10543 union node *vars, **vpp;
10544 union node **rpp, *redir;
10545 int savecheckkwd;
10546#if ENABLE_ASH_BASH_COMPAT
10547 smallint double_brackets_flag = 0;
10548#endif
10549
10550 args = NULL;
10551 app = &args;
10552 vars = NULL;
10553 vpp = &vars;
10554 redir = NULL;
10555 rpp = &redir;
10556
10557 savecheckkwd = CHKALIAS;
10558 for (;;) {
10559 int t;
10560 checkkwd = savecheckkwd;
10561 t = readtoken();
10562 switch (t) {
10563#if ENABLE_ASH_BASH_COMPAT
10564 case TAND:
10565 case TOR:
10566 if (!double_brackets_flag) {
10567 tokpushback = 1;
10568 goto out;
10569 }
10570 wordtext = (char *) (t == TAND ? "-a" : "-o");
10571#endif
10572 case TWORD:
10573 n = stzalloc(sizeof(struct narg));
10574 n->type = NARG;
10575
10576 n->narg.text = wordtext;
10577#if ENABLE_ASH_BASH_COMPAT
10578 if (strcmp("[[", wordtext) == 0)
10579 double_brackets_flag = 1;
10580 else if (strcmp("]]", wordtext) == 0)
10581 double_brackets_flag = 0;
10582#endif
10583 n->narg.backquote = backquotelist;
10584 if (savecheckkwd && isassignment(wordtext)) {
10585 *vpp = n;
10586 vpp = &n->narg.next;
10587 } else {
10588 *app = n;
10589 app = &n->narg.next;
10590 savecheckkwd = 0;
10591 }
10592 break;
10593 case TREDIR:
10594 *rpp = n = redirnode;
10595 rpp = &n->nfile.next;
10596 parsefname();
10597 break;
10598 case TLP:
10599 if (args && app == &args->narg.next
10600 && !vars && !redir
10601 ) {
10602 struct builtincmd *bcmd;
10603 const char *name;
10604
10605
10606 if (readtoken() != TRP)
10607 raise_error_unexpected_syntax(TRP);
10608 name = n->narg.text;
10609 if (!goodname(name)
10610 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10611 ) {
10612 raise_error_syntax("bad function name");
10613 }
10614 n->type = NDEFUN;
10615 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10616 n->narg.next = parse_command();
10617 return n;
10618 }
10619
10620 default:
10621 tokpushback = 1;
10622 goto out;
10623 }
10624 }
10625 out:
10626 *app = NULL;
10627 *vpp = NULL;
10628 *rpp = NULL;
10629 n = stzalloc(sizeof(struct ncmd));
10630 n->type = NCMD;
10631 n->ncmd.args = args;
10632 n->ncmd.assign = vars;
10633 n->ncmd.redirect = redir;
10634 return n;
10635}
10636
10637static union node *
10638parse_command(void)
10639{
10640 union node *n1, *n2;
10641 union node *ap, **app;
10642 union node *cp, **cpp;
10643 union node *redir, **rpp;
10644 union node **rpp2;
10645 int t;
10646
10647 redir = NULL;
10648 rpp2 = &redir;
10649
10650 switch (readtoken()) {
10651 default:
10652 raise_error_unexpected_syntax(-1);
10653
10654 case TIF:
10655 n1 = stzalloc(sizeof(struct nif));
10656 n1->type = NIF;
10657 n1->nif.test = list(0);
10658 if (readtoken() != TTHEN)
10659 raise_error_unexpected_syntax(TTHEN);
10660 n1->nif.ifpart = list(0);
10661 n2 = n1;
10662 while (readtoken() == TELIF) {
10663 n2->nif.elsepart = stzalloc(sizeof(struct nif));
10664 n2 = n2->nif.elsepart;
10665 n2->type = NIF;
10666 n2->nif.test = list(0);
10667 if (readtoken() != TTHEN)
10668 raise_error_unexpected_syntax(TTHEN);
10669 n2->nif.ifpart = list(0);
10670 }
10671 if (lasttoken == TELSE)
10672 n2->nif.elsepart = list(0);
10673 else {
10674 n2->nif.elsepart = NULL;
10675 tokpushback = 1;
10676 }
10677 t = TFI;
10678 break;
10679 case TWHILE:
10680 case TUNTIL: {
10681 int got;
10682 n1 = stzalloc(sizeof(struct nbinary));
10683 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
10684 n1->nbinary.ch1 = list(0);
10685 got = readtoken();
10686 if (got != TDO) {
10687 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
10688 got == TWORD ? wordtext : ""));
10689 raise_error_unexpected_syntax(TDO);
10690 }
10691 n1->nbinary.ch2 = list(0);
10692 t = TDONE;
10693 break;
10694 }
10695 case TFOR:
10696 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
10697 raise_error_syntax("bad for loop variable");
10698 n1 = stzalloc(sizeof(struct nfor));
10699 n1->type = NFOR;
10700 n1->nfor.var = wordtext;
10701 checkkwd = CHKKWD | CHKALIAS;
10702 if (readtoken() == TIN) {
10703 app = ≈
10704 while (readtoken() == TWORD) {
10705 n2 = stzalloc(sizeof(struct narg));
10706 n2->type = NARG;
10707
10708 n2->narg.text = wordtext;
10709 n2->narg.backquote = backquotelist;
10710 *app = n2;
10711 app = &n2->narg.next;
10712 }
10713 *app = NULL;
10714 n1->nfor.args = ap;
10715 if (lasttoken != TNL && lasttoken != TSEMI)
10716 raise_error_unexpected_syntax(-1);
10717 } else {
10718 n2 = stzalloc(sizeof(struct narg));
10719 n2->type = NARG;
10720
10721 n2->narg.text = (char *)dolatstr;
10722
10723 n1->nfor.args = n2;
10724
10725
10726
10727
10728 if (lasttoken != TNL && lasttoken != TSEMI)
10729 tokpushback = 1;
10730 }
10731 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10732 if (readtoken() != TDO)
10733 raise_error_unexpected_syntax(TDO);
10734 n1->nfor.body = list(0);
10735 t = TDONE;
10736 break;
10737 case TCASE:
10738 n1 = stzalloc(sizeof(struct ncase));
10739 n1->type = NCASE;
10740 if (readtoken() != TWORD)
10741 raise_error_unexpected_syntax(TWORD);
10742 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
10743 n2->type = NARG;
10744
10745 n2->narg.text = wordtext;
10746 n2->narg.backquote = backquotelist;
10747 do {
10748 checkkwd = CHKKWD | CHKALIAS;
10749 } while (readtoken() == TNL);
10750 if (lasttoken != TIN)
10751 raise_error_unexpected_syntax(TIN);
10752 cpp = &n1->ncase.cases;
10753 next_case:
10754 checkkwd = CHKNL | CHKKWD;
10755 t = readtoken();
10756 while (t != TESAC) {
10757 if (lasttoken == TLP)
10758 readtoken();
10759 *cpp = cp = stzalloc(sizeof(struct nclist));
10760 cp->type = NCLIST;
10761 app = &cp->nclist.pattern;
10762 for (;;) {
10763 *app = ap = stzalloc(sizeof(struct narg));
10764 ap->type = NARG;
10765
10766 ap->narg.text = wordtext;
10767 ap->narg.backquote = backquotelist;
10768 if (readtoken() != TPIPE)
10769 break;
10770 app = &ap->narg.next;
10771 readtoken();
10772 }
10773
10774 if (lasttoken != TRP)
10775 raise_error_unexpected_syntax(TRP);
10776 cp->nclist.body = list(2);
10777
10778 cpp = &cp->nclist.next;
10779
10780 checkkwd = CHKNL | CHKKWD;
10781 t = readtoken();
10782 if (t != TESAC) {
10783 if (t != TENDCASE)
10784 raise_error_unexpected_syntax(TENDCASE);
10785 goto next_case;
10786 }
10787 }
10788 *cpp = NULL;
10789 goto redir;
10790 case TLP:
10791 n1 = stzalloc(sizeof(struct nredir));
10792 n1->type = NSUBSHELL;
10793 n1->nredir.n = list(0);
10794
10795 t = TRP;
10796 break;
10797 case TBEGIN:
10798 n1 = list(0);
10799 t = TEND;
10800 break;
10801 case TWORD:
10802 case TREDIR:
10803 tokpushback = 1;
10804 return simplecmd();
10805 }
10806
10807 if (readtoken() != t)
10808 raise_error_unexpected_syntax(t);
10809
10810 redir:
10811
10812 checkkwd = CHKKWD | CHKALIAS;
10813 rpp = rpp2;
10814 while (readtoken() == TREDIR) {
10815 *rpp = n2 = redirnode;
10816 rpp = &n2->nfile.next;
10817 parsefname();
10818 }
10819 tokpushback = 1;
10820 *rpp = NULL;
10821 if (redir) {
10822 if (n1->type != NSUBSHELL) {
10823 n2 = stzalloc(sizeof(struct nredir));
10824 n2->type = NREDIR;
10825 n2->nredir.n = n1;
10826 n1 = n2;
10827 }
10828 n1->nredir.redirect = redir;
10829 }
10830 return n1;
10831}
10832
10833#if ENABLE_ASH_BASH_COMPAT
10834static int decode_dollar_squote(void)
10835{
10836 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10837 int c, cnt;
10838 char *p;
10839 char buf[4];
10840
10841 c = pgetc();
10842 p = strchr(C_escapes, c);
10843 if (p) {
10844 buf[0] = c;
10845 p = buf;
10846 cnt = 3;
10847 if ((unsigned char)(c - '0') <= 7) {
10848 do {
10849 c = pgetc();
10850 *++p = c;
10851 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10852 pungetc();
10853 } else if (c == 'x') {
10854 do {
10855 c = pgetc();
10856 *++p = c;
10857 } while (isxdigit(c) && --cnt);
10858 pungetc();
10859 if (cnt == 3) {
10860 c = 'x';
10861 goto unrecognized;
10862 }
10863 } else {
10864 p++;
10865 }
10866 *p = '\0';
10867 p = buf;
10868 c = bb_process_escape_sequence((void*)&p);
10869 } else {
10870 if (c != '\'' && c != '"') {
10871 unrecognized:
10872 c |= 0x100;
10873 }
10874 }
10875 return c;
10876}
10877#endif
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890#define CHECKEND() {goto checkend; checkend_return:;}
10891#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10892#define PARSESUB() {goto parsesub; parsesub_return:;}
10893#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10894#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10895#define PARSEARITH() {goto parsearith; parsearith_return:;}
10896static int
10897readtoken1(int c, int syntax, char *eofmark, int striptabs)
10898{
10899
10900
10901 char *out;
10902 int len;
10903 char line[EOFMARKLEN + 1];
10904 struct nodelist *bqlist;
10905 smallint quotef;
10906 smallint dblquote;
10907 smallint oldstyle;
10908 smallint prevsyntax;
10909#if ENABLE_ASH_EXPAND_PRMT
10910 smallint pssyntax;
10911#endif
10912 int varnest;
10913 int arinest;
10914 int parenlevel;
10915 int dqvarnest;
10916
10917 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10918
10919#if __GNUC__
10920
10921 (void) &out;
10922 (void) "ef;
10923 (void) &dblquote;
10924 (void) &varnest;
10925 (void) &arinest;
10926 (void) &parenlevel;
10927 (void) &dqvarnest;
10928 (void) &oldstyle;
10929 (void) &prevsyntax;
10930 (void) &syntax;
10931#endif
10932 startlinno = g_parsefile->linno;
10933 bqlist = NULL;
10934 quotef = 0;
10935 oldstyle = 0;
10936 prevsyntax = 0;
10937#if ENABLE_ASH_EXPAND_PRMT
10938 pssyntax = (syntax == PSSYNTAX);
10939 if (pssyntax)
10940 syntax = DQSYNTAX;
10941#endif
10942 dblquote = (syntax == DQSYNTAX);
10943 varnest = 0;
10944 arinest = 0;
10945 parenlevel = 0;
10946 dqvarnest = 0;
10947
10948 STARTSTACKSTR(out);
10949 loop:
10950
10951 {
10952 CHECKEND();
10953 for (;;) {
10954 CHECKSTRSPACE(4, out);
10955 switch (SIT(c, syntax)) {
10956 case CNL:
10957 if (syntax == BASESYNTAX)
10958 goto endword;
10959 USTPUTC(c, out);
10960 g_parsefile->linno++;
10961 if (doprompt)
10962 setprompt(2);
10963 c = pgetc();
10964 goto loop;
10965 case CWORD:
10966 USTPUTC(c, out);
10967 break;
10968 case CCTL:
10969 if (eofmark == NULL || dblquote)
10970 USTPUTC(CTLESC, out);
10971#if ENABLE_ASH_BASH_COMPAT
10972 if (c == '\\' && bash_dollar_squote) {
10973 c = decode_dollar_squote();
10974 if (c & 0x100) {
10975 USTPUTC('\\', out);
10976 c = (unsigned char)c;
10977 }
10978 }
10979#endif
10980 USTPUTC(c, out);
10981 break;
10982 case CBACK:
10983 c = pgetc_without_PEOA();
10984 if (c == PEOF) {
10985 USTPUTC(CTLESC, out);
10986 USTPUTC('\\', out);
10987 pungetc();
10988 } else if (c == '\n') {
10989 if (doprompt)
10990 setprompt(2);
10991 } else {
10992#if ENABLE_ASH_EXPAND_PRMT
10993 if (c == '$' && pssyntax) {
10994 USTPUTC(CTLESC, out);
10995 USTPUTC('\\', out);
10996 }
10997#endif
10998 if (dblquote && c != '\\'
10999 && c != '`' && c != '$'
11000 && (c != '"' || eofmark != NULL)
11001 ) {
11002 USTPUTC(CTLESC, out);
11003 USTPUTC('\\', out);
11004 }
11005 if (SIT(c, SQSYNTAX) == CCTL)
11006 USTPUTC(CTLESC, out);
11007 USTPUTC(c, out);
11008 quotef = 1;
11009 }
11010 break;
11011 case CSQUOTE:
11012 syntax = SQSYNTAX;
11013 quotemark:
11014 if (eofmark == NULL) {
11015 USTPUTC(CTLQUOTEMARK, out);
11016 }
11017 break;
11018 case CDQUOTE:
11019 syntax = DQSYNTAX;
11020 dblquote = 1;
11021 goto quotemark;
11022 case CENDQUOTE:
11023 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11024 if (eofmark != NULL && arinest == 0
11025 && varnest == 0
11026 ) {
11027 USTPUTC(c, out);
11028 } else {
11029 if (dqvarnest == 0) {
11030 syntax = BASESYNTAX;
11031 dblquote = 0;
11032 }
11033 quotef = 1;
11034 goto quotemark;
11035 }
11036 break;
11037 case CVAR:
11038 PARSESUB();
11039 break;
11040 case CENDVAR:
11041 if (varnest > 0) {
11042 varnest--;
11043 if (dqvarnest > 0) {
11044 dqvarnest--;
11045 }
11046 USTPUTC(CTLENDVAR, out);
11047 } else {
11048 USTPUTC(c, out);
11049 }
11050 break;
11051#if ENABLE_SH_MATH_SUPPORT
11052 case CLP:
11053 parenlevel++;
11054 USTPUTC(c, out);
11055 break;
11056 case CRP:
11057 if (parenlevel > 0) {
11058 USTPUTC(c, out);
11059 --parenlevel;
11060 } else {
11061 if (pgetc() == ')') {
11062 if (--arinest == 0) {
11063 USTPUTC(CTLENDARI, out);
11064 syntax = prevsyntax;
11065 dblquote = (syntax == DQSYNTAX);
11066 } else
11067 USTPUTC(')', out);
11068 } else {
11069
11070
11071
11072
11073 pungetc();
11074 USTPUTC(')', out);
11075 }
11076 }
11077 break;
11078#endif
11079 case CBQUOTE:
11080 PARSEBACKQOLD();
11081 break;
11082 case CENDFILE:
11083 goto endword;
11084 case CIGN:
11085 break;
11086 default:
11087 if (varnest == 0) {
11088#if ENABLE_ASH_BASH_COMPAT
11089 if (c == '&') {
11090 if (pgetc() == '>')
11091 c = 0x100 + '>';
11092 pungetc();
11093 }
11094#endif
11095 goto endword;
11096 }
11097 IF_ASH_ALIAS(if (c != PEOA))
11098 USTPUTC(c, out);
11099
11100 }
11101 c = pgetc_fast();
11102 }
11103 }
11104 endword:
11105#if ENABLE_SH_MATH_SUPPORT
11106 if (syntax == ARISYNTAX)
11107 raise_error_syntax("missing '))'");
11108#endif
11109 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
11110 raise_error_syntax("unterminated quoted string");
11111 if (varnest != 0) {
11112 startlinno = g_parsefile->linno;
11113
11114 raise_error_syntax("missing '}'");
11115 }
11116 USTPUTC('\0', out);
11117 len = out - (char *)stackblock();
11118 out = stackblock();
11119 if (eofmark == NULL) {
11120 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
11121 && quotef == 0
11122 ) {
11123 if (isdigit_str9(out)) {
11124 PARSEREDIR();
11125 lasttoken = TREDIR;
11126 return lasttoken;
11127 }
11128
11129
11130 }
11131 pungetc();
11132 }
11133 quoteflag = quotef;
11134 backquotelist = bqlist;
11135 grabstackblock(len);
11136 wordtext = out;
11137 lasttoken = TWORD;
11138 return lasttoken;
11139
11140
11141
11142
11143
11144
11145
11146checkend: {
11147 if (eofmark) {
11148#if ENABLE_ASH_ALIAS
11149 if (c == PEOA)
11150 c = pgetc_without_PEOA();
11151#endif
11152 if (striptabs) {
11153 while (c == '\t') {
11154 c = pgetc_without_PEOA();
11155 }
11156 }
11157 if (c == *eofmark) {
11158 if (pfgets(line, sizeof(line)) != NULL) {
11159 char *p, *q;
11160
11161 p = line;
11162 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11163 continue;
11164 if (*p == '\n' && *q == '\0') {
11165 c = PEOF;
11166 g_parsefile->linno++;
11167 needprompt = doprompt;
11168 } else {
11169 pushstring(line, NULL);
11170 }
11171 }
11172 }
11173 }
11174 goto checkend_return;
11175}
11176
11177
11178
11179
11180
11181
11182parseredir: {
11183
11184 int fd = (*out == '\0' ? -1 : atoi(out));
11185 union node *np;
11186
11187 np = stzalloc(sizeof(struct nfile));
11188 if (c == '>') {
11189 np->nfile.fd = 1;
11190 c = pgetc();
11191 if (c == '>')
11192 np->type = NAPPEND;
11193 else if (c == '|')
11194 np->type = NCLOBBER;
11195 else if (c == '&')
11196 np->type = NTOFD;
11197
11198 else {
11199 np->type = NTO;
11200 pungetc();
11201 }
11202 }
11203#if ENABLE_ASH_BASH_COMPAT
11204 else if (c == 0x100 + '>') {
11205 np->nfile.fd = 1;
11206 pgetc();
11207 np->type = NTO2;
11208 }
11209#endif
11210 else {
11211
11212 c = pgetc();
11213 switch (c) {
11214 case '<':
11215 if (sizeof(struct nfile) != sizeof(struct nhere)) {
11216 np = stzalloc(sizeof(struct nhere));
11217
11218 }
11219 np->type = NHERE;
11220 heredoc = stzalloc(sizeof(struct heredoc));
11221 heredoc->here = np;
11222 c = pgetc();
11223 if (c == '-') {
11224 heredoc->striptabs = 1;
11225 } else {
11226
11227 pungetc();
11228 }
11229 break;
11230
11231 case '&':
11232 np->type = NFROMFD;
11233 break;
11234
11235 case '>':
11236 np->type = NFROMTO;
11237 break;
11238
11239 default:
11240 np->type = NFROM;
11241 pungetc();
11242 break;
11243 }
11244 }
11245 if (fd >= 0)
11246 np->nfile.fd = fd;
11247 redirnode = np;
11248 goto parseredir_return;
11249}
11250
11251
11252
11253
11254
11255
11256
11257
11258#define is_special(c) \
11259 (((unsigned)(c) - 33 < 32) \
11260 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
11261parsesub: {
11262 unsigned char subtype;
11263 int typeloc;
11264 int flags;
11265 char *p;
11266 static const char types[] ALIGN1 = "}-+?=";
11267
11268 c = pgetc();
11269 if (c > 255
11270 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
11271 ) {
11272#if ENABLE_ASH_BASH_COMPAT
11273 if (c == '\'')
11274 bash_dollar_squote = 1;
11275 else
11276#endif
11277 USTPUTC('$', out);
11278 pungetc();
11279 } else if (c == '(') {
11280 if (pgetc() == '(') {
11281#if ENABLE_SH_MATH_SUPPORT
11282 PARSEARITH();
11283#else
11284 raise_error_syntax("you disabled math support for $((arith)) syntax");
11285#endif
11286 } else {
11287 pungetc();
11288 PARSEBACKQNEW();
11289 }
11290 } else {
11291 USTPUTC(CTLVAR, out);
11292 typeloc = out - (char *)stackblock();
11293 USTPUTC(VSNORMAL, out);
11294 subtype = VSNORMAL;
11295 if (c == '{') {
11296 c = pgetc();
11297 if (c == '#') {
11298 c = pgetc();
11299 if (c == '}')
11300 c = '#';
11301 else
11302 subtype = VSLENGTH;
11303 } else
11304 subtype = 0;
11305 }
11306 if (c <= 255 && is_name(c)) {
11307 do {
11308 STPUTC(c, out);
11309 c = pgetc();
11310 } while (c <= 255 && is_in_name(c));
11311 } else if (isdigit(c)) {
11312 do {
11313 STPUTC(c, out);
11314 c = pgetc();
11315 } while (isdigit(c));
11316 } else if (is_special(c)) {
11317 USTPUTC(c, out);
11318 c = pgetc();
11319 } else {
11320 badsub:
11321 raise_error_syntax("bad substitution");
11322 }
11323 if (c != '}' && subtype == VSLENGTH)
11324 goto badsub;
11325
11326 STPUTC('=', out);
11327 flags = 0;
11328 if (subtype == 0) {
11329 switch (c) {
11330 case ':':
11331 c = pgetc();
11332#if ENABLE_ASH_BASH_COMPAT
11333 if (c == ':' || c == '$' || isdigit(c)) {
11334 pungetc();
11335 subtype = VSSUBSTR;
11336 break;
11337 }
11338#endif
11339 flags = VSNUL;
11340
11341 default:
11342 p = strchr(types, c);
11343 if (p == NULL)
11344 goto badsub;
11345 subtype = p - types + VSNORMAL;
11346 break;
11347 case '%':
11348 case '#': {
11349 int cc = c;
11350 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11351 c = pgetc();
11352 if (c == cc)
11353 subtype++;
11354 else
11355 pungetc();
11356 break;
11357 }
11358#if ENABLE_ASH_BASH_COMPAT
11359 case '/':
11360 subtype = VSREPLACE;
11361 c = pgetc();
11362 if (c == '/')
11363 subtype++;
11364 else
11365 pungetc();
11366 break;
11367#endif
11368 }
11369 } else {
11370 pungetc();
11371 }
11372 if (dblquote || arinest)
11373 flags |= VSQUOTE;
11374 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
11375 if (subtype != VSNORMAL) {
11376 varnest++;
11377 if (dblquote || arinest) {
11378 dqvarnest++;
11379 }
11380 }
11381 }
11382 goto parsesub_return;
11383}
11384
11385
11386
11387
11388
11389
11390
11391parsebackq: {
11392 struct nodelist **nlpp;
11393 smallint savepbq;
11394 union node *n;
11395 char *volatile str;
11396 struct jmploc jmploc;
11397 struct jmploc *volatile savehandler;
11398 size_t savelen;
11399 smallint saveprompt = 0;
11400
11401#ifdef __GNUC__
11402 (void) &saveprompt;
11403#endif
11404 savepbq = parsebackquote;
11405 if (setjmp(jmploc.loc)) {
11406 free(str);
11407 parsebackquote = 0;
11408 exception_handler = savehandler;
11409 longjmp(exception_handler->loc, 1);
11410 }
11411 INT_OFF;
11412 str = NULL;
11413 savelen = out - (char *)stackblock();
11414 if (savelen > 0) {
11415 str = ckmalloc(savelen);
11416 memcpy(str, stackblock(), savelen);
11417 }
11418 savehandler = exception_handler;
11419 exception_handler = &jmploc;
11420 INT_ON;
11421 if (oldstyle) {
11422
11423
11424
11425 char *pout;
11426 int pc;
11427 size_t psavelen;
11428 char *pstr;
11429
11430
11431 STARTSTACKSTR(pout);
11432 for (;;) {
11433 if (needprompt) {
11434 setprompt(2);
11435 }
11436 pc = pgetc();
11437 switch (pc) {
11438 case '`':
11439 goto done;
11440
11441 case '\\':
11442 pc = pgetc();
11443 if (pc == '\n') {
11444 g_parsefile->linno++;
11445 if (doprompt)
11446 setprompt(2);
11447
11448
11449
11450
11451
11452
11453 continue;
11454 }
11455 if (pc != '\\' && pc != '`' && pc != '$'
11456 && (!dblquote || pc != '"')
11457 ) {
11458 STPUTC('\\', pout);
11459 }
11460 if (pc <= 255 ) {
11461 break;
11462 }
11463
11464
11465 case PEOF:
11466 IF_ASH_ALIAS(case PEOA:)
11467 startlinno = g_parsefile->linno;
11468 raise_error_syntax("EOF in backquote substitution");
11469
11470 case '\n':
11471 g_parsefile->linno++;
11472 needprompt = doprompt;
11473 break;
11474
11475 default:
11476 break;
11477 }
11478 STPUTC(pc, pout);
11479 }
11480 done:
11481 STPUTC('\0', pout);
11482 psavelen = pout - (char *)stackblock();
11483 if (psavelen > 0) {
11484 pstr = grabstackstr(pout);
11485 setinputstring(pstr);
11486 }
11487 }
11488 nlpp = &bqlist;
11489 while (*nlpp)
11490 nlpp = &(*nlpp)->next;
11491 *nlpp = stzalloc(sizeof(**nlpp));
11492
11493 parsebackquote = oldstyle;
11494
11495 if (oldstyle) {
11496 saveprompt = doprompt;
11497 doprompt = 0;
11498 }
11499
11500 n = list(2);
11501
11502 if (oldstyle)
11503 doprompt = saveprompt;
11504 else if (readtoken() != TRP)
11505 raise_error_unexpected_syntax(TRP);
11506
11507 (*nlpp)->n = n;
11508 if (oldstyle) {
11509
11510
11511
11512
11513 popfile();
11514 tokpushback = 0;
11515 }
11516 while (stackblocksize() <= savelen)
11517 growstackblock();
11518 STARTSTACKSTR(out);
11519 if (str) {
11520 memcpy(out, str, savelen);
11521 STADJUST(savelen, out);
11522 INT_OFF;
11523 free(str);
11524 str = NULL;
11525 INT_ON;
11526 }
11527 parsebackquote = savepbq;
11528 exception_handler = savehandler;
11529 if (arinest || dblquote)
11530 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11531 else
11532 USTPUTC(CTLBACKQ, out);
11533 if (oldstyle)
11534 goto parsebackq_oldreturn;
11535 goto parsebackq_newreturn;
11536}
11537
11538#if ENABLE_SH_MATH_SUPPORT
11539
11540
11541
11542parsearith: {
11543 if (++arinest == 1) {
11544 prevsyntax = syntax;
11545 syntax = ARISYNTAX;
11546 USTPUTC(CTLARI, out);
11547 if (dblquote)
11548 USTPUTC('"', out);
11549 else
11550 USTPUTC(' ', out);
11551 } else {
11552
11553
11554
11555
11556 USTPUTC('(', out);
11557 }
11558 goto parsearith_return;
11559}
11560#endif
11561
11562}
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581#define NEW_xxreadtoken
11582#ifdef NEW_xxreadtoken
11583
11584static const char xxreadtoken_chars[7] ALIGN1 = {
11585 '\n', '(', ')',
11586 '&', '|', ';',
11587 0
11588};
11589
11590#define xxreadtoken_singles 3
11591#define xxreadtoken_doubles 3
11592
11593static const char xxreadtoken_tokens[] ALIGN1 = {
11594 TNL, TLP, TRP,
11595 TBACKGND, TPIPE, TSEMI,
11596 TEOF,
11597 TAND, TOR, TENDCASE
11598};
11599
11600static int
11601xxreadtoken(void)
11602{
11603 int c;
11604
11605 if (tokpushback) {
11606 tokpushback = 0;
11607 return lasttoken;
11608 }
11609 if (needprompt) {
11610 setprompt(2);
11611 }
11612 startlinno = g_parsefile->linno;
11613 for (;;) {
11614 c = pgetc_fast();
11615 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
11616 continue;
11617
11618 if (c == '#') {
11619 while ((c = pgetc()) != '\n' && c != PEOF)
11620 continue;
11621 pungetc();
11622 } else if (c == '\\') {
11623 if (pgetc() != '\n') {
11624 pungetc();
11625 break;
11626 }
11627 startlinno = ++g_parsefile->linno;
11628 if (doprompt)
11629 setprompt(2);
11630 } else {
11631 const char *p;
11632
11633 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11634 if (c != PEOF) {
11635 if (c == '\n') {
11636 g_parsefile->linno++;
11637 needprompt = doprompt;
11638 }
11639
11640 p = strchr(xxreadtoken_chars, c);
11641 if (p == NULL)
11642 break;
11643
11644 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11645 int cc = pgetc();
11646 if (cc == c) {
11647 p += xxreadtoken_doubles + 1;
11648 } else {
11649 pungetc();
11650#if ENABLE_ASH_BASH_COMPAT
11651 if (c == '&' && cc == '>')
11652 break;
11653#endif
11654 }
11655 }
11656 }
11657 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11658 return lasttoken;
11659 }
11660 }
11661
11662 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11663}
11664#else
11665#define RETURN(token) return lasttoken = token
11666static int
11667xxreadtoken(void)
11668{
11669 int c;
11670
11671 if (tokpushback) {
11672 tokpushback = 0;
11673 return lasttoken;
11674 }
11675 if (needprompt) {
11676 setprompt(2);
11677 }
11678 startlinno = g_parsefile->linno;
11679 for (;;) {
11680 c = pgetc_fast();
11681 switch (c) {
11682 case ' ': case '\t':
11683 IF_ASH_ALIAS(case PEOA:)
11684 continue;
11685 case '#':
11686 while ((c = pgetc()) != '\n' && c != PEOF)
11687 continue;
11688 pungetc();
11689 continue;
11690 case '\\':
11691 if (pgetc() == '\n') {
11692 startlinno = ++g_parsefile->linno;
11693 if (doprompt)
11694 setprompt(2);
11695 continue;
11696 }
11697 pungetc();
11698 goto breakloop;
11699 case '\n':
11700 g_parsefile->linno++;
11701 needprompt = doprompt;
11702 RETURN(TNL);
11703 case PEOF:
11704 RETURN(TEOF);
11705 case '&':
11706 if (pgetc() == '&')
11707 RETURN(TAND);
11708 pungetc();
11709 RETURN(TBACKGND);
11710 case '|':
11711 if (pgetc() == '|')
11712 RETURN(TOR);
11713 pungetc();
11714 RETURN(TPIPE);
11715 case ';':
11716 if (pgetc() == ';')
11717 RETURN(TENDCASE);
11718 pungetc();
11719 RETURN(TSEMI);
11720 case '(':
11721 RETURN(TLP);
11722 case ')':
11723 RETURN(TRP);
11724 default:
11725 goto breakloop;
11726 }
11727 }
11728 breakloop:
11729 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11730#undef RETURN
11731}
11732#endif
11733
11734static int
11735readtoken(void)
11736{
11737 int t;
11738#if DEBUG
11739 smallint alreadyseen = tokpushback;
11740#endif
11741
11742#if ENABLE_ASH_ALIAS
11743 top:
11744#endif
11745
11746 t = xxreadtoken();
11747
11748
11749
11750
11751 if (checkkwd & CHKNL) {
11752 while (t == TNL) {
11753 parseheredoc();
11754 t = xxreadtoken();
11755 }
11756 }
11757
11758 if (t != TWORD || quoteflag) {
11759 goto out;
11760 }
11761
11762
11763
11764
11765 if (checkkwd & CHKKWD) {
11766 const char *const *pp;
11767
11768 pp = findkwd(wordtext);
11769 if (pp) {
11770 lasttoken = t = pp - tokname_array;
11771 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
11772 goto out;
11773 }
11774 }
11775
11776 if (checkkwd & CHKALIAS) {
11777#if ENABLE_ASH_ALIAS
11778 struct alias *ap;
11779 ap = lookupalias(wordtext, 1);
11780 if (ap != NULL) {
11781 if (*ap->val) {
11782 pushstring(ap->val, ap);
11783 }
11784 goto top;
11785 }
11786#endif
11787 }
11788 out:
11789 checkkwd = 0;
11790#if DEBUG
11791 if (!alreadyseen)
11792 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
11793 else
11794 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
11795#endif
11796 return t;
11797}
11798
11799static char
11800peektoken(void)
11801{
11802 int t;
11803
11804 t = readtoken();
11805 tokpushback = 1;
11806 return tokname_array[t][0];
11807}
11808
11809
11810
11811
11812
11813static union node *
11814parsecmd(int interact)
11815{
11816 int t;
11817
11818 tokpushback = 0;
11819 doprompt = interact;
11820 if (doprompt)
11821 setprompt(doprompt);
11822 needprompt = 0;
11823 t = readtoken();
11824 if (t == TEOF)
11825 return NODE_EOF;
11826 if (t == TNL)
11827 return NULL;
11828 tokpushback = 1;
11829 return list(1);
11830}
11831
11832
11833
11834
11835static void
11836parseheredoc(void)
11837{
11838 struct heredoc *here;
11839 union node *n;
11840
11841 here = heredoclist;
11842 heredoclist = NULL;
11843
11844 while (here) {
11845 if (needprompt) {
11846 setprompt(2);
11847 }
11848 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11849 here->eofmark, here->striptabs);
11850 n = stzalloc(sizeof(struct narg));
11851 n->narg.type = NARG;
11852
11853 n->narg.text = wordtext;
11854 n->narg.backquote = backquotelist;
11855 here->here->nhere.doc = n;
11856 here = here->next;
11857 }
11858}
11859
11860
11861
11862
11863
11864#if ENABLE_ASH_EXPAND_PRMT
11865static const char *
11866expandstr(const char *ps)
11867{
11868 union node n;
11869
11870
11871
11872 setinputstring((char *)ps);
11873 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
11874 popfile();
11875
11876 n.narg.type = NARG;
11877 n.narg.next = NULL;
11878 n.narg.text = wordtext;
11879 n.narg.backquote = backquotelist;
11880
11881 expandarg(&n, NULL, 0);
11882 return stackblock();
11883}
11884#endif
11885
11886
11887
11888
11889static int
11890evalstring(char *s, int mask)
11891{
11892 union node *n;
11893 struct stackmark smark;
11894 int skip;
11895
11896 setinputstring(s);
11897 setstackmark(&smark);
11898
11899 skip = 0;
11900 while ((n = parsecmd(0)) != NODE_EOF) {
11901 evaltree(n, 0);
11902 popstackmark(&smark);
11903 skip = evalskip;
11904 if (skip)
11905 break;
11906 }
11907 popfile();
11908
11909 skip &= mask;
11910 evalskip = skip;
11911 return skip;
11912}
11913
11914
11915
11916
11917static int FAST_FUNC
11918evalcmd(int argc UNUSED_PARAM, char **argv)
11919{
11920 char *p;
11921 char *concat;
11922
11923 if (argv[1]) {
11924 p = argv[1];
11925 argv += 2;
11926 if (argv[0]) {
11927 STARTSTACKSTR(concat);
11928 for (;;) {
11929 concat = stack_putstr(p, concat);
11930 p = *argv++;
11931 if (p == NULL)
11932 break;
11933 STPUTC(' ', concat);
11934 }
11935 STPUTC('\0', concat);
11936 p = grabstackstr(concat);
11937 }
11938 evalstring(p, ~SKIPEVAL);
11939
11940 }
11941 return exitstatus;
11942}
11943
11944
11945
11946
11947
11948
11949static int
11950cmdloop(int top)
11951{
11952 union node *n;
11953 struct stackmark smark;
11954 int inter;
11955 int numeof = 0;
11956
11957 TRACE(("cmdloop(%d) called\n", top));
11958 for (;;) {
11959 int skip;
11960
11961 setstackmark(&smark);
11962#if JOBS
11963 if (doing_jobctl)
11964 showjobs(stderr, SHOW_CHANGED);
11965#endif
11966 inter = 0;
11967 if (iflag && top) {
11968 inter++;
11969#if ENABLE_ASH_MAIL
11970 chkmail();
11971#endif
11972 }
11973 n = parsecmd(inter);
11974#if DEBUG
11975 if (DEBUG > 2 && debug && (n != NODE_EOF))
11976 showtree(n);
11977#endif
11978 if (n == NODE_EOF) {
11979 if (!top || numeof >= 50)
11980 break;
11981 if (!stoppedjobs()) {
11982 if (!Iflag)
11983 break;
11984 out2str("\nUse \"exit\" to leave shell.\n");
11985 }
11986 numeof++;
11987 } else if (nflag == 0) {
11988
11989 job_warning >>= 1;
11990 numeof = 0;
11991 evaltree(n, 0);
11992 }
11993 popstackmark(&smark);
11994 skip = evalskip;
11995
11996 if (skip) {
11997 evalskip = 0;
11998 return skip & SKIPEVAL;
11999 }
12000 }
12001 return 0;
12002}
12003
12004
12005
12006
12007
12008static char *
12009find_dot_file(char *name)
12010{
12011 char *fullname;
12012 const char *path = pathval();
12013 struct stat statb;
12014
12015
12016 if (strchr(name, '/'))
12017 return name;
12018
12019
12020
12021
12022 if (1) {
12023 fullname = name;
12024 goto try_cur_dir;
12025 }
12026
12027 while ((fullname = path_advance(&path, name)) != NULL) {
12028 try_cur_dir:
12029 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12030
12031
12032
12033
12034 return fullname;
12035 }
12036 if (fullname != name)
12037 stunalloc(fullname);
12038 }
12039
12040
12041 ash_msg_and_raise_error("%s: not found", name);
12042
12043}
12044
12045static int FAST_FUNC
12046dotcmd(int argc, char **argv)
12047{
12048 char *fullname;
12049 struct strlist *sp;
12050 volatile struct shparam saveparam;
12051
12052 for (sp = cmdenviron; sp; sp = sp->next)
12053 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
12054
12055 if (!argv[1]) {
12056
12057 return 2;
12058 }
12059
12060
12061 exitstatus = 0;
12062
12063 fullname = find_dot_file(argv[1]);
12064
12065 argv += 2;
12066 argc -= 2;
12067 if (argc) {
12068 saveparam = shellparam;
12069 shellparam.malloced = 0;
12070 shellparam.nparam = argc;
12071 shellparam.p = argv;
12072 };
12073
12074 setinputfile(fullname, INPUT_PUSH_FILE);
12075 commandname = fullname;
12076 cmdloop(0);
12077 popfile();
12078
12079 if (argc) {
12080 freeparam(&shellparam);
12081 shellparam = saveparam;
12082 };
12083
12084 return exitstatus;
12085}
12086
12087static int FAST_FUNC
12088exitcmd(int argc UNUSED_PARAM, char **argv)
12089{
12090 if (stoppedjobs())
12091 return 0;
12092 if (argv[1])
12093 exitstatus = number(argv[1]);
12094 raise_exception(EXEXIT);
12095
12096}
12097
12098
12099
12100
12101static void
12102readcmdfile(char *name)
12103{
12104 setinputfile(name, INPUT_PUSH_FILE);
12105 cmdloop(0);
12106 popfile();
12107}
12108
12109
12110
12111
12112
12113
12114
12115
12116static void
12117find_command(char *name, struct cmdentry *entry, int act, const char *path)
12118{
12119 struct tblentry *cmdp;
12120 int idx;
12121 int prev;
12122 char *fullname;
12123 struct stat statb;
12124 int e;
12125 int updatetbl;
12126 struct builtincmd *bcmd;
12127
12128
12129 if (strchr(name, '/') != NULL) {
12130 entry->u.index = -1;
12131 if (act & DO_ABS) {
12132 while (stat(name, &statb) < 0) {
12133#ifdef SYSV
12134 if (errno == EINTR)
12135 continue;
12136#endif
12137 entry->cmdtype = CMDUNKNOWN;
12138 return;
12139 }
12140 }
12141 entry->cmdtype = CMDNORMAL;
12142 return;
12143 }
12144
12145
12146
12147 updatetbl = (path == pathval());
12148 if (!updatetbl) {
12149 act |= DO_ALTPATH;
12150 if (strstr(path, "%builtin") != NULL)
12151 act |= DO_ALTBLTIN;
12152 }
12153
12154
12155 cmdp = cmdlookup(name, 0);
12156 if (cmdp != NULL) {
12157 int bit;
12158
12159 switch (cmdp->cmdtype) {
12160 default:
12161#if DEBUG
12162 abort();
12163#endif
12164 case CMDNORMAL:
12165 bit = DO_ALTPATH;
12166 break;
12167 case CMDFUNCTION:
12168 bit = DO_NOFUNC;
12169 break;
12170 case CMDBUILTIN:
12171 bit = DO_ALTBLTIN;
12172 break;
12173 }
12174 if (act & bit) {
12175 updatetbl = 0;
12176 cmdp = NULL;
12177 } else if (cmdp->rehash == 0)
12178
12179 goto success;
12180 }
12181
12182
12183 bcmd = find_builtin(name);
12184 if (bcmd) {
12185 if (IS_BUILTIN_REGULAR(bcmd))
12186 goto builtin_success;
12187 if (act & DO_ALTPATH) {
12188 if (!(act & DO_ALTBLTIN))
12189 goto builtin_success;
12190 } else if (builtinloc <= 0) {
12191 goto builtin_success;
12192 }
12193 }
12194
12195#if ENABLE_FEATURE_SH_STANDALONE
12196 {
12197 int applet_no = find_applet_by_name(name);
12198 if (applet_no >= 0) {
12199 entry->cmdtype = CMDNORMAL;
12200 entry->u.index = -2 - applet_no;
12201 return;
12202 }
12203 }
12204#endif
12205
12206
12207 prev = -1;
12208 if (cmdp && cmdp->rehash) {
12209 if (cmdp->cmdtype == CMDBUILTIN)
12210 prev = builtinloc;
12211 else
12212 prev = cmdp->param.index;
12213 }
12214
12215 e = ENOENT;
12216 idx = -1;
12217 loop:
12218 while ((fullname = path_advance(&path, name)) != NULL) {
12219 stunalloc(fullname);
12220
12221
12222 idx++;
12223 if (pathopt) {
12224 if (prefix(pathopt, "builtin")) {
12225 if (bcmd)
12226 goto builtin_success;
12227 continue;
12228 }
12229 if ((act & DO_NOFUNC)
12230 || !prefix(pathopt, "func")
12231 ) {
12232 continue;
12233 }
12234 }
12235
12236 if (fullname[0] == '/' && idx <= prev) {
12237 if (idx < prev)
12238 continue;
12239 TRACE(("searchexec \"%s\": no change\n", name));
12240 goto success;
12241 }
12242 while (stat(fullname, &statb) < 0) {
12243#ifdef SYSV
12244 if (errno == EINTR)
12245 continue;
12246#endif
12247 if (errno != ENOENT && errno != ENOTDIR)
12248 e = errno;
12249 goto loop;
12250 }
12251 e = EACCES;
12252 if (!S_ISREG(statb.st_mode))
12253 continue;
12254 if (pathopt) {
12255 stalloc(strlen(fullname) + 1);
12256
12257
12258
12259 readcmdfile(fullname);
12260 cmdp = cmdlookup(name, 0);
12261 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12262 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12263 stunalloc(fullname);
12264 goto success;
12265 }
12266 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12267 if (!updatetbl) {
12268 entry->cmdtype = CMDNORMAL;
12269 entry->u.index = idx;
12270 return;
12271 }
12272 INT_OFF;
12273 cmdp = cmdlookup(name, 1);
12274 cmdp->cmdtype = CMDNORMAL;
12275 cmdp->param.index = idx;
12276 INT_ON;
12277 goto success;
12278 }
12279
12280
12281 if (cmdp && updatetbl)
12282 delete_cmd_entry();
12283 if (act & DO_ERR)
12284 ash_msg("%s: %s", name, errmsg(e, "not found"));
12285 entry->cmdtype = CMDUNKNOWN;
12286 return;
12287
12288 builtin_success:
12289 if (!updatetbl) {
12290 entry->cmdtype = CMDBUILTIN;
12291 entry->u.cmd = bcmd;
12292 return;
12293 }
12294 INT_OFF;
12295 cmdp = cmdlookup(name, 1);
12296 cmdp->cmdtype = CMDBUILTIN;
12297 cmdp->param.cmd = bcmd;
12298 INT_ON;
12299 success:
12300 cmdp->rehash = 0;
12301 entry->cmdtype = cmdp->cmdtype;
12302 entry->u = cmdp->param;
12303}
12304
12305
12306
12307
12308
12309
12310
12311static int FAST_FUNC
12312trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12313{
12314 char *action;
12315 char **ap;
12316 int signo, exitcode;
12317
12318 nextopt(nullstr);
12319 ap = argptr;
12320 if (!*ap) {
12321 for (signo = 0; signo < NSIG; signo++) {
12322 char *tr = trap_ptr[signo];
12323 if (tr) {
12324
12325
12326
12327
12328 out1fmt("trap -- %s %s\n",
12329 single_quote(tr),
12330 get_signame(signo));
12331
12332
12333
12334
12335 }
12336 }
12337
12338
12339
12340
12341
12342
12343 return 0;
12344 }
12345
12346 action = NULL;
12347 if (ap[1])
12348 action = *ap++;
12349 exitcode = 0;
12350 while (*ap) {
12351 signo = get_signum(*ap);
12352 if (signo < 0) {
12353
12354 ash_msg("%s: invalid signal specification", *ap);
12355 exitcode = 1;
12356 goto next;
12357 }
12358 INT_OFF;
12359 if (action) {
12360 if (LONE_DASH(action))
12361 action = NULL;
12362 else
12363 action = ckstrdup(action);
12364 }
12365 free(trap[signo]);
12366 if (action)
12367 may_have_traps = 1;
12368 trap[signo] = action;
12369 if (signo != 0)
12370 setsignal(signo);
12371 INT_ON;
12372 next:
12373 ap++;
12374 }
12375 return exitcode;
12376}
12377
12378
12379
12380
12381#if !ENABLE_FEATURE_SH_EXTRA_QUIET
12382
12383
12384
12385static int FAST_FUNC
12386helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12387{
12388 unsigned col;
12389 unsigned i;
12390
12391 out1fmt(
12392 "Built-in commands:\n"
12393 "------------------\n");
12394 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
12395 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
12396 builtintab[i].name + 1);
12397 if (col > 60) {
12398 out1fmt("\n");
12399 col = 0;
12400 }
12401 }
12402#if ENABLE_FEATURE_SH_STANDALONE
12403 {
12404 const char *a = applet_names;
12405 while (*a) {
12406 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12407 if (col > 60) {
12408 out1fmt("\n");
12409 col = 0;
12410 }
12411 a += strlen(a) + 1;
12412 }
12413 }
12414#endif
12415 out1fmt("\n\n");
12416 return EXIT_SUCCESS;
12417}
12418#endif
12419
12420
12421
12422
12423static int FAST_FUNC
12424exportcmd(int argc UNUSED_PARAM, char **argv)
12425{
12426 struct var *vp;
12427 char *name;
12428 const char *p;
12429 char **aptr;
12430 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
12431
12432 if (nextopt("p") != 'p') {
12433 aptr = argptr;
12434 name = *aptr;
12435 if (name) {
12436 do {
12437 p = strchr(name, '=');
12438 if (p != NULL) {
12439 p++;
12440 } else {
12441 vp = *findvar(hashvar(name), name);
12442 if (vp) {
12443 vp->flags |= flag;
12444 continue;
12445 }
12446 }
12447 setvar(name, p, flag);
12448 } while ((name = *++aptr) != NULL);
12449 return 0;
12450 }
12451 }
12452 showvars(argv[0], flag, 0);
12453 return 0;
12454}
12455
12456
12457
12458
12459static void
12460unsetfunc(const char *name)
12461{
12462 struct tblentry *cmdp;
12463
12464 cmdp = cmdlookup(name, 0);
12465 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
12466 delete_cmd_entry();
12467}
12468
12469
12470
12471
12472
12473
12474static int FAST_FUNC
12475unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12476{
12477 char **ap;
12478 int i;
12479 int flag = 0;
12480 int ret = 0;
12481
12482 while ((i = nextopt("vf")) != 0) {
12483 flag = i;
12484 }
12485
12486 for (ap = argptr; *ap; ap++) {
12487 if (flag != 'f') {
12488 i = unsetvar(*ap);
12489 ret |= i;
12490 if (!(i & 2))
12491 continue;
12492 }
12493 if (flag != 'v')
12494 unsetfunc(*ap);
12495 }
12496 return ret & 1;
12497}
12498
12499static const unsigned char timescmd_str[] ALIGN1 = {
12500 ' ', offsetof(struct tms, tms_utime),
12501 '\n', offsetof(struct tms, tms_stime),
12502 ' ', offsetof(struct tms, tms_cutime),
12503 '\n', offsetof(struct tms, tms_cstime),
12504 0
12505};
12506static int FAST_FUNC
12507timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12508{
12509 unsigned long clk_tck, s, t;
12510 const unsigned char *p;
12511 struct tms buf;
12512
12513 clk_tck = sysconf(_SC_CLK_TCK);
12514 times(&buf);
12515
12516 p = timescmd_str;
12517 do {
12518 t = *(clock_t *)(((char *) &buf) + p[1]);
12519 s = t / clk_tck;
12520 t = t % clk_tck;
12521 out1fmt("%lum%lu.%03lus%c",
12522 s / 60, s % 60,
12523 (t * 1000) / clk_tck,
12524 p[0]);
12525 p += 2;
12526 } while (*p);
12527
12528 return 0;
12529}
12530
12531#if ENABLE_SH_MATH_SUPPORT
12532
12533
12534
12535
12536
12537
12538static int FAST_FUNC
12539letcmd(int argc UNUSED_PARAM, char **argv)
12540{
12541 arith_t i;
12542
12543 argv++;
12544 if (!*argv)
12545 ash_msg_and_raise_error("expression expected");
12546 do {
12547 i = ash_arith(*argv);
12548 } while (*++argv);
12549
12550 return !i;
12551}
12552#endif
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568static int FAST_FUNC
12569readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12570{
12571 char *opt_n = NULL;
12572 char *opt_p = NULL;
12573 char *opt_t = NULL;
12574 char *opt_u = NULL;
12575 int read_flags = 0;
12576 const char *r;
12577 int i;
12578
12579 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
12580 switch (i) {
12581 case 'p':
12582 opt_p = optionarg;
12583 break;
12584 case 'n':
12585 opt_n = optionarg;
12586 break;
12587 case 's':
12588 read_flags |= BUILTIN_READ_SILENT;
12589 break;
12590 case 't':
12591 opt_t = optionarg;
12592 break;
12593 case 'r':
12594 read_flags |= BUILTIN_READ_RAW;
12595 break;
12596 case 'u':
12597 opt_u = optionarg;
12598 break;
12599 default:
12600 break;
12601 }
12602 }
12603
12604 r = shell_builtin_read(setvar2,
12605 argptr,
12606 bltinlookup("IFS"),
12607 read_flags,
12608 opt_n,
12609 opt_p,
12610 opt_t,
12611 opt_u
12612 );
12613
12614 if ((uintptr_t)r > 1)
12615 ash_msg_and_raise_error(r);
12616
12617 return (uintptr_t)r;
12618}
12619
12620static int FAST_FUNC
12621umaskcmd(int argc UNUSED_PARAM, char **argv)
12622{
12623 static const char permuser[3] ALIGN1 = "ugo";
12624 static const char permmode[3] ALIGN1 = "rwx";
12625 static const short permmask[] ALIGN2 = {
12626 S_IRUSR, S_IWUSR, S_IXUSR,
12627 S_IRGRP, S_IWGRP, S_IXGRP,
12628 S_IROTH, S_IWOTH, S_IXOTH
12629 };
12630
12631
12632
12633 char *ap;
12634 mode_t mask;
12635 int i;
12636 int symbolic_mode = 0;
12637
12638 while (nextopt("S") != '\0') {
12639 symbolic_mode = 1;
12640 }
12641
12642 INT_OFF;
12643 mask = umask(0);
12644 umask(mask);
12645 INT_ON;
12646
12647 ap = *argptr;
12648 if (ap == NULL) {
12649 if (symbolic_mode) {
12650 char buf[18];
12651 char *p = buf;
12652
12653 for (i = 0; i < 3; i++) {
12654 int j;
12655
12656 *p++ = permuser[i];
12657 *p++ = '=';
12658 for (j = 0; j < 3; j++) {
12659 if ((mask & permmask[3 * i + j]) == 0) {
12660 *p++ = permmode[j];
12661 }
12662 }
12663 *p++ = ',';
12664 }
12665 *--p = 0;
12666 puts(buf);
12667 } else {
12668 out1fmt("%.4o\n", mask);
12669 }
12670 } else {
12671 if (isdigit((unsigned char) *ap)) {
12672 mask = 0;
12673 do {
12674 if (*ap >= '8' || *ap < '0')
12675 ash_msg_and_raise_error(msg_illnum, argv[1]);
12676 mask = (mask << 3) + (*ap - '0');
12677 } while (*++ap != '\0');
12678 umask(mask);
12679 } else {
12680 mask = ~mask & 0777;
12681 if (!bb_parse_mode(ap, &mask)) {
12682 ash_msg_and_raise_error("illegal mode: %s", ap);
12683 }
12684 umask(~mask & 0777);
12685 }
12686 }
12687 return 0;
12688}
12689
12690static int FAST_FUNC
12691ulimitcmd(int argc UNUSED_PARAM, char **argv)
12692{
12693 return shell_builtin_ulimit(argv);
12694}
12695
12696
12697
12698
12699
12700
12701static void exitshell(void) NORETURN;
12702static void
12703exitshell(void)
12704{
12705 struct jmploc loc;
12706 char *p;
12707 int status;
12708
12709 status = exitstatus;
12710 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12711 if (setjmp(loc.loc)) {
12712 if (exception_type == EXEXIT)
12713
12714
12715
12716
12717 status = exitstatus;
12718 goto out;
12719 }
12720 exception_handler = &loc;
12721 p = trap[0];
12722 if (p) {
12723 trap[0] = NULL;
12724 evalstring(p, 0);
12725 free(p);
12726 }
12727 flush_stdout_stderr();
12728 out:
12729 setjobctl(0);
12730 _exit(status);
12731
12732}
12733
12734static void
12735init(void)
12736{
12737
12738
12739 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
12740
12741
12742 signal(SIGCHLD, SIG_DFL);
12743
12744
12745
12746 signal(SIGHUP, SIG_DFL);
12747
12748
12749 {
12750 char **envp;
12751 const char *p;
12752 struct stat st1, st2;
12753
12754 initvar();
12755 for (envp = environ; envp && *envp; envp++) {
12756 if (strchr(*envp, '=')) {
12757 setvareq(*envp, VEXPORT|VTEXTFIXED);
12758 }
12759 }
12760
12761 setvar("PPID", utoa(getppid()), 0);
12762
12763 p = lookupvar("PWD");
12764 if (p)
12765 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12766 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12767 p = '\0';
12768 setpwd(p, 0);
12769 }
12770}
12771
12772
12773
12774
12775static void
12776procargs(char **argv)
12777{
12778 int i;
12779 const char *xminusc;
12780 char **xargv;
12781
12782 xargv = argv;
12783 arg0 = xargv[0];
12784
12785 xargv++;
12786 for (i = 0; i < NOPTS; i++)
12787 optlist[i] = 2;
12788 argptr = xargv;
12789 if (options(1)) {
12790
12791 raise_exception(EXERROR);
12792 }
12793 xargv = argptr;
12794 xminusc = minusc;
12795 if (*xargv == NULL) {
12796 if (xminusc)
12797 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12798 sflag = 1;
12799 }
12800 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12801 iflag = 1;
12802 if (mflag == 2)
12803 mflag = iflag;
12804 for (i = 0; i < NOPTS; i++)
12805 if (optlist[i] == 2)
12806 optlist[i] = 0;
12807#if DEBUG == 2
12808 debug = 1;
12809#endif
12810
12811 if (xminusc) {
12812 minusc = *xargv++;
12813 if (*xargv)
12814 goto setarg0;
12815 } else if (!sflag) {
12816 setinputfile(*xargv, 0);
12817 setarg0:
12818 arg0 = *xargv++;
12819 commandname = arg0;
12820 }
12821
12822 shellparam.p = xargv;
12823#if ENABLE_ASH_GETOPTS
12824 shellparam.optind = 1;
12825 shellparam.optoff = -1;
12826#endif
12827
12828 while (*xargv) {
12829 shellparam.nparam++;
12830 xargv++;
12831 }
12832 optschanged();
12833}
12834
12835
12836
12837
12838static void
12839read_profile(const char *name)
12840{
12841 int skip;
12842
12843 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12844 return;
12845 skip = cmdloop(0);
12846 popfile();
12847 if (skip)
12848 exitshell();
12849}
12850
12851
12852
12853
12854
12855static void
12856reset(void)
12857{
12858
12859 evalskip = 0;
12860 loopnest = 0;
12861
12862 g_parsefile->left_in_buffer = 0;
12863 g_parsefile->left_in_line = 0;
12864 popallfiles();
12865
12866 tokpushback = 0;
12867 checkkwd = 0;
12868
12869 clearredir( 0);
12870}
12871
12872#if PROFILE
12873static short profile_buf[16384];
12874extern int etext();
12875#endif
12876
12877
12878
12879
12880
12881
12882
12883
12884int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
12885int ash_main(int argc UNUSED_PARAM, char **argv)
12886{
12887 const char *shinit;
12888 volatile smallint state;
12889 struct jmploc jmploc;
12890 struct stackmark smark;
12891
12892
12893 INIT_G_misc();
12894 INIT_G_memstack();
12895 INIT_G_var();
12896#if ENABLE_ASH_ALIAS
12897 INIT_G_alias();
12898#endif
12899 INIT_G_cmdtable();
12900
12901#if PROFILE
12902 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12903#endif
12904
12905#if ENABLE_FEATURE_EDITING
12906 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12907#endif
12908 state = 0;
12909 if (setjmp(jmploc.loc)) {
12910 smallint e;
12911 smallint s;
12912
12913 reset();
12914
12915 e = exception_type;
12916 if (e == EXERROR)
12917 exitstatus = 2;
12918 s = state;
12919 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12920 exitshell();
12921 if (e == EXINT)
12922 outcslow('\n', stderr);
12923
12924 popstackmark(&smark);
12925 FORCE_INT_ON;
12926 if (s == 1)
12927 goto state1;
12928 if (s == 2)
12929 goto state2;
12930 if (s == 3)
12931 goto state3;
12932 goto state4;
12933 }
12934 exception_handler = &jmploc;
12935#if DEBUG
12936 opentrace();
12937 TRACE(("Shell args: "));
12938 trace_puts_args(argv);
12939#endif
12940 rootpid = getpid();
12941
12942 init();
12943 setstackmark(&smark);
12944 procargs(argv);
12945
12946#if ENABLE_FEATURE_EDITING_SAVEHISTORY
12947 if (iflag) {
12948 const char *hp = lookupvar("HISTFILE");
12949
12950 if (hp == NULL) {
12951 hp = lookupvar("HOME");
12952 if (hp != NULL) {
12953 char *defhp = concat_path_file(hp, ".ash_history");
12954 setvar("HISTFILE", defhp, 0);
12955 free(defhp);
12956 }
12957 }
12958 }
12959#endif
12960 if ( argv[0][0] == '-')
12961 isloginsh = 1;
12962 if (isloginsh) {
12963 state = 1;
12964 read_profile("/etc/profile");
12965 state1:
12966 state = 2;
12967 read_profile(".profile");
12968 }
12969 state2:
12970 state = 3;
12971 if (
12972#ifndef linux
12973 getuid() == geteuid() && getgid() == getegid() &&
12974#endif
12975 iflag
12976 ) {
12977 shinit = lookupvar("ENV");
12978 if (shinit != NULL && *shinit != '\0') {
12979 read_profile(shinit);
12980 }
12981 }
12982 state3:
12983 state = 4;
12984 if (minusc) {
12985
12986
12987
12988
12989
12990
12991
12992 evalstring(minusc, 0);
12993 }
12994
12995 if (sflag || minusc == NULL) {
12996#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
12997 if (iflag) {
12998 const char *hp = lookupvar("HISTFILE");
12999 if (hp)
13000 line_input_state->hist_file = hp;
13001 }
13002#endif
13003 state4:
13004 cmdloop(1);
13005 }
13006#if PROFILE
13007 monitor(0);
13008#endif
13009#ifdef GPROF
13010 {
13011 extern void _mcleanup(void);
13012 _mcleanup();
13013 }
13014#endif
13015 exitshell();
13016
13017}
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
13045
13046
13047
13048
13049
13050
13051