[net.sources] tele.c - Yet another GREP program

dan@rna.UUCP (Dan Ts'o) (08/08/84)

Hi,
	Well I hacked "tele" a bit further (sorry, I should have waited).
Below is a Bourne shell archive of "tele" and a manual page for it. The
GREP-like interface "g" now supports most of the grep options and besides
context provides another feature that I've always wanted in grep, multiple
patterns. For example, ever grep through the kernel looking for several
different defines or identifiers ?

	g -c3 -e nami -e iput iget sys/*.c

will print plus and minus three lines around every hit of nami, iput or iget.


					Cheers,
					Dan Ts'o
					Dept. Neurobiology
					Rockefeller Univ.
					1230 York Ave.
					NY, NY 10021
					212-570-7671
					...cmcl2!rna!dan

echo /usr/man/manl/tele.l
sed 's/^X//' > /usr/man/manl/tele.l << 'All work and no play makes Jack a dull boy'
X.TH TELE 1 8/6/84
X.SH NAME
Xtele,g  \-  find telephone numbers/alternative grep command
X.SH SYNOPSIS
X.B tele
X[
X.B \-abcfghlnpstvCFN
X]\ \ names or patterns ...
X.br
X.B g
X[
X.B \-abcfghlnpstvCFN
X]\ \ pattern\ \ [
Xfilename ...
X]
X.SH DESCRIPTION
XThis commands has two modes of function, \fItele\fP and \fIg\fP, as
Xset by the name of invocation.
X.I Tele
Xfinds lines contain a match to given names or patterns in a group of
Xfiles and prints the match lines on the standard output. Searches are
Xusually case folded in this mode.
XThe following files are searched in the given order:
X.sp
X.nf
X.in +1i
X$PHONE
X$HOME/phone
X$HOME/Phone
X/usr/pub/phone
X/usr/pub/phone.local
X/usr/pub/phone.misc
X.in -1i
X.fi
X.sp
XThe
X.I g
Xentry accepts a \fIgrep\fP-like command syntax and only searches among the
Xgroup of files given on the command line, or the standard input.
XBoth modes accept the regular expression operators ^ . $ which match the
Xbeginning of a line, any character, and the end of a line, respectively.
XNo other regular expression operators are recognized.
XThe options are given below.
X.TP
X.BI \-c [#lines]
XPrints a number of lines surrounding a match, providing a context of the
Xmatch. If a number of lines of context is not given, one line before and one
Xline after a match are printed.
X.TP
X.BI \-a [#lines]
X.TP
X.BI \-b [#lines]
X.br
XSimilar to
X.B \-c
Xexcept that specified number of lines are printed only after or before the
Xmatching line, respectively.
X.TP
X.B \-p
XModifies the number of context lines printed after a match (as specified
Xby
X.B \-c
Xand
X.B \-a
X) to extend to the next empty line. This is useful for printing to the end
Xof a paragraph or text section.
X.TP
X.BI \-h [#hits]
XOnly print up to the given number of matches. If no number is given,
Xthe command terminates after printing one matching line.
X.TP
X.B \-l
XOnly print filenames that contain matches, one filename per line.
X.TP
X.BI \-e pattern
XAdd the given pattern to the list of patterns to be searched for. Only
Xfunctional with \fIg\fP mode.
X.TP
X.B \-v
XMatch lines which do not contain the pattern instead of lines that do
Xcontain the pattern.
X.TP
X.B \-n
XPrint the line numbers of matching lines as well.
X.TP
X.B \-N
XPrint the filename of matching lines as well. This is the default behavior
Xfor \fIg\fP mode with multiple input files.
X.TP
X.B \-C
XPrint only the count of matches.
X.TP
X.B \-s
XSuppress error messages about unopenable files.
X.TP
X.B \-f
XFold upper and lower case when performing matches. This is the default
Xbehavior in \fItele\fP mode.
X.TP
X.B \-F
XDo not fold cases. This is the default behavior in \fIg\fP mode.
X.TP
X.B \-g
XAssume \fIg\fP mode.
X.TP
X.B \-t
XAssume \fItele\fP mode.
X.PP
XWith multiple patterns, lines are printed if any pattern matches
X(or with \fI-v\fP, if any pattern does not match). To achieve the
Xother possible interpretation, \fIi.e.\fP print lines which contain all
X(or do not contain all) patterns, multiple instances of \fIgrep\fP or
X\fIg\fP can be cascaded in a pipeline.
X.SH FILES
X.ta 2i
X/usr/pub/phone	Public phone directory
X.br
X/usr/pub/phone.local	Public local phone directory
X.br
X/usr/pub/phone.misc	Public miscellaneous phone directory
X.br
X$HOME/phone	User's phone directory
X.br
X$HOME/Phone
X.br
X$PHONE	Settable phone directory
X.br
X.SH SEE ALSO
Xgrep(1), egrep(1), fgrep(1)
All work and no play makes Jack a dull boy
echo /usr/src/local/cmd/tele.c
sed 's/^X//' > /usr/src/local/cmd/tele.c << 'All work and no play makes Jack a dull boy'
X/*
X * tele - Search for telephone numbers and the like
X * g - Alternative GREP program with context and multiple patterns
X *	Daniel Ts'o, Rockefeller Univ.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X
X#define	PHONE1LIST "/phone"
X#define	PHONE2LIST "/Phone"
X#define	PHONEPUB "/usr/pub/phone"
X#define	PHONELOCAL "/usr/pub/phone.local"
X#define	PHONEMISC "/usr/pub/phone.misc"
X#define	LINESIZE 512
X#define	MAXPATTERNS 100
X
X#define	CARAT	(0200+'^')
X#define	DOT	(0200+'.')
X#define	DOLLAR	(0200+'$')
X#define	SPECIAL	0200
X
Xint aflag = 0;
Xint bflag = 0;
Xint cflag = 0;
Xint sflag = 0;
Xint tflag = 1;
Xint vflag = 0;
Xint pflag = 0;
Xint Cflag = 0;
Xint lflag = 0;
Xlong hflag = 0;
Xlong hits = 0;
Xint fileflag = 0;
Xint numberflag = 0;
Xlong lineno = 0;
Xint fold = 1;
Xchar **cv;
Xchar *ealloc();
Xchar *nodename;
Xchar *getenv();
Xlong getnum();
X
Xmain(c, v)
Xchar **v;
X{
X	register char *s;
X	register int f, f1;
X	int f2;
X	char pname[LINESIZE];
X	char obuf[BUFSIZ];
X	char *logdir();
X	char **files;
X	char *PHONE;
X	char **pp;
X	char *patterns[MAXPATTERNS+2];
X
X	/* Act like grep */
X	nodename = v[0];
X	if (*nodename == 'g')
X		fold = tflag = 0;
X	PHONE = getenv("PHONE");
X	if (*PHONE == 0)
X		PHONE = NULL;
X	pp = patterns;
X	while (c > 1 && v[1][0] == '-' && v[1][1]) {
X		c--;
X		v++;
X		s = *v;
X		for (;;) {
X			if (*++s == 0)
X				break;
X			switch(*s) {
X			/* After match */
X			case 'a':
X				aflag++;
X			/* Before match */
X			case 'b':
X			case 'c':
X				if (*s == 'b')
X					bflag++;
X				cflag = getnum(s);
X				break;
X			case 'h':
X				hflag = getnum(s);
X				break;
X			case 'g':
X				fold = tflag = 0;
X				break;
X			case 'l':
X				lflag++;
X				break;
X			case 'C':
X				Cflag++;
X				break;
X			case 'p':
X				pflag++;
X				break;
X			case 's':
X				sflag++;
X				break;
X			case 't':
X				fold = tflag = 1;
X				break;
X			case 'F':
X				fold = 0;
X				break;
X			case 'f':
X				fold = 1;
X				break;
X			case 'n':
X				numberflag = 1;
X				break;
X			case 'N':
X				fileflag = 1;
X				break;
X			case 'v':
X				vflag = 1;
X				break;
X			case 'e':
X				if (pp >= &patterns[MAXPATTERNS]) {
X					fprintf(stderr,
X						"%s: Too many patterns\n",
X						nodename);
X					exit(-1);
X				}
X				if (s[1]) {
X					*pp++ = s + 1;
X					*s = 0;
X					s--;
X					break;
X				}
X				else if (c > 1) {
X					c--;
X					v++;
X					*pp++ = *v;
X					break;
X				}	
X			default:
X				fprintf(stderr, "%s: %s: Bad option\n", *v,
X					nodename);
X				exit(-1);
X			}
X		}
X	}
X	if (c < 2) {
X		if (tflag)
X			fprintf(stderr, "Usage: %s names_and_patterns\n",
X				nodename);
X		else
X			fprintf(stderr, "Usage: %s pattern [files]\n",
X				nodename);
X		exit(-1);
X	}
X
X	v[c] = 0;
X	c--;
X	if (!tflag) {
X		files = v + 2;
X		*pp++ = v[1];
X		*pp = 0;
X		v = patterns;
X		if (c > 2)
X			fileflag++;
X		c = pp - patterns;
X	}
X	else
X		v++;
X	while (c-- > 0)
X		special(v[c]);
X	/* Set-up array of line buffers for context */
X	if (cflag) {
X		cv = (char **) ealloc(cflag*(sizeof (char *)));
X		for (f = 0; f < cflag; f++)
X			cv[f] = (char *)ealloc(LINESIZE);
X	}
X
X	setbuf(stdout, obuf);
X	if (tflag) {
X		strcpy(pname, logdir());
X		strcat(pname, PHONE1LIST);
X		if (PHONE != NULL) {
X			f = f1 = phone(v, PHONE);
X			f2 = phone(v, pname);
X			if (f2 >= 0) {
X				f += f2;
X				f1 = 0;
X			}
X		}
X		else
X			f = f1 = phone(v, pname);
X		strcpy(pname, logdir());
X		strcat(pname, PHONE2LIST);
X		f2 = phone(v, pname);
X		if (f2 >= 0) {
X			f += f2;
X			f1 = 0;
X		}
X		f2 = phone(v, PHONELOCAL);
X		if (f2 >= 0) {
X			f += f2;
X			f1 = 0;
X		}
X		f2 = phone(v, PHONEMISC);
X		if (f2 >= 0) {
X			f += f2;
X			f1 = 0;
X		}
X		f2 = phone(v, PHONEPUB);
X		if (f2 >= 0) {
X			f += f2;
X			f1 = 0;
X		}
X		if (f1 && !sflag)
X			fprintf(stderr, "No phone list\n");
X	}
X	else if (*files == 0)
X		f = phone(v, 0);
X	else {
X		f = 0;
X		while (*files)
X			f += phone(v, *files++);
X	}
X	if (Cflag)
X		printf("%ld\n", hits);
X	exit(f > 0 ? 0 : -1);
X}
X
X/*
X * Do case-folding of pattern and map SPECIALS
X *	SPECIALS have 0200 bit set. Conscious decision - don't strip 0200 off
X *	pattern (maybe someone will want it)
X */
Xspecial(pat)
Xregister char *pat;
X{
X	register char *s;
X
X	s = pat;
X	while (*pat) {
X		if (fold)
X			if (*pat >= 'A' && *pat <= 'Z')
X				*pat += 'a' - 'A';
X		switch (*pat) {
X		case '\\':
X			if (pat[1])
X				pat++;
X			*s++ = *pat++;
X			break;
X		case '^':
X			*s++ = CARAT;
X			pat++;
X			break;
X		case '.':
X			*s++ = DOT;
X			pat++;
X			break;
X		case '$':
X			*s++ = DOLLAR;
X			pat++;
X			break;
X		default:
X			*s++ = *pat++;
X		}
X	}
X	*s = 0;
X}
X
Xlong getnum(s)
Xregister char *s;
X{
X	long i;
X
X	s++;
X	if ('0' <= *s && *s <= '9') {
X		i = atol(s);
X		*s = 0;
X		if (i <= 0)
X			i = 1;
X	}
X	else
X		i = 1;
X	return i;
X}
X
Xphone(v, fname)
Xchar **v;
Xchar *fname;
X{
X	register char **vv, *s, *t;
X	register int i;
X	int f;
X	long ln;
X	char buf[LINESIZE], sbuf[LINESIZE];
X	FILE *pd;
X	int ccnt, cindex, nomatch;
X
X	f = 0;
X	if (fname && strcmp("-", fname)) {
X		pd = fopen(fname, "r");
X		if (pd == NULL) {
X			if (!tflag && !sflag)
X				fprintf(stderr, "%s: %s: Cannot open\n",
X					nodename, fname);
X			return -1;
X		}
X	}
X	else
X		pd = stdin;
X	cindex = 0;
X	ccnt = 0;
X	lineno = 0;
X	if (cflag)
X		for (i = 0; i < cflag; *cv[i++] = 0);
X	while (fgets(buf, LINESIZE, pd) != NULL) {
X		for (i = 0, s = buf, t = sbuf; *t++ = *s; i++, s++)
X			if (fold)
X				if (*s >= 'A' && *s <= 'Z')
X					*s = *s - 'A' + 'a';
X		lineno++;
X		nomatch = 1;
X		for (vv = v; *vv; vv++)
X			if (match(*vv, buf, i) ^ vflag) {
X				if (lflag) {
X					printf("%s\n",
X						(fname ? fname : "(stdin)"));
X					fflush(stdout);
X					if (pd != stdin)
X						fclose(pd);
X					return 1;
X				}
X				f++;
X				hits++;
X				nomatch = 0;
X				if (cflag) {
X					ccnt = cindex;
X					ln = lineno - cflag;
X					if (!aflag) do {
X						output(fname, ln++, cv[cindex]);
X						*cv[cindex] = 0;
X						if (++cindex >= cflag)
X							cindex = 0;
X					} while (cindex != ccnt);
X					ccnt = cflag + 1;
X				}
X				output(fname, lineno, sbuf);
X				if (tflag)
X					fflush(stdout);
X				break;
X			}
X		if (cflag) {
X			if (nomatch && ccnt && !bflag) {
X				output(fname, lineno, sbuf);
X				if (tflag)
X					fflush(stdout);
X				*cv[cindex] = 0;
X			}
X			else if (!nomatch)
X				*cv[cindex] = 0;
X			else {
X				t = cv[cindex];
X				s = sbuf;
X				while (*t++ = *s++);
X			}
X			if (++cindex >= cflag)
X				cindex = 0;
X		}
X		if (ccnt) {
X			if (pflag && ccnt == 1 && *sbuf != '\n')
X				ccnt = 2;
X			ccnt--;
X		}
X		if (hflag && ccnt == 0 && hits >= hflag) {
X			fflush(stdout);
X			exit(0);
X		}
X	}
X	if (pd != stdin)
X		fclose(pd);
X	fflush(stdout);
X	return f;
X}
X
Xoutput(fname, ln, s)
Xchar *fname;
Xlong ln;
Xchar *s;
X{
X	if (s == 0 || *s == 0)
X		return;
X	if (Cflag)
X		return;
X	if (fileflag && fname)
X		printf("%s:", fname);
X	if (numberflag)
X		printf("%ld:", ln);
X	if (fputs(s, stdout) == EOF) {
X		fprintf(stderr, "%s: Output error\n", nodename);
X		exit(-1);
X	}
X}
X
Xmatch(a, b, m)
Xchar *a, *b;
Xregister int m;
X{
X	register char *s, *t;
X	int n;
X	int carat;
X
X	if ((*a&0377) == CARAT) {
X		a++;
X		if (*a == 0)
X			return 1;
X		carat = 1;
X	}
X	else
X		carat = 0;
X	n = 0;
X	s = a;
X	if (*s == 0)
X		return 0;
X	while (*s++)
X		n++;
X	for (;;) {
X		s = a;
X		t = b;
X		if ((*s&0377) != DOT)
X			for (;;) {
X				if (*t == 0)
X					break;
X				if (*t++ == *s) {
X					t--;
X					break;
X				}
X				m--;
X			}
X		if (carat && t != b)
X			break;
X		b = t;
X		if (n > m)
X			break;
X		for (;;) {
X			if (*t == '\n') {
X				if ((*s&0377) == DOLLAR)
X					return 1;
X				break;
X			}
X			if ((*s&0377) == DOT) {
X				s++;
X				t++;
X			}
X			else if (*s++ != *t++)
X				break;
X			if (*s == 0)
X				return 1;
X		}
X		if (carat)
X			break;
X		m--;
X		b++;
X	}
X	return 0;
X}
X
Xchar *logdir()
X{
X	char *getenv();
X	struct passwd *getpwuid();
X	register char *hd;
X	register struct passwd *p;
X
X	hd = getenv("HOME");
X	if (hd == NULL || *hd == 0) {
X		p = getpwuid(getuid());
X		hd = p->pw_dir;
X	}
X	if (hd == NULL || *hd == 0)
X		hd = "/";
X	return hd;
X}
X
Xchar *ealloc(n)
Xint n;
X{
X	register char *s;
X
X	s = (char *)malloc(n);
X	if (s == NULL) {
X		fprintf(stderr, "malloc: %s: Out of memory\n", nodename);
X		exit(-1);
X	}
X	return s;
X}
All work and no play makes Jack a dull boy
exit