sh1.c revision 302408
1#define Extern extern
2#include <sys/types.h>
3#include <signal.h>
4#define _NSIG NSIG
5#include <errno.h>
6#include <setjmp.h>
7#include "sh.h"
8/* -------- sh.c -------- */
9/*
10 * shell
11 */
12
13/* #include "sh.h" */
14
15int	intr;
16int	inparse;
17char	flags['z'-'a'+1];
18char	*flag = flags-'a';
19char	*elinep = line+sizeof(line)-5;
20char	*null	= "";
21int	heedint =1;
22struct	env	e ={line, iostack, iostack-1,
23		    (xint *)NULL, FDBASE, (struct env *)NULL};
24
25extern	char	**environ;	/* environment pointer */
26
27/*
28 * default shell, search rules
29 */
30char	shellname[] = "/bin/sh";
31char	search[] = ":/bin:/usr/bin";
32
33_PROTOTYPE(void (*qflag), (int)) = SIG_IGN;
34
35_PROTOTYPE(int main, (int argc, char **argv ));
36_PROTOTYPE(int newfile, (char *s ));
37_PROTOTYPE(static char *findeq, (char *cp ));
38_PROTOTYPE(static char *cclass, (char *p, int sub ));
39_PROTOTYPE(void initarea, (void));
40
41int main(argc, argv)
42int argc;
43register char **argv;
44{
45	register int f;
46	register char *s;
47	int cflag;
48	char *name, **ap;
49	int (*iof)();
50
51	initarea();
52	if ((ap = environ) != NULL) {
53		while (*ap)
54			assign(*ap++, !COPYV);
55		for (ap = environ; *ap;)
56			export(lookup(*ap++));
57	}
58	closeall();
59	areanum = 1;
60
61	shell = lookup("SHELL");
62	if (shell->value == null)
63		setval(shell, shellname);
64	export(shell);
65
66	homedir = lookup("HOME");
67	if (homedir->value == null)
68		setval(homedir, "/");
69	export(homedir);
70
71	setval(lookup("$"), itoa(getpid(), 5));
72
73	path = lookup("PATH");
74	if (path->value == null)
75		setval(path, search);
76	export(path);
77
78	ifs = lookup("IFS");
79	if (ifs->value == null)
80		setval(ifs, " \t\n");
81
82	prompt = lookup("PS1");
83	if (prompt->value == null)
84#ifndef UNIXSHELL
85		setval(prompt, "$ ");
86#else
87		setval(prompt, "% ");
88#endif
89
90	if (geteuid() == 0) {
91		setval(prompt, "# ");
92		prompt->status &= ~EXPORT;
93	}
94	cprompt = lookup("PS2");
95	if (cprompt->value == null)
96		setval(cprompt, "> ");
97
98	iof = filechar;
99	cflag = 0;
100	name = *argv++;
101	if (--argc >= 1) {
102		if(argv[0][0] == '-' && argv[0][1] != '\0') {
103			for (s = argv[0]+1; *s; s++)
104				switch (*s) {
105				case 'c':
106					prompt->status &= ~EXPORT;
107					cprompt->status &= ~EXPORT;
108					setval(prompt, "");
109					setval(cprompt, "");
110					cflag = 1;
111					if (--argc > 0)
112						PUSHIO(aword, *++argv, iof = nlchar);
113					break;
114
115				case 'q':
116					qflag = SIG_DFL;
117					break;
118
119				case 's':
120					/* standard input */
121					break;
122
123				case 't':
124					prompt->status &= ~EXPORT;
125					setval(prompt, "");
126					iof = linechar;
127					break;
128
129				case 'i':
130					talking++;
131				default:
132					if (*s>='a' && *s<='z')
133						flag[*s]++;
134				}
135		} else {
136			argv--;
137			argc++;
138		}
139		if (iof == filechar && --argc > 0) {
140			setval(prompt, "");
141			setval(cprompt, "");
142			prompt->status &= ~EXPORT;
143			cprompt->status &= ~EXPORT;
144			if (newfile(name = *++argv))
145				exit(1);
146		}
147	}
148	setdash();
149	if (e.iop < iostack) {
150		PUSHIO(afile, 0, iof);
151		if (isatty(0) && isatty(1) && !cflag)
152			talking++;
153	}
154	signal(SIGQUIT, qflag);
155	if (name && name[0] == '-') {
156		talking++;
157		if ((f = open(".profile", 0)) >= 0)
158			next(remap(f));
159		if ((f = open("/etc/profile", 0)) >= 0)
160			next(remap(f));
161	}
162	if (talking)
163		signal(SIGTERM, sig);
164	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
165		signal(SIGINT, onintr);
166	dolv = argv;
167	dolc = argc;
168	dolv[0] = name;
169	if (dolc > 1)
170		for (ap = ++argv; --argc > 0;)
171			if (assign(*ap = *argv++, !COPYV))
172				dolc--;	/* keyword */
173			else
174				ap++;
175	setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
176
177	for (;;) {
178		if (talking && e.iop <= iostack)
179			prs(prompt->value);
180		onecommand();
181	}
182}
183
184void
185setdash()
186{
187	register char *cp, c;
188	char m['z'-'a'+1];
189
190	cp = m;
191	for (c='a'; c<='z'; c++)
192		if (flag[c])
193			*cp++ = c;
194	*cp = 0;
195	setval(lookup("-"), m);
196}
197
198int
199newfile(s)
200register char *s;
201{
202	register f;
203
204	if (strcmp(s, "-") != 0) {
205		f = open(s, 0);
206		if (f < 0) {
207			prs(s);
208			err(": cannot open");
209			return(1);
210		}
211	} else
212		f = 0;
213	next(remap(f));
214	return(0);
215}
216
217void
218onecommand()
219{
220	register i;
221	jmp_buf m1;
222
223	while (e.oenv)
224		quitenv();
225	areanum = 1;
226	freehere(areanum);
227	freearea(areanum);
228	garbage();
229	wdlist = 0;
230	iolist = 0;
231	e.errpt = 0;
232	e.linep = line;
233	yynerrs = 0;
234	multiline = 0;
235	inparse = 1;
236	intr = 0;
237	execflg = 0;
238	setjmp(failpt = m1);	/* Bruce Evans' fix */
239	if (setjmp(failpt = m1) || yyparse() || intr) {
240		while (e.oenv)
241			quitenv();
242		scraphere();
243		if (!talking && intr)
244			leave();
245		inparse = 0;
246		intr = 0;
247		return;
248	}
249	inparse = 0;
250	brklist = 0;
251	intr = 0;
252	execflg = 0;
253	if (!flag['n'])
254		execute(outtree, NOPIPE, NOPIPE, 0);
255	if (!talking && intr) {
256		execflg = 0;
257		leave();
258	}
259	if ((i = trapset) != 0) {
260		trapset = 0;
261		runtrap(i);
262	}
263}
264
265void
266fail()
267{
268	longjmp(failpt, 1);
269	/* NOTREACHED */
270}
271
272void
273leave()
274{
275	if (execflg)
276		fail();
277	scraphere();
278	freehere(1);
279	runtrap(0);
280	exit(exstat);
281	/* NOTREACHED */
282}
283
284void
285warn(s)
286register char *s;
287{
288	if(*s) {
289		prs(s);
290		exstat = -1;
291	}
292	prs("\n");
293	if (flag['e'])
294		leave();
295}
296
297void
298err(s)
299char *s;
300{
301	warn(s);
302	if (flag['n'])
303		return;
304	if (!talking)
305		leave();
306	if (e.errpt)
307		longjmp(e.errpt, 1);
308	closeall();
309	e.iop = e.iobase = iostack;
310}
311
312int
313newenv(f)
314int f;
315{
316	register struct env *ep;
317
318	if (f) {
319		quitenv();
320		return(1);
321	}
322	ep = (struct env *) space(sizeof(*ep));
323	if (ep == NULL) {
324		while (e.oenv)
325			quitenv();
326		fail();
327	}
328	*ep = e;
329	e.oenv = ep;
330	e.errpt = errpt;
331	return(0);
332}
333
334void
335quitenv()
336{
337	register struct env *ep;
338	register fd;
339
340	if ((ep = e.oenv) != NULL) {
341		fd = e.iofd;
342		e = *ep;
343		/* should close `'d files */
344		DELETE(ep);
345		while (--fd >= e.iofd)
346			close(fd);
347	}
348}
349
350/*
351 * Is any character from s1 in s2?
352 */
353int
354anys(s1, s2)
355register char *s1, *s2;
356{
357	while (*s1)
358		if (any(*s1++, s2))
359			return(1);
360	return(0);
361}
362
363/*
364 * Is character c in s?
365 */
366int
367any(c, s)
368register int c;
369register char *s;
370{
371	while (*s)
372		if (*s++ == c)
373			return(1);
374	return(0);
375}
376
377char *
378putn(n)
379register int n;
380{
381	return(itoa(n, -1));
382}
383
384char *
385itoa(u, n)
386register unsigned u;
387int n;
388{
389	register char *cp;
390	static char s[20];
391	int m;
392
393	m = 0;
394	if (n < 0 && (int) u < 0) {
395		m++;
396		u = -u;
397	}
398	cp = s+sizeof(s);
399	*--cp = 0;
400	do {
401		*--cp = u%10 + '0';
402		u /= 10;
403	} while (--n > 0 || u);
404	if (m)
405		*--cp = '-';
406	return(cp);
407}
408
409void
410next(f)
411int f;
412{
413	PUSHIO(afile, f, filechar);
414}
415
416void
417onintr(s)
418int s;				/* ANSI C requires a parameter */
419{
420	signal(SIGINT, onintr);
421	intr = 1;
422	if (talking) {
423		if (inparse) {
424			prs("\n");
425			fail();
426		}
427	}
428	else if (heedint) {
429		execflg = 0;
430		leave();
431	}
432}
433
434int
435letter(c)
436register c;
437{
438	return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
439}
440
441int
442digit(c)
443register c;
444{
445	return(c >= '0' && c <= '9');
446}
447
448int
449letnum(c)
450register c;
451{
452	return(letter(c) || digit(c));
453}
454
455char *
456space(n)
457int n;
458{
459	register char *cp;
460
461	if ((cp = getcell(n)) == 0)
462		err("out of string space");
463	return(cp);
464}
465
466char *
467strsave(s, a)
468register char *s;
469int a;
470{
471	register char *cp, *xp;
472
473	if ((cp = space(strlen(s)+1)) != NULL) {
474		setarea((char *)cp, a);
475		for (xp = cp; (*xp++ = *s++) != '\0';)
476			;
477		return(cp);
478	}
479	return("");
480}
481
482void
483xfree(s)
484register char *s;
485{
486	DELETE(s);
487}
488
489/*
490 * trap handling
491 */
492void
493sig(i)
494register int i;
495{
496	trapset = i;
497	signal(i, sig);
498}
499
500void runtrap(i)
501int i;
502{
503	char *trapstr;
504
505	if ((trapstr = trap[i]) == NULL)
506		return;
507	if (i == 0)
508		trap[i] = 0;
509	RUN(aword, trapstr, nlchar);
510}
511
512/* -------- var.c -------- */
513/* #include "sh.h" */
514
515/*
516 * Find the given name in the dictionary
517 * and return its value.  If the name was
518 * not previously there, enter it now and
519 * return a null value.
520 */
521struct var *
522lookup(n)
523register char *n;
524{
525	register struct var *vp;
526	register char *cp;
527	register int c;
528	static struct var dummy;
529
530	if (digit(*n)) {
531		dummy.name = n;
532		for (c = 0; digit(*n) && c < 1000; n++)
533			c = c*10 + *n-'0';
534		dummy.status = RONLY;
535		dummy.value = c <= dolc? dolv[c]: null;
536		return(&dummy);
537	}
538	for (vp = vlist; vp; vp = vp->next)
539		if (eqname(vp->name, n))
540			return(vp);
541	cp = findeq(n);
542	vp = (struct var *)space(sizeof(*vp));
543	if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) {
544		dummy.name = dummy.value = "";
545		return(&dummy);
546	}
547	for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++)
548		;
549	if (*cp == 0)
550		*cp = '=';
551	*++cp = 0;
552	setarea((char *)vp, 0);
553	setarea((char *)vp->name, 0);
554	vp->value = null;
555	vp->next = vlist;
556	vp->status = GETCELL;
557	vlist = vp;
558	return(vp);
559}
560
561/*
562 * give variable at `vp' the value `val'.
563 */
564void
565setval(vp, val)
566struct var *vp;
567char *val;
568{
569	nameval(vp, val, (char *)NULL);
570}
571
572/*
573 * if name is not NULL, it must be
574 * a prefix of the space `val',
575 * and end with `='.
576 * this is all so that exporting
577 * values is reasonably painless.
578 */
579void
580nameval(vp, val, name)
581register struct var *vp;
582char *val, *name;
583{
584	register char *cp, *xp;
585	char *nv;
586	int fl;
587
588	if (vp->status & RONLY) {
589		for (xp = vp->name; *xp && *xp != '=';)
590			putc(*xp++);
591		err(" is read-only");
592		return;
593	}
594	fl = 0;
595	if (name == NULL) {
596		xp = space(strlen(vp->name)+strlen(val)+2);
597		if (xp == 0)
598			return;
599		/* make string:  name=value */
600		setarea((char *)xp, 0);
601		name = xp;
602		for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++)
603			;
604		if (*xp++ == 0)
605			xp[-1] = '=';
606		nv = xp;
607		for (cp = val; (*xp++ = *cp++) != '\0';)
608			;
609		val = nv;
610		fl = GETCELL;
611	}
612	if (vp->status & GETCELL)
613		xfree(vp->name);	/* form new string `name=value' */
614	vp->name = name;
615	vp->value = val;
616	vp->status |= fl;
617}
618
619void
620export(vp)
621struct var *vp;
622{
623	vp->status |= EXPORT;
624}
625
626void
627ronly(vp)
628struct var *vp;
629{
630	if (letter(vp->name[0]))	/* not an internal symbol ($# etc) */
631		vp->status |= RONLY;
632}
633
634int
635isassign(s)
636register char *s;
637{
638	if (!letter((int)*s))
639		return(0);
640	for (; *s != '='; s++)
641		if (*s == 0 || !letnum(*s))
642			return(0);
643	return(1);
644}
645
646int
647assign(s, cf)
648register char *s;
649int cf;
650{
651	register char *cp;
652	struct var *vp;
653
654	if (!letter(*s))
655		return(0);
656	for (cp = s; *cp != '='; cp++)
657		if (*cp == 0 || !letnum(*cp))
658			return(0);
659	vp = lookup(s);
660	nameval(vp, ++cp, cf == COPYV? (char *)NULL: s);
661	if (cf != COPYV)
662		vp->status &= ~GETCELL;
663	return(1);
664}
665
666int
667checkname(cp)
668register char *cp;
669{
670	if (!letter(*cp++))
671		return(0);
672	while (*cp)
673		if (!letnum(*cp++))
674			return(0);
675	return(1);
676}
677
678void
679putvlist(f, out)
680register int f, out;
681{
682	register struct var *vp;
683
684	for (vp = vlist; vp; vp = vp->next)
685		if (vp->status & f && letter(*vp->name)) {
686			if (vp->status & EXPORT)
687				write(out, "export ", 7);
688			if (vp->status & RONLY)
689				write(out, "readonly ", 9);
690			write(out, vp->name, (int)(findeq(vp->name) - vp->name));
691			write(out, "\n", 1);
692		}
693}
694
695int
696eqname(n1, n2)
697register char *n1, *n2;
698{
699	for (; *n1 != '=' && *n1 != 0; n1++)
700		if (*n2++ != *n1)
701			return(0);
702	return(*n2 == 0 || *n2 == '=');
703}
704
705static char *
706findeq(cp)
707register char *cp;
708{
709	while (*cp != '\0' && *cp != '=')
710		cp++;
711	return(cp);
712}
713
714/* -------- gmatch.c -------- */
715/*
716 * int gmatch(string, pattern)
717 * char *string, *pattern;
718 *
719 * Match a pattern as in sh(1).
720 */
721
722#define	CMASK	0377
723#define	QUOTE	0200
724#define	QMASK	(CMASK&~QUOTE)
725#define	NOT	'!'	/* might use ^ */
726
727int
728gmatch(s, p)
729register char *s, *p;
730{
731	register int sc, pc;
732
733	if (s == NULL || p == NULL)
734		return(0);
735	while ((pc = *p++ & CMASK) != '\0') {
736		sc = *s++ & QMASK;
737		switch (pc) {
738		case '[':
739			if ((p = cclass(p, sc)) == NULL)
740				return(0);
741			break;
742
743		case '?':
744			if (sc == 0)
745				return(0);
746			break;
747
748		case '*':
749			s--;
750			do {
751				if (*p == '\0' || gmatch(s, p))
752					return(1);
753			} while (*s++ != '\0');
754			return(0);
755
756		default:
757			if (sc != (pc&~QUOTE))
758				return(0);
759		}
760	}
761	return(*s == 0);
762}
763
764static char *
765cclass(p, sub)
766register char *p;
767register int sub;
768{
769	register int c, d, not, found;
770
771	if ((not = *p == NOT) != 0)
772		p++;
773	found = not;
774	do {
775		if (*p == '\0')
776			return((char *)NULL);
777		c = *p & CMASK;
778		if (p[1] == '-' && p[2] != ']') {
779			d = p[2] & CMASK;
780			p++;
781		} else
782			d = c;
783		if (c == sub || (c <= sub && sub <= d))
784			found = !not;
785	} while (*++p != ']');
786	return(found? p+1: (char *)NULL);
787}
788
789/* -------- area.c -------- */
790#define	REGSIZE		sizeof(struct region)
791#define GROWBY		256
792#undef	SHRINKBY	64
793#define FREE 32767
794#define BUSY 0
795#define	ALIGN (sizeof(int)-1)
796
797/* #include "area.h" */
798
799struct region {
800	struct	region *next;
801	int	area;
802};
803
804/*
805 * All memory between (char *)areabot and (char *)(areatop+1) is
806 * exclusively administered by the area management routines.
807 * It is assumed that sbrk() and brk() manipulate the high end.
808 */
809static	struct region *areabot;		/* bottom of area */
810static	struct region *areatop;		/* top of area */
811static	struct region *areanxt;		/* starting point of scan */
812
813void
814initarea()
815{
816	while ((int)sbrk(0) & ALIGN)
817		sbrk(1);
818	areabot = (struct region *)sbrk(REGSIZE);
819	areabot->next = areabot;
820	areabot->area = BUSY;
821	areatop = areabot;
822	areanxt = areabot;
823}
824
825char *
826getcell(nbytes)
827unsigned nbytes;
828{
829	register int nregio;
830	register struct region *p, *q;
831	register i;
832
833	if (nbytes == 0)
834		abort();	/* silly and defeats the algorithm */
835	/*
836	 * round upwards and add administration area
837	 */
838	nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1;
839	for (p = areanxt;;) {
840		if (p->area > areanum) {
841			/*
842			 * merge free cells
843			 */
844			while ((q = p->next)->area > areanum && q != areanxt)
845				p->next = q->next;
846			/*
847			 * exit loop if cell big enough
848			 */
849			if (q >= p + nregio)
850				goto found;
851		}
852		p = p->next;
853		if (p == areanxt)
854			break;
855	}
856	i = nregio >= GROWBY ? nregio : GROWBY;
857	p = (struct region *)sbrk(i * REGSIZE);
858	if (p == (struct region *)-1)
859		return((char *)NULL);
860	p--;
861	if (p != areatop)
862		abort();	/* allocated areas are contiguous */
863	q = p + i;
864	p->next = q;
865	p->area = FREE;
866	q->next = areabot;
867	q->area = BUSY;
868	areatop = q;
869found:
870	/*
871	 * we found a FREE area big enough, pointed to by 'p', and up to 'q'
872	 */
873	areanxt = p + nregio;
874	if (areanxt < q) {
875		/*
876		 * split into requested area and rest
877		 */
878		if (areanxt+1 > q)
879			abort();	/* insufficient space left for admin */
880		areanxt->next = q;
881		areanxt->area = FREE;
882		p->next = areanxt;
883	}
884	p->area = areanum;
885	return((char *)(p+1));
886}
887
888void
889freecell(cp)
890char *cp;
891{
892	register struct region *p;
893
894	if ((p = (struct region *)cp) != NULL) {
895		p--;
896		if (p < areanxt)
897			areanxt = p;
898		p->area = FREE;
899	}
900}
901
902void
903freearea(a)
904register int a;
905{
906	register struct region *p, *top;
907
908	top = areatop;
909	for (p = areabot; p != top; p = p->next)
910		if (p->area >= a)
911			p->area = FREE;
912}
913
914void
915setarea(cp,a)
916char *cp;
917int a;
918{
919	register struct region *p;
920
921	if ((p = (struct region *)cp) != NULL)
922		(p-1)->area = a;
923}
924
925int
926getarea(cp)
927char *cp;
928{
929	return ((struct region*)cp-1)->area;
930}
931
932void
933garbage()
934{
935	register struct region *p, *q, *top;
936
937	top = areatop;
938	for (p = areabot; p != top; p = p->next) {
939		if (p->area > areanum) {
940			while ((q = p->next)->area > areanum)
941				p->next = q->next;
942			areanxt = p;
943		}
944	}
945#ifdef SHRINKBY
946	if (areatop >= q + SHRINKBY && q->area > areanum) {
947		brk((char *)(q+1));
948		q->next = areabot;
949		q->area = BUSY;
950		areatop = q;
951	}
952#endif
953}
954