/*
 * grep - Global Regular Expression search and Print
 *
 * works with non DEC clib and VAX11C
 */

#include "stdio.h"
#include "clib2.h"

#define	LMAX	128
#define PMAX	128

#define z       if(0==1)
#define	END	0
#define CHAR	1
#define BOL	2
#define EOL	3
#define	ANY	4
#define CLASS	5
#define	NCLASS	6
#define CLOSURE	7

static int	cflag = 0 ;
static int	fflag = 0 ;
static int	nflag = 0 ;
static int      tflag = 0 ;
static int	vflag = 0 ;
static int      eflag = 0 ;
static char	*pp = 0 ;

static  char     lbuf[LMAX] = 0 ;
static  char     pbuf[PMAX] = 0 ;
static  stderr = 0;
static  stdin  = 0;
function zmain(argc, argv) int     argc; char    *argv[];
{
    register char *p;
    register int c, i;
    int nf, gp;
    char tfile[120];
    FILE *f,*ioptr;
    stderr = valof_stderr();    /* NOTE this is different from standard i/o */
    stdin = valof_stdin();
    nf = argc-1;
    gp = 0;
    for(i=1; i<argc; ++i)
        {
        
        p = argv[i];
        if(*p == '-')
            {
            
            ++p;
            while(c = *p++)
            switch(lower(c))
                {
                
                case 't':
                ++tflag;
                break;
                
                case 'c':
                ++cflag;
                break;
                
		case 'e':
                ++eflag;
                break;
                
                case 'f':
                ++fflag;
                break;
                
                case 'n':
                ++nflag;
                break;
                
                case 'v':
                ++vflag;
                break;
                
                default:
                usage();
                
                }
            
            argv[i] = 0;
            --nf;
            
            }
        else if(!gp)
            {
            
            compile(p);
            argv[i] = 0;
            ++gp;
            --nf;
            
            }
        
        }
	if (!gp) {
		usage();
		exit(1);
	}
	if(nf == 0)
		grep(stdin, 0);
	else
		for(i=1; i<argc; ++i)
			if(p = argv[i])
				{
        
if ((ioptr = search(p))== NULL)
	exit (4);
else
	while ((p = nextf(ioptr)) != NULL) {
	      reseg(tfile,p,";.*$",1);
	      if (!eflag && (fflag || tflag)  && !isatty(valof_stdout())) then
		eprintf("%s\n",tfile);
	      fi
		if ((f=fopen(p,"r")) == NULL) {
			fprintf (stderr, "grep: can't open %s\n", p);
			continue;
		}
		grep (f,tfile);
		fclose (f);
	}
        
}
    
}

function static grep(fp, fn)
FILE *fp;
char *fn;
{
    register int lno, count, m;
    char *oldfn,tbuf[120];

    oldfn = fn;
    if (tflag) then
      reseg(tbuf,oldfn,"^.*\\]",1);
    fi
    lno = 0;
    count = 0;
    while(eric(lbuf, fp))
        {
        
        ++lno;
        m = match();
        if((m && !vflag) || (!m && vflag))
            {
            
            ++count;
            if(!cflag)
                {
                
                if(fflag && fn)
                    {
                    
                    file(fn);
                    fn = 0;
                    
                    }
                
		if(nflag || tflag) then
		   if (tflag) then
		       printf("%s%15T %5d%20T : ",tbuf , lno);
		   elsif (nflag) then
                       printf("%d\t", lno);
		   fi
		fi
                printf("%s\n", lbuf);
                
                }
            
            
            }
        
        }
    if(cflag && count > 0) then
        
        if(fflag && fn) then
           file(fn);
	fi
	printf("%d\n", count);
    fi
}

