dan@rna.UUCP (Dan Ts'o) (04/24/86)
x I've had a number of requests for this program so here it is, G. G functions almost identical to grep except that it has a -c# option to print # lines around a match. G, however, only understands the ^.$ regular expression characters. G also has options to ignore case, find matches for more than one regular expression, and most of grep's options. G also has an alterego, TELE, which provides a convenient telephone-file searcher. G compiles under Microsoft C 3.0 and works fine under MSDOS as well as UNIX. Cheers, Dan Ts'o Dept. Neurobiology Rockefeller Univ. 1230 York Ave. NY, NY 10021 212-570-7671 ...cmcl2!rna!dan rna!dan@cmcl2.arpa echo tele.1 sed 's/^X//' > tele.1 << 'All work and no play makes Jack a dull boy' X.ds ]W Rockefeller Neurobiology 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 matching line output and error messages about unopenable files. XUseful for testing just the exit value for success or failure. X.TP X.B "\-f,-i" 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 \-w XRequire match as the entire word (defined as a sequence of alphanumerics or Xunderline). 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 tele.c sed 's/^X//' > 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; Xint wflag = 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 == '/') { X for (s = nodename; *s; s++) X if (*s == '/') X nodename = s; X nodename++; X } 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 'i': 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 'w': X wflag = 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 && !sflag) 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 long atol(); 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 && !sflag && !Cflag) { 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 (sflag) 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 char *bb; 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 bb = b; X for (;;) { X s = a; X t = b; X /* Try to find a place to begin match */ X if ((*s&0377) != DOT) X for (;;) { X /* At end of line ? */ X if (*t == 0) X break; X /* Found beginning of possible match */ X if (*t++ == *s) { X t--; X break; X } X m--; X } X /* Must match occur at beginning of line ? */ X if (carat && t != b) X break; X /* Not enough characters left for a match */ X if (n > m) X break; X /* Is it at a start of a word ? */ X b = t; X if (wflag) { X b--; X if (t != bb && (*b == '_' X || ('a' <= *b && *b <= 'z') X || ('A' <= *b && *b <= 'Z') X || ('0' <= *b && *b <= '9'))) { X b++; X m--; X continue; X } X } X for (;;) { X /* Match end of line */ X if (*t == '\n') { X if ((*s&0377) == DOLLAR) X return 1; X break; X } X /* Match anything */ X if ((*s&0377) == DOT) { X s++; X t++; X } X else if (*s++ != *t++) X break; X /* Complete match */ X if (*s == 0) { X if (wflag && *t != '\n' && (*t == '_' X || ('a' <= *t && *t <= 'z') X || ('A' <= *t && *t <= 'Z') X || ('0' <= *t && *t <= '9'))) X break; X return 1; X } X } X /* Could not match beginning of line */ 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