#
/* global command --

   glob params

   "*" in params matches r.e ".*"
   "?" in params matches r.e. "."
   "[...]" in params matches character class
   "[...a-z...]" in params matches a through z.

   perform command with argument list
  constructed as follows:
     if param does not contain "*", "[", or "?", use it as is
     if it does, find all files in current directory
     which match the param, sort them, and use them

   prepend the command name with "/bin" or "/usr/bin"
   as required.
*/

#define	E2BIG	7
#define	ENOEXEC	8
#define	ENOENT	2

#define	STRSIZ	5300
char	ab[STRSIZ];		/* generated characters */
char	*ava[500];		/* generated arguments */
char	**av &ava[1];
char	*string ab;
int	errno;
int	ncoll;

main(argc, argv)
char *argv[];
{
	register char *cp;
	register acctflg;

	if (argc < 3) {
		write(2, "Arg count\n", 10);
		return;
	}
	argv++;
	*av++ = *argv;
	while (--argc >= 2)
		expand(*++argv);
	if (ncoll==0) {
		write(2, "No match\n", 9);
		return;
	}
	acctflg = shexec(ava[1], &ava[1]);
	cp = cat("/usr/bin/", ava[1]);
	acctflg =+ shexec(cp+4, &ava[1]);
	acctflg =+ shexec(cp, &ava[1]);
	err(acctflg? ":permission denied!": ":not found");
	write(2, "Command not found.\n", 19);
}

expand(as)
char *as;
{
	register char *s, *cs;
	register int dirf;
	char **oav;
	static struct {
		int	ino;
		char	name[16];
	} entry;

	s = cs = as;
	while (*cs!='*' && *cs!='?' && *cs!='[') {
		if (*cs++ == 0) {
			*av++ = cat(s, "");
			return;
		}
	}
	for (;;) {
		if (cs==s) {
			dirf = open(".", 0);
			s = "";
			break;
		}
		if (*--cs == '/') {
			*cs = 0;
			dirf = open(s==cs? "/": s, 0);
			*cs++ = 0200;
			break;
		}
	}
	if (dirf<0) {
		write(2, "No directory\n", 13);
		exit(8);
	}
	oav = av;
	while (read(dirf, &entry, 16) == 16) {
		if (entry.ino==0)
			continue;
		if (match(entry.name, cs)) {
			*av++ = cat(s, entry.name);
			ncoll++;
		}
	}
	close(dirf);
	sort(oav);
}

sort(oav)
char **oav;
{
	register char **p1, **p2, **c;

	p1 = oav;
	while (p1 < av-1) {
		p2 = p1;
		while(++p2 < av) {
			if (compar(*p1, *p2) > 0) {
				c = *p1;
				*p1 = *p2;
				*p2 = c;
			}
		}
		p1++;
	}
}


err(s) {
	prs(s);
	prs("\n");
	exit(8);
}
toolong()
{
	err("Arg list too long");
}

match(s, p)
char *s, *p;
{
	if (*s=='.' && *p!='.')
		return(0);
	return(amatch(s, p));
}

amatch(as, ap)
char *as, *ap;
{
	register char *s, *p;
	register scc;
	int c, cc, ok, lc;

	s = as;
	p = ap;
	if (scc = *s++)
		if ((scc =& 0177) == 0)
			scc = 0200;
	switch (c = *p++) {

	case '[':
		ok = 0;
		lc = 077777;
		while (cc = *p++) {
			if (cc==']') {
				if (ok)
					return(amatch(s, p));
				else
					return(0);
			} else if (cc=='-') {
				if (lc<=scc && scc<=(c = *p++))
					ok++;
			} else
				if (scc == (lc=cc))
					ok++;
		}
		return(0);

	default:
		if (c!=scc)
			return(0);

	case '?':
		if (scc)
			return(amatch(s, p));
		return(0);

	case '*':
		return(umatch(--s, p));

	case '\0':
		return(!scc);
	}
}

umatch(s, p)
char *s, *p;
{
	if(*p==0)
		return(1);
	while(*s)
		if (amatch(s++,p))
			return(1);
	return(0);
}

compar(as1, as2)
char *as1, *as2;
{
	register char *s1, *s2;

	s1 = as1;
	s2 = as2;
	while (*s1++ ==  *s2)
		if (*s2++ == 0)
			return(0);
	return (*--s1 - *s2);
}

cat(as1, as2)
char *as1, *as2;
{
	register char *s1, *s2;
	register int c;

	s2 = string;
	s1 = as1;
	while (c = *s1++) {
		if (s2 > &ab[STRSIZ])
			toolong();
		c =& 0177;
		if (c==0) {
			*s2++ = '/';
			break;
		}
		*s2++ = c;
	}
	s1 = as2;
	do {
		if (s2 > &ab[STRSIZ])
			toolong();
		*s2++ = c = *s1++;
	} while (c);
	s1 = string;
	string = s2;
	return(s1);
}
#define	ENOENT	2
#define	E2BIG	7
#define	ENOEXEC	8
#define	ENOMEM	12
#define	EACCESS	13
#define	ENOTDIR	20
#define	EDIRECT	13
#define	SHELL	"/bin/sh"

int	ldivr;
char	nbuf[6];
shexec(afile, aargp)
	char *afile, *aargp[];
	{
	register char *file, **rargp;
	extern errno;

	file = afile;
	rargp = aargp;
	execv(file, rargp);
/* temp */	if(!errno || errno & 0177700) errno = EDIRECT;
	if(errno == ENOEXEC) {
		*rargp = file;
		*--rargp = SHELL+5;
		execv(SHELL, rargp);
		if(errno == ENOENT || errno == ENOTDIR)
			err("No Shell!");
	}
	switch (errno) {

	case E2BIG:
		prs(*rargp);
		err(": arg list too long");

	case ENOMEM:
		prs(*rargp);
		err(": not enough core");

	default:
		prs(*rargp);
		prs(": unknown error (errno=");
		prn(errno);
		err(").");

	case EACCESS:
		return(1);

	case ENOENT:
	case ENOTDIR:
		return(0);
	}
}

prs(as)
char *as;
{
	register char *s;

	s = as;
	while(*s)
		write(2, s++, 1);
}

prn(n) {
	prs(btoa(n, nbuf+5));
}

btoa(n, ptr)
	char *ptr;
	{
	register char *p;
	register v;

	p = ptr;
	*p = 0;
	v = n;
	do {
		v = ldiv(0, v, 10);
		*--p = ldivr | '0';
	} while(v);
	return(p);
}