function static compile(s)
register char *s;
    {
    register char *lp;
    register int c;
    int o;
    char *spp, *cclass();
    
    pp = pbuf;
    while(c = *s++)
        {
        
        /*
        * Closure is special.
        */
        if(c == '*')
            {
            
            if(pp==pbuf || (o=pp[-1])==BOL || o==EOL || o==CLOSURE)
            badpat();
            store(END);
            store(END);
            spp = pp;
            while(--pp > lp)
            *pp = pp[-1];
            *pp = CLOSURE;
            pp = spp;
            continue;
            
            }
        
        /*
        * All the rest.
        */
        lp = pp;
        switch(c)
            {
            
            
            case '^':
            store(BOL);
            break;
            
            case '$':
            store(EOL);
            break;
            
            case '.':
            store(ANY);
            break;
            
            case '[':
            s = cclass(s);
            break;
            
            case '\\':
            if(*s)
            c = *s++;
            
            default:
            store(CHAR);
            store(lower(c));
            
            }
        
        }
    store(END);
    }


function static char *cclass(s)
register char *s;
    {
    register char *cp;
    register int c;
    int o;
    
    o = CLASS;
    if(*s == '^')
        {
        
        ++s;
        o = NCLASS;
        }
    store(o);
    cp = pp;
    store(0);  /* Byte count */
    while((c = *s++) && c!=']')
        {
        
        if(c == '\\')
        if((c = *s++) == '\0')
        badpat();
        store(lower(c));
        if(++*cp == 0) {
	    fprintf (stderr, "grep: class too complex\n");
	    exit (1);
	}
        }
    if(c != ']')
    badpat();
    return(s);
    }

function static store(op)
    {
    if(pp >= &pbuf[PMAX]) {
	fprintf (stderr, "grep: pattern too complex\n");
	exit (1);
    }
    *pp++ = op;
    }

function static match()
    {
    register char *l;
    char *pmatch();
    
    l = lbuf;
    while(*l)
        {
        
        if(pmatch(l, pbuf))
        return(1);
        ++l;
        }
    return(0);
    }


function static char *pmatch(l, p)
register char *l, *p;
    {
    register char *e;
    int op, c, n;
    char *are;
    
    while((op = *p++) != END)
    switch(op)
        {
        
        
        case CHAR:
        if(lower(*l++) != *p++)
        return(0);
        break;
        
        case BOL:
        if(l != lbuf)
        return(0);
        break;
        
        case EOL:
        if(*l != '\0')
        return(0);
        break;
        
        case ANY:
        if(*l++ == '\0')
        return(0);
        break;
        
        case CLASS:
        c = lower(*l++);
        if((n = *p++&0377) == 0)
        return(0);
        do
            {
            
            if(c == *p++)
            break;
            
            }
        while(--n);
        if(n == 0)
        return(0);
        p += n-1;
        break;
        
        case NCLASS:
        c = lower(*l++);
        if((n = *p++&0377) == 0)
        break;
        do
            {
            
            if(c == *p++)
            break;
            
            }
        while(--n);
        if(n)
        return(0);
        break;
        
        case CLOSURE:
        are = l;
        while(e = pmatch(l, p))
        l = e;
        while(*p++ != END)
        ;
        while(l >= are)
            {
            
            if(e = pmatch(l, p))
            return(e);
            --l;
            
            }
        
        return(0);
        
        default:
		fprintf (stderr, "grep: internal error - match\n");
		exit (1);
        
        }
    
    return(l);
    }

lower (c)
register int c;
{
    if(c>='A' && c<='Z')
    c += 'a'-'A';
    return(c);
}

badpat ()
{
	fprintf (stderr, "grep: bad pattern\n");
	exit (1);
}

function static file(s)
char *s;
    {
    printf("File %s\n", s);
    }

cant (s)
char	*s;
{
	fprintf (stderr, "grep: cannot open %s\n", s);
	exit (1);
}

usage ()
{
	fprintf (stderr, "usage: grep [-cfnvet] pattern [file...]\n");
        fprintf (stderr,
	"c:count f:filename n:number v:inverse e:nostderr t:together\n");
	exit (1);
}

function static eric(xbuf,fp)
char *xbuf;
FILE *fp;
{
    int i,j;

    i = fgets(xbuf,LMAX,fp);
    j = strlen(xbuf);
    xbuf[j-1] = '\0';
    return (i);
}
