953 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			953 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| #define Extern extern
 | |
| #include <sys/types.h>
 | |
| #include <signal.h>
 | |
| #include <errno.h>
 | |
| #include <setjmp.h>
 | |
| #include "sh.h"
 | |
| /* -------- sh.c -------- */
 | |
| /*
 | |
|  * shell
 | |
|  */
 | |
| 
 | |
| /* #include "sh.h" */
 | |
| 
 | |
| int	intr;
 | |
| int	inparse;
 | |
| char	flags['z'-'a'+1];
 | |
| char	*flag = flags-'a';
 | |
| char	*elinep = line+sizeof(line)-5;
 | |
| char	*null	= "";
 | |
| int	heedint =1;
 | |
| struct	env	e ={line, iostack, iostack-1,
 | |
| 		    (xint *)NULL, FDBASE, (struct env *)NULL};
 | |
| 
 | |
| extern	char	**environ;	/* environment pointer */
 | |
| 
 | |
| /*
 | |
|  * default shell, search rules
 | |
|  */
 | |
| char	shellname[] = "/bin/sh";
 | |
| char	search[] = ":/bin:/usr/bin";
 | |
| 
 | |
| _PROTOTYPE(void (*qflag), (int)) = SIG_IGN;
 | |
| 
 | |
| _PROTOTYPE(int main, (int argc, char **argv ));
 | |
| _PROTOTYPE(int newfile, (char *s ));
 | |
| _PROTOTYPE(static char *findeq, (char *cp ));
 | |
| _PROTOTYPE(static char *cclass, (char *p, int sub ));
 | |
| _PROTOTYPE(void initarea, (void));
 | |
| 
 | |
| int main(argc, argv)
 | |
| int argc;
 | |
| register char **argv;
 | |
