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