/*
 *	UNIX shell
 *
 *	S. R. Bourne
 *	Bell Telephone Laboratories
 *
 */

#include	"defs.h"
#include	"types.h"
#include	"stat.h"
#include	"dir.h"

#define NULL 0


/* globals (file name generation)
 *
 * "*" in params matches r.e ".*"
 * "?" in params matches r.e. "."
 * "[...]" in params matches character class
 * "[...a-z...]" in params matches a through z.
 *
 */

PROC VOID	addg();


INT	expand(as,rflg)
	STRING		as;
{
	INT		count;
	int		dirf;
	BOOL		dir;
	STRING		rescan;
	REG STRING	s, cs;
	ARGPTR		schain;
	struct direct	*dp;
        REG BOOL slash;
	REG STRING rs;
	REG ARGPTR	rchain;
        REG CHAR	c;
	STATBUF		statb;
	struct direct	*readdir();

	dir=0;
	rescan = 0;
	schain = gchain;
	IF trapnote&SIGSET THEN return(0); FI

	s=cs=as;

	/* check for meta chars */
	BEGIN
	   slash=0;
	   WHILE !fngchar(*cs)
	   DO	IF *cs++==0
		THEN	IF rflg ANDF slash THEN break; ELSE return(0) FI
		ELIF *cs=='/'
		THEN	slash++;
		FI
	   OD
	END

	LOOP	IF cs==s
		THEN	s=nullstr;
			break;
		ELIF *--cs == '/'
		THEN	*cs=0;
			IF s==cs THEN s="/" FI
			break;
		FI
	POOL
	IF stat(*s?s:".", &statb)>=0
	    ANDF (statb.st_mode&S_IFMT)==S_IFDIR
	    ANDF (dirf=open(*s?s:".", 0)) >= 0
	THEN	dir++;
	FI
	count=0;
	IF *cs==0 THEN *cs++=0200 FI
	IF dir
	THEN	/* check for rescan */
		rs=cs;

		REP	IF *rs=='/' THEN rescan=rs; *rs=0; gchain=0 FI
		PER	*rs++ DONE

		IF setjmp(INTbuf) == 0 THEN trapjmp[INTR] = 1; FI
		WHILE (trapnote&SIGSET) == 0 ANDF (dp = readdir(dirf)) != NULL
		DO	IF (*dp->d_name=='.' ANDF *cs!='.')
			THEN	continue;
			FI
			IF gmatch(dp->d_name, cs)
			THEN	addg(s,dp->d_name,rescan); count++;
			FI
		OD
		close(dirf); trapjmp[INTR] = 0;

		IF rescan
		THEN
			rchain=gchain; gchain=schain;
			IF count
			THEN	count=0;
				WHILE rchain
				DO	count += expand(rchain->argval,1);
					rchain=rchain->argnxt;
				OD
			FI
			*rescan='/';
		FI
	FI

	BEGIN
	   s=as;
	   WHILE c = *s
	   DO	*s++=(c&STRIP?c:'/') OD
	END
	return(count);
}

gmatch(s, p)
	REG STRING	s, p;
{
	REG INT		scc;
	CHAR		c;
	BOOL ok; INT lc;

	IF scc = *s++
	THEN	IF (scc &= STRIP)==0
		THEN	scc=0200;
		FI
	FI
	SWITCH c = *p++ IN

	    case '[':
		{
		ok=0; lc=077777;
		WHILE c = *p++
		DO	IF c==']'
			THEN	return(ok?gmatch(s,p):0);
			ELIF c==MINUS
			THEN	IF lc<=scc ANDF scc<=(*p++) THEN ok++ FI
			ELSE	IF scc==(lc=(c&STRIP)) THEN ok++ FI
			FI
		OD
		return(0);
		}

	    default:
		IF (c&STRIP)!=scc THEN return(0) FI

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

	    case '*':
		IF *p==0 THEN return(1) FI
		--s;
		WHILE *s
		DO  IF gmatch(s++,p) THEN return(1) FI OD
		return(0);

	    case 0:
		return(scc==0);
	ENDSW
}

LOCAL VOID	addg(as1,as2,as3)
	STRING		as1, as2, as3;
{
	REG STRING	s1, s2;
	REG INT		c;

	s2 = locstak()+BYTESPERWORD;

	s1=as1;
	WHILE c = *s1++
	DO	IF (c &= STRIP)==0
		THEN	*s2++='/';
			break;
		FI
		*s2++=c;
	OD
	s1=as2;
	WHILE *s2 = *s1++ DO s2++ OD
	IF s1=as3
	THEN	*s2++='/';
		WHILE *s2++ = *++s1 DONE
	FI
	makearg(endstak(s2));
}

makearg(args)
	REG STRING	args;
{
	args->argnxt=gchain;
	gchain=args;
}

struct direct *
readdir(d)
int d;
{
    static struct direct dir;

    do
    {
	if (read(d,&dir,sizeof(dir)) != sizeof(dir))
	    return(NULL);
    } while (dir.d_ino == 0);
    return(&dir);
}