| {
 | |
| 	register int f;
 | |
| 	register char *s;
 | |
| 	int cflag;
 | |
| 	char *name, **ap;
 | |
| 	int (*iof)();
 | |
| 
 | |
| 	initarea();
 | |
| 	if ((ap = environ) != NULL) {
 | |
| 		while (*ap)
 | |
| 			assign(*ap++, !COPYV);
 | |
| 		for (ap = environ; *ap;)
 | |
| 			export(lookup(*ap++));
 | |
| 	}
 | |
| 	closeall();
 | |
| 	areanum = 1;
 | |
| 
 | |
| 	shell = lookup("SHELL");
 | |
| 	if (shell->value == null)
 | |
| 		setval(shell, shellname);
 | |
| 	export(shell);
 | |
| 
 | |
| 	homedir = lookup("HOME");
 | |
| 	if (homedir->value == null)
 | |
| 		setval(homedir, "/");
 | |
| 	export(homedir);
 | |
| 
 | |
| 	setval(lookup("$"), itoa(getpid(), 5));
 | |
| 
 | |
| 	path = lookup("PATH");
 | |
| 	if (path->value == null)
 | |
| 		setval(path, search);
 | |
| 	export(path);
 | |
| 
 | |
| 	ifs = lookup("IFS");
 | |
| 	if (ifs->value == null)
 | |
| 		setval(ifs, " \t\n");
 | |
| 
 | |
| 	prompt = lookup("PS1");
 | |
| 	if (prompt->value == null)
 | |
| #ifndef UNIXSHELL
 | |
| 		setval(prompt, "$ ");
 | |
| #else
 | |
| 		setval(prompt, "% ");
 | |
| #endif
 | |
| 
 | |
| 	if (geteuid() == 0) {
 | |
| 		setval(prompt, "# ");
 | |
| 		prompt->status &= ~EXPORT;
 | |
| 	}
 | |
| 	cprompt = lookup("PS2");
 | |
| 	if (cprompt->value == null)
 | |
| 		setval(cprompt, "> ");
 | |
| 
 | |
| 	iof = filechar;
 | |
| 	cflag = 0;
 | |
| 	name = *argv++;
 | |
| 	if (--argc >= 1) {
 | |
| 		if(argv[0][0] == '-' && argv[0][1] != '\0') {
 | |
| 			for (s = argv[0]+1; *s; s++)
 | |
| 				switch (*s) {
 | |
| 				case 'c':
 | |
| 					prompt->status &= ~EXPORT;
 | |
| 					cprompt->status &= ~EXPORT;
 | |
| 					setval(prompt, "");
 | |
| 					setval(cprompt, "");
 | |
| 					cflag = 1;
 | |
| 					if (--argc > 0)
 | |
| 						PUSHIO(aword, *++argv, iof = nlchar);
 | |
| 					break;
 | |
| 	
 | |
| 				case 'q':
 | |
| 					qflag = SIG_DFL;
 | |
| 					break;
 | |
| 
 | |
| 				case 's':
 | |
| 					/* standard input */
 | |
| 					break;
 | |
| 
 | |
| 				case 't':
 | |
| 					prompt->status &= ~EXPORT;
 | |
| 					setval(prompt, "");
 | |
| 					iof = linechar;
 | |
| 					break;
 | |
| 	
 | |
| 				case 'i':
 | |
| 					talking++;
 | |
| 				default:
 | |
| 					if (*s>='a' && *s<='z')
 | |
| 						flag[*s]++;
 | |
| 				}
 | |
| 		} else {
 | |
| 			argv--;
 | |
| 			argc++;
 | |
| 		}
 | |
| 		if (iof == filechar && --argc > 0) {
 | |
| 			setval(prompt, "");
 | |
| 			setval(cprompt, "");
 | |
| 			prompt->status &= ~EXPORT;
 | |
| 			cprompt->status &= ~EXPORT;
 | |
| 			if (newfile(name = *++argv))
 | |
| 				exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	setdash();
 | |
| 	if (e.iop < iostack) {
 | |
| 		PUSHIO(afile, 0, iof);
 | |
| 		if (isatty(0) && isatty(1) && !cflag)
 | |
| 			talking++;
 | |
| 	}
 | |
| 	signal(SIGQUIT, qflag);
 | |
| 	if (name && name[0] == '-') {
 | |
| 		talking++;
 | |
| 		if ((f = open(".profile", 0)) >= 0)
 | |
| 			next(remap(f));
 | |
| 		if ((f = open("/etc/profile", 0)) >= 0)
 | |
| 			next(remap(f));
 | |
| 	}
 | |
| 	if (talking)
 | |
| 		signal(SIGTERM, sig);
 | |
| 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
 | |
| 		signal(SIGINT, onintr);
 | |
| 	dolv = argv;
 | |
| 	dolc = argc;
 | |
| 	dolv[0] = name;
 | |
| 	if (dolc > 1)
 | |
| 		for (ap = ++argv; --argc > 0;)
 | |
| 			if (assign(*ap = *argv++, !COPYV))
 | |
| 				dolc--;	/* keyword */
 | |
| 			else
 | |
| 				ap++;
 | |
| 	setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
 | |
| 
 | |
| 	for (;;) {
 | |
| 		if (talking && e.iop <= iostack)
 | |
| 			prs(prompt->value);
 | |
| 		onecommand();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| setdash()
 | |
| {
 | |
| 	register char *cp, c;
 | |
| 	char m['z'-'a'+1];
 | |
| 
 | |
| 	cp = m;
 | |
| 	for (c='a'; c<='z'; c++)
 | |
| 		if (flag[c])
 | |
| 			*cp++ = c;
 | |
| 	*cp = 0;
 | |
| 	setval(lookup("-"), m);
 | |
| }
 | |
| 
 | |
| int
 | |
| newfile(s)
 | |
| register char *s;
 | |
| {
 | |
| 	register f;
 | |
| 
 | |
| 	if (strcmp(s, "-") != 0) {
 | |
| 		f = open(s, 0);
 | |
| 		if (f < 0) {
 | |
| 			prs(s);
 | |
| 			err(": cannot open");
 | |
| 			return(1);
 | |
| 		}
 | |
| 	} else
 | |
| 		f = 0;
 | |
| 	next(remap(f));
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| void
 | |
| onecommand()
 | |
| {
 | |
| 	register i;
 | |
| 	jmp_buf m1;
 | |
| 
 | |
| 	while (e.oenv)
 | |
| 		quitenv();
 | |
| 	areanum = 1;
 | |
| 	freehere(areanum);
 | |
| 	freearea(areanum);
 | |
| 	garbage();
 | |
| 	wdlist = 0;
 | |
| 	iolist = 0;
 | |
| 	e.errpt = 0;
 | |
| 	e.linep = line;
 | |
| 	yynerrs = 0;
 | |
| 	multiline = 0;
 | |
| 	inparse = 1;
 | |
| 	intr = 0;
 | |
| 	execflg = 0;
 | |
| 	setjmp(failpt = m1);	/* Bruce Evans' fix */
 | |
| 	if (setjmp(failpt = m1) || yyparse() || intr) {
 | |
| 		while (e.oenv)
 | |
| 			quitenv();
 | |
| 		scraphere();
 | |
| 		if (!talking && intr)
 | |
| 			leave();
 | |
| 		inparse = 0;
 | |
| 		intr = 0;
 | |
| 		return;
 | |
| 	}
 | |
| 	inparse = 0;
 | |
| 	brklist = 0;
 | |
| 	intr = 0;
 | |
| 	execflg = 0;
 | |
| 	if (!flag['n'])
 | |
| 		execute(outtree, NOPIPE, NOPIPE, 0);
 | |
| 	if (!talking && intr) {
 | |
| 		execflg = 0;
 | |
| 		leave();
 | |
| 	}
 | |
| 	if ((i = trapset) != 0) {
 | |
| 		trapset = 0;
 | |
| 		runtrap(i);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| fail()
 | |
| {
 | |
| 	longjmp(failpt, 1);
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| void
 | |
| leave()
 | |
| {
 | |
| 	if (execflg)
 | |
| 		fail();
 | |
| 	scraphere();
 | |
| 	freehere(1);
 | |
| 	runtrap(0);
 | |
| 	exit(exstat);
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| void
 | |
| warn(s)
 | |
| register char *s;
 | |
| {
 | |
| 	if(*s) {
 | |
| 		prs(s);
 | |
| 		exstat = -1;
 | |
| 	}
 | |
| 	prs("\n");
 | |
| 	if (flag['e'])
 | |
| 		leave();
 | |
| }
 | |
| 
 | |
| void
 | |
| err(s)
 | |
| char *s;
 | |
| {
 | |
| 	warn(s);
 | |
| 	if (flag['n'])
 | |
| 		return;
 | |
| 	if (!talking)
 | |
| 		leave();
 | |
| 	if (e.errpt)
 | |
| 		longjmp(e.errpt, 1);
 | |
| 	closeall();
 | |
| 	e.iop = e.iobase = iostack;
 | |
| }
 | |
| 
 | |
| int
 | |
| newenv(f)
 | |
| int f;
 | |
| {
 | |
| 	register struct env *ep;
 | |
| 
 | |
| 	if (f) {
 | |
| 		quitenv();
 | |
| 		return(1);
 | |
| 	}
 | |
| 	ep = (struct env *) space(sizeof(*ep));
 | |
| 	if (ep == NULL) {
 | |
| 		while (e.oenv)
 | |
| 			quitenv();
 | |
| 		fail();
 | |
| 	}
 | |
| 	*ep = e;
 | |
| 	e.oenv = ep;
 | |
| 	e.errpt = errpt;
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| void
 | |
| quitenv()
 | |
| {
 | |
| 	register struct env *ep;
 | |
| 	register fd;
 | |
| 
 | |
| 	if ((ep = e.oenv) != NULL) {
 | |
| 		fd = e.iofd;
 | |
| 		e = *ep;
 | |
| 		/* should close `'d files */
 | |
| 		DELETE(ep);
 | |
| 		while (--fd >= e.iofd)
 | |
| 			close(fd);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Is any character from s1 in s2?
 | |
|  */
 | |
| int
 | |
| anys(s1, s2)
 | |
| register char *s1, *s2;
 | |
| {
 | |
| 	while (*s1)
 | |
| 		if (any(*s1++, s2))
 | |
| 			return(1);
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Is character c in s?
 | |
|  */
 | |
| int
 | |
| any(c, s)
 | |
| register int c;
 | |
| register char *s;
 | |
| {
 | |
| 	while (*s)
 | |
| 		if (*s++ == c)
 | |
| 			return(1);
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| char *
 | |
| putn(n)
 | |
| register int n;
 | |
| {
 | |
| 	return(itoa(n, -1));
 | |
| }
 | |
| 
 | |
| char *
 | |
| itoa(u, n)
 | |
| register unsigned u;
 | |
| int n;
 | |
| {
 | |
| 	register char *cp;
 | |
| 	static char s[20];
 | |
| 	int m;
 | |
| 
 | |
| 	m = 0;
 | |
| 	if (n < 0 && (int) u < 0) {
 | |
| 		m++;
 | |
| 		u = -u;
 | |
| 	}
 | |
| 	cp = s+sizeof(s);
 | |
| 	*--cp = 0;
 | |
| 	do {
 | |
| 		*--cp = u%10 + '0';
 | |
| 		u /= 10;
 | |
| 	} while (--n > 0 || u);
 | |
| 	if (m)
 | |
| 		*--cp = '-';
 | |
| 	return(cp);
 | |
| }
 | |
| 
 | |
| void
 | |
| next(f)
 | |
| int f;
 | |
| {
 | |
| 	PUSHIO(afile, f, filechar);
 | |
| }
 | |
| 
 | |
| void
 | |
| onintr(s)
 | |
| int s;				/* ANSI C requires a parameter */
 | |
| {
 | |
| 	signal(SIGINT, onintr);
 | |
| 	intr = 1;
 | |
| 	if (talking) {
 | |
| 		if (inparse) {
 | |
| 			prs("\n");
 | |
| 			fail();
 | |
| 		}
 | |
| 	}
 | |
| 	else if (heedint) {
 | |
| 		execflg = 0;
 | |
| 		leave();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int
 | |
| letter(c)
 | |
| register c;
 | |
| {
 | |
| 	return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
 | |
| }
 | |
| 
 | |
| int
 | |
| digit(c)
 | |
| register c;
 | |
| {
 | |
| 	return(c >= '0' && c <= '9');
 | |
| }
 | |
| 
 | |
| int
 | |
| letnum(c)
 | |
| register c;
 | |
| {
 | |
| 	return(letter(c) || digit(c));
 | |
| }
 | |
| 
 | |
| char *
 | |
| space(n)
 | |
| int n;
 | |
| {
 | |
| 	register char *cp;
 | |
| 
 | |
| 	if ((cp = getcell(n)) == 0)
 | |
| 		err("out of string space");
 | |
| 	return(cp);
 | |
| }
 | |
| 
 | |
| char *
 | |
| strsave(s, a)
 | |
| register char *s;
 | |
| int a;
 | |
| {
 | |
| 	register char *cp, *xp;
 | |
| 
 | |
| 	if ((cp = space(strlen(s)+1)) != NULL) {
 | |
| 		setarea((char *)cp, a);
 | |
| 		for (xp = cp; (*xp++ = *s++) != '\0';)
 | |
| 			;
 | |
| 		return(cp);
 | |
| 	}
 | |
| 	return("");
 | |
| }
 | |
| 
 | |
| void
 | |
| xfree(s)
 | |
| register char *s;
 | |
| {
 | |
| 	DELETE(s);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * trap handling
 | |
|  */
 | |
| void
 | |
| sig(i)
 | |
| register int i;
 | |
| {
 | |
| 	trapset = i;
 | |
| 	signal(i, sig);
 | |
| }
 | |
| 
 | |
| void runtrap(i)
 | |
| int i;
 | |
| {
 | |
| 	char *trapstr;
 | |
| 
 | |
| 	if ((trapstr = trap[i]) == NULL)
 | |
| 		return;
 | |
| 	if (i == 0)
 | |
| 		trap[i] = 0;
 | |
| 	RUN(aword, trapstr, nlchar);
 | |
| }
 | |
| 
 | |
| /* -------- var.c -------- */
 | |
| /* #include "sh.h" */
 | |
| 
 | |
| /*
 | |
|  * Find the given name in the dictionary
 | |
|  * and return its value.  If the name was
 | |
|  * not previously there, enter it now and
 | |
|  * return a null value.
 | |
|  */
 | |
| struct var *
 | |
| lookup(n)
 | |
| register char *n;
 | |
| {
 | |
| 	register struct var *vp;
 | |
| 	register char *cp;
 | |
| 	register int c;
 | |
| 	static struct var dummy;
 | |
| 
 | |
| 	if (digit(*n)) {
 | |
| 		dummy.name = n;
 | |
| 		for (c = 0; digit(*n) && c < 1000; n++)
 | |
| 			c = c*10 + *n-'0';
 | |
| 		dummy.status = RONLY;
 | |
| 		dummy.value = c <= dolc? dolv[c]: null;
 | |
| 		return(&dummy);
 | |
| 	}
 | |
| 	for (vp = vlist; vp; vp = vp->next)
 | |
| 		if (eqname(vp->name, n))
 | |
| 			return(vp);
 | |
| 	cp = findeq(n);
 | |
| 	vp = (struct var *)space(sizeof(*vp));
 | |
| 	if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) {
 | |
| 		dummy.name = dummy.value = "";
 | |
| 		return(&dummy);
 | |
| 	}
 | |
| 	for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++)
 | |
| 		;
 | |
| 	if (*cp == 0)
 | |
| 		*cp = '=';
 | |
| 	*++cp = 0;
 | |
| 	setarea((char *)vp, 0);
 | |
| 	setarea((char *)vp->name, 0);
 | |
| 	vp->value = null;
 | |
| 	vp->next = vlist;
 | |
| 	vp->status = GETCELL;
 | |
| 	vlist = vp;
 | |
| 	return(vp);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * give variable at `vp' the value `val'.
 | |
|  */
 | |
| void
 | |
| setval(vp, val)
 | |
| struct var *vp;
 | |
| char *val;
 | |
| {
 | |
| 	nameval(vp, val, (char *)NULL);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * if name is not NULL, it must be
 | |
|  * a prefix of the space `val',
 | |
|  * and end with `='.
 | |
|  * this is all so that exporting
 | |
|  * values is reasonably painless.
 | |
|  */
 | |
| void
 | |
| nameval(vp, val, name)
 | |
| register struct var *vp;
 | |
| char *val, *name;
 | |
| {
 | |
| 	register char *cp, *xp;
 | |
| 	char *nv;
 | |
| 	int fl;
 | |
| 
 | |
| 	if (vp->status & RONLY) {
 | |
| 		for (xp = vp->name; *xp && *xp != '=';)
 | |
| 			putc(*xp++);
 | |
| 		err(" is read-only");
 | |
| 		return;
 | |
| 	}
 | |
| 	fl = 0;
 | |
| 	if (name == NULL) {
 | |
| 		xp = space(strlen(vp->name)+strlen(val)+2);
 | |
| 		if (xp == 0)
 | |
| 			return;
 | |
| 		/* make string:  name=value */
 | |
| 		setarea((char *)xp, 0);
 | |
| 		name = xp;
 | |
| 		for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++)
 | |
| 			;
 | |
| 		if (*xp++ == 0)
 | |
| 			xp[-1] = '=';
 | |
| 		nv = xp;
 | |
| 		for (cp = val; (*xp++ = *cp++) != '\0';)
 | |
| 			;
 | |
| 		val = nv;
 | |
| 		fl = GETCELL;
 | |
| 	}
 | |
| 	if (vp->status & GETCELL)
 | |
| 		xfree(vp->name);	/* form new string `name=value' */
 | |
| 	vp->name = name;
 | |
| 	vp->value = val;
 | |
| 	vp->status |= fl;
 | |
| }
 | |
| 
 | |
| void
 | |
| export(vp)
 | |
| struct var *vp;
 | |
| {
 | |
| 	vp->status |= EXPORT;
 | |
| }
 | |
| 
 | |
| void
 | |
| ronly(vp)
 | |
| struct var *vp;
 | |
| {
 | |
| 	if (letter(vp->name[0]))	/* not an internal symbol ($# etc) */
 | |
| 		vp->status |= RONLY;
 | |
| }
 | |
| 
 | |
| int
 | |
| isassign(s)
 | |
| register char *s;
 | |
| {
 | |
| 	if (!letter((int)*s))
 | |
| 		return(0);
 | |
| 	for (; *s != '='; s++)
 | |
| 		if (*s == 0 || !letnum(*s))
 | |
| 			return(0);
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| assign(s, cf)
 | |
| register char *s;
 | |
| int cf;
 | |
| {
 | |
| 	register char *cp;
 | |
| 	struct var *vp;
 | |
| 
 | |
| 	if (!letter(*s))
 | |
| 		return(0);
 | |
| 	for (cp = s; *cp != '='; cp++)
 | |
| 		if (*cp == 0 || !letnum(*cp))
 | |
| 			return(0);
 | |
| 	vp = lookup(s);
 | |
| 	nameval(vp, ++cp, cf == COPYV? (char *)NULL: s);
 | |
| 	if (cf != COPYV)
 | |
| 		vp->status &= ~GETCELL;
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| checkname(cp)
 | |
| register char *cp;
 | |
| {
 | |
| 	if (!letter(*cp++))
 | |
| 		return(0);
 | |
| 	while (*cp)
 | |
| 		if (!letnum(*cp++))
 | |
| 			return(0);
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| void
 | |
| putvlist(f, out)
 | |
| register int f, out;
 | |
| {
 | |
| 	register struct var *vp;
 | |
| 
 | |
| 	for (vp = vlist; vp; vp = vp->next)
 | |
| 		if (vp->status & f && letter(*vp->name)) {
 | |
| 			if (vp->status & EXPORT)
 | |
| 				write(out, "export ", 7);
 | |
| 			if (vp->status & RONLY)
 | |
| 				write(out, "readonly ", 9);
 | |
| 			write(out, vp->name, (int)(findeq(vp->name) - vp->name));
 | |
| 			write(out, "\n", 1);
 | |
| 		}
 | |
| }
 | |
| 
 | |
| int
 | |
| eqname(n1, n2)
 | |
| register char *n1, *n2;
 | |
| {
 | |
| 	for (; *n1 != '=' && *n1 != 0; n1++)
 | |
| 		if (*n2++ != *n1)
 | |
| 			return(0);
 | |
| 	return(*n2 == 0 || *n2 == '=');
 | |
| }
 | |
| 
 | |
| static char *
 | |
| findeq(cp)
 | |
| register char *cp;
 | |
| {
 | |
| 	while (*cp != '\0' && *cp != '=')
 | |
| 		cp++;
 | |
| 	return(cp);
 | |
| }
 | |
| 
 | |
| /* -------- gmatch.c -------- */
 | |
| /*
 | |
|  * int gmatch(string, pattern)
 | |
|  * char *string, *pattern;
 | |
|  *
 | |
|  * Match a pattern as in sh(1).
 | |
|  */
 | |
| 
 | |
| #define	CMASK	0377
 | |
| #define	QUOTE	0200
 | |
| #define	QMASK	(CMASK&~QUOTE)
 | |
| #define	NOT	'!'	/* might use ^ */
 | |
| 
 | |
| int
 | |
| gmatch(s, p)
 | |
| register char *s, *p;
 | |
| {
 | |
| 	register int sc, pc;
 | |
| 
 | |
| 	if (s == NULL || p == NULL)
 | |
| 		return(0);
 | |
| 	while ((pc = *p++ & CMASK) != '\0') {
 | |
| 		sc = *s++ & QMASK;
 | |
| 		switch (pc) {
 | |
| 		case '[':
 | |
| 			if ((p = cclass(p, sc)) == NULL)
 | |
| 				return(0);
 | |
| 			break;
 | |
| 
 | |
| 		case '?':
 | |
| 			if (sc == 0)
 | |
| 				return(0);
 | |
| 			break;
 | |
| 
 | |
| 		case '*':
 | |
| 			s--;
 | |
| 			do {
 | |
| 				if (*p == '\0' || gmatch(s, p))
 | |
| 					return(1);
 | |
| 			} while (*s++ != '\0');
 | |
| 			return(0);
 | |
| 
 | |
| 		default:
 | |
| 			if (sc != (pc&~QUOTE))
 | |
| 				return(0);
 | |
| 		}
 | |
| 	}
 | |
| 	return(*s == 0);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| cclass(p, sub)
 | |
| register char *p;
 | |
| register int sub;
 | |
| {
 | |
| 	register int c, d, not, found;
 | |
| 
 | |
| 	if ((not = *p == NOT) != 0)
 | |
| 		p++;
 | |
| 	found = not;
 | |
| 	do {
 | |
| 		if (*p == '\0')
 | |
| 			return((char *)NULL);
 | |
| 		c = *p & CMASK;
 | |
| 		if (p[1] == '-' && p[2] != ']') {
 | |
| 			d = p[2] & CMASK;
 | |
| 			p++;
 | |
| 		} else
 | |
| 			d = c;
 | |
| 		if (c == sub || (c <= sub && sub <= d))
 | |
| 			found = !not;
 | |
| 	} while (*++p != ']');
 | |
| 	return(found? p+1: (char *)NULL);
 | |
| }
 | |
| 
 | |
| /* -------- area.c -------- */
 | |
| #define	REGSIZE		sizeof(struct region)
 | |
| #define GROWBY		256
 | |
| #undef	SHRINKBY	64
 | |
| #define FREE 32767
 | |
| #define BUSY 0
 | |
| #define	ALIGN (sizeof(int)-1)
 | |
| 
 | |
| /* #include "area.h" */
 | |
| 
 | |
| struct region {
 | |
| 	struct	region *next;
 | |
| 	int	area;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * All memory between (char *)areabot and (char *)(areatop+1) is
 | |
|  * exclusively administered by the area management routines.
 | |
|  * It is assumed that sbrk() and brk() manipulate the high end.
 | |
|  */
 | |
| static	struct region *areabot;		/* bottom of area */
 | |
| static	struct region *areatop;		/* top of area */
 | |
| static	struct region *areanxt;		/* starting point of scan */
 | |
| 
 | |
| void
 | |
| initarea()
 | |
| {
 | |
| 	while ((int)sbrk(0) & ALIGN)
 | |
| 		sbrk(1);
 | |
| 	areabot = (struct region *)sbrk(REGSIZE);
 | |
| 	areabot->next = areabot;
 | |
| 	areabot->area = BUSY;
 | |
| 	areatop = areabot;
 | |
| 	areanxt = areabot;
 | |
| }
 | |
| 
 | |
| char *
 | |
| getcell(nbytes)
 | |
| unsigned nbytes;
 | |
| {
 | |
| 	register int nregio;
 | |
| 	register struct region *p, *q;
 | |
| 	register i;
 | |
| 
 | |
| 	if (nbytes == 0)
 | |
| 		abort();	/* silly and defeats the algorithm */
 | |
| 	/*
 | |
| 	 * round upwards and add administration area
 | |
| 	 */
 | |
| 	nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1;
 | |
| 	for (p = areanxt;;) {
 | |
| 		if (p->area > areanum) {
 | |
| 			/*
 | |
| 			 * merge free cells
 | |
| 			 */
 | |
| 			while ((q = p->next)->area > areanum && q != areanxt)
 | |
| 				p->next = q->next;
 | |
| 			/*
 | |
| 			 * exit loop if cell big enough
 | |
| 			 */
 | |
| 			if (q >= p + nregio)
 | |
| 				goto found;
 | |
| 		}
 | |
| 		p = p->next;
 | |
| 		if (p == areanxt)
 | |
| 			break;
 | |
| 	}
 | |
| 	i = nregio >= GROWBY ? nregio : GROWBY;
 | |
| 	p = (struct region *)sbrk(i * REGSIZE);
 | |
| 	if (p == (struct region *)-1)
 | |
| 		return((char *)NULL);
 | |
| 	p--;
 | |
| 	if (p != areatop)
 | |
| 		abort();	/* allocated areas are contiguous */
 | |
| 	q = p + i;
 | |
| 	p->next = q;
 | |
| 	p->area = FREE;
 | |
| 	q->next = areabot;
 | |
| 	q->area = BUSY;
 | |
| 	areatop = q;
 | |
| found:
 | |
| 	/*
 | |
| 	 * we found a FREE area big enough, pointed to by 'p', and up to 'q'
 | |
| 	 */
 | |
| 	areanxt = p + nregio;
 | |
| 	if (areanxt < q) {
 | |
| 		/*
 | |
| 		 * split into requested area and rest
 | |
| 		 */
 | |
| 		if (areanxt+1 > q)
 | |
| 			abort();	/* insufficient space left for admin */
 | |
| 		areanxt->next = q;
 | |
| 		areanxt->area = FREE;
 | |
| 		p->next = areanxt;
 | |
| 	}
 | |
| 	p->area = areanum;
 | |
| 	return((char *)(p+1));
 | |
| }
 | |
| 
 | |
| void
 | |
| freecell(cp)
 | |
| char *cp;
 | |
| {
 | |
| 	register struct region *p;
 | |
| 
 | |
| 	if ((p = (struct region *)cp) != NULL) {
 | |
| 		p--;
 | |
| 		if (p < areanxt)
 | |
| 			areanxt = p;
 | |
| 		p->area = FREE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| freearea(a)
 | |
| register int a;
 | |
| {
 | |
| 	register struct region *p, *top;
 | |
| 
 | |
| 	top = areatop;
 | |
| 	for (p = areabot; p != top; p = p->next)
 | |
| 		if (p->area >= a)
 | |
| 			p->area = FREE;
 | |
| }
 | |
| 
 | |
| void
 | |
| setarea(cp,a)
 | |
| char *cp;
 | |
| int a;
 | |
| {
 | |
| 	register struct region *p;
 | |
| 
 | |
| 	if ((p = (struct region *)cp) != NULL)
 | |
| 		(p-1)->area = a;
 | |
| }
 | |
| 
 | |
| int
 | |
| getarea(cp)
 | |
| char *cp;
 | |
| {
 | |
| 	return ((struct region*)cp-1)->area;
 | |
| }
 | |
| 
 | |
| void
 | |
| garbage()
 | |
| {
 | |
| 	register struct region *p, *q, *top;
 | |
| 
 | |
| 	top = areatop;
 | |
| 	for (p = areabot; p != top; p = p->next) {
 | |
| 		if (p->area > areanum) {
 | |
| 			while ((q = p->next)->area > areanum)
 | |
| 				p->next = q->next;
 | |
| 			areanxt = p;
 | |
| 		}
 | |
| 	}
 | |
| #ifdef SHRINKBY
 | |
| 	if (areatop >= q + SHRINKBY && q->area > areanum) {
 | |
| 		brk((char *)(q+1));
 | |
| 		q->next = areabot;
 | |
| 		q->area = BUSY;
 | |
| 		areatop = q;
 | |
| 	}
 | |
| #endif
 | |
| }
 | 
