|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T f
Length: 13098 (0x332a) Types: TextFile Names: »fgrep.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦de6342db5⟧ »./fgrep-1.0.tar.Z« └─⟦571f2592a⟧ └─⟦this⟧ »fgrep-1.0/fgrep.c«
/* fgrep.c - grep program built around matcher. Copyright 1989 Free Software Foundation Written August 1989 by Mike Haertel. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author may be reached (Email) at the address mike@ai.mit.edu, or (US mail) as Mike Haertel c/o Free Software Foundation. */ #include "std.h" #include "unix.h" #include <errno.h> #include <stdio.h> #include <string.h> #include "kwset.h" #define NCHAR (UCHAR_MAX + 1) /* For error messages. */ static const char *prog; static int error_seen; /* Flags controlling the style of output. */ static int out_silent; /* Suppress all normal output. */ static int out_invert; /* Print nonmatching stuff. */ static int out_file; /* Print filenames. */ static int out_line; /* Print line numbers. */ static int out_byte; /* Print byte offsets. */ static int out_before; /* Lines of leading context. */ static int out_after; /* Lines of trailing context. */ /* Print MESG and possibly the error string for ERRNUM. Remember that something awful happened. */ static void DEFUN(error, (mesg, errnum), const char *mesg AND int errnum) { if (errnum) fprintf(stderr, "%s: %s: %s\n", prog, mesg, strerror(errnum)); else fprintf(stderr, "%s: %s\n", prog, mesg); error_seen = 1; } /* Like error(), but die horribly after printing. */ static void DEFUN(fatal, (mesg, errnum), const char *mesg AND int errnum) { error(mesg, errnum); exit(2); } /* Interface to handle errors and fix library lossage. */ static PTR DEFUN(xmalloc, (size), size_t size) { PTR result; result = malloc(size); if (size && !result) fatal("memory exhausted", 0); return result; } /* Interface to handle errors and fix some library lossage. */ static PTR DEFUN(xrealloc, (ptr, size), PTR ptr AND size_t size) { PTR result; if (ptr) result = realloc(ptr, size); else result = malloc(size); if (size && !result) fatal("memory exhausted", 0); return result; } /* Compiled search pattern. */ kwset_t kwset; /* Flags controlling how pattern matching is performed. */ static int match_fold; /* Fold all letters to one case. */ static int match_words; /* Match only whole words. */ static int match_lines; /* Match only whole lines. */ static void DEFUN(compile, (pattern, size), const char *pattern AND size_t size) { const char *beg, *lim, *err; static char trans[NCHAR]; int i; if (match_fold) for (i = 0; i < NCHAR; ++i) trans[i] = TOLOWER(i); if (!(kwset = kwsalloc(match_fold ? trans : (const char *) NULL))) fatal("memory exhausted", 0); beg = pattern; do { for (lim = beg; lim < pattern + size && *lim != '\n'; ++lim) ; if (err = kwsincr(kwset, beg, lim - beg)) fatal(err, 0); if (lim < pattern + size) ++lim; beg = lim; } while (beg < pattern + size); if (err = kwsprep(kwset)) fatal(err, 0); } static char * DEFUN(execute, (buf, size), char *buf AND size_t size) { register char *beg, *try; register size_t len; struct kwsmatch kwsmatch; beg = buf; for (;beg <= buf + size; ++beg) { if (!(beg = kwsexec(kwset, beg, buf + size - beg, &kwsmatch))) return NULL;; len = kwsmatch.size[0]; if (match_lines) { if (beg > buf && beg[-1] != '\n') continue; if (beg + len < buf + size && *(beg + len) != '\n') continue; return beg; } else if (match_words) for (try = beg; len && try;) { if (try > buf && (ISALNUM((unsigned char) try[-1]) || !ISALNUM((unsigned char) *try))) goto retry; if (try + len < buf + size && (ISALNUM((unsigned char) *(try + len)) || !ISALNUM((unsigned char) (try + len)[-1]))) goto retry; return try; retry: if (--len) try = kwsexec(kwset, beg, len, &kwsmatch); else break; len = kwsmatch.size[0]; } else return beg; } return NULL; } /* Hairy buffering mechanism to efficiently support all the options. */ static char *bufbeg; /* Beginning of user-visible portion. */ static char *buflim; /* Limit of user-visible portion. */ static char *buf; /* Pointer to base of buffer. */ static size_t bufalloc; /* Allocated size of buffer. */ static size_t bufcc; /* Count of characters in buffer. */ static unsigned long int buftotalcc; /* Total character count since reset. */ static char *buflast; /* Pointer after last character printed. */ static int bufgap; /* Weird flag indicating buflast is a lie. */ static unsigned long int buftotalnl; /* Count of newlines before last character. */ static int bufpending; /* Lines of pending output at buflast. */ static int bufdesc; /* File descriptor to read from. */ static int bufeof; /* Flag indicating EOF reached. */ static const char *buffile; /* File name for messages. */ /* Scan and count the newlines prior to LIM in the buffer. */ static void DEFUN(nlscan, (lim), register char *lim) { register char *p; for (p = buflast; p < lim; ++p) if (*p == '\n') ++buftotalnl; buflast = lim; } /* Print the line beginning at BEG, using SEP to separate optional label fields from the text of the line. Return the size of the line. */ static size_t DEFUN(prline, (beg, sep), register char *beg AND register char sep) { register size_t cc; register char c; static int err; cc = 0; if (out_silent || err) while (beg < buflim) { ++cc; if (*beg++ == '\n') break; } else { if (out_file) printf("%s%c", buffile, sep); if (out_line) { nlscan(beg); printf("%d%c", buftotalnl + 1, sep); } if (out_byte) printf("%lu%c", buftotalcc + (beg - buf), sep); while (beg < buflim) { ++cc; c = *beg++; putchar(c); if (c == '\n') break; } if (ferror(stdout)) { error("output error", errno); err = 1; } } if (out_line) nlscan(beg); else buflast = beg; bufgap = 0; return cc; } /* Print pending bytes of last trailing context prior to LIM. */ static void DEFUN(prpending, (lim), register char *lim) { while (buflast < lim && bufpending) { --bufpending; prline(buflast, '-'); } } /* Print the lines between BEG and LIM. Deal with context crap. Return the count of lines between BEG and LIM. */ static int DEFUN(prtext, (beg, lim), char *beg AND char *lim) { static int used; register char *p; int i, n; prpending(beg); p = beg; for (i = 0; i < out_before; ++i) if (p > buflast) do --p; while (p > buflast && p[-1] != '\n'); if ((out_before || out_after) && used && (p > buflast || bufgap)) puts("--"); while (p < beg) p += prline(p, '-'); n = 0; while (p < lim) { ++n; p += prline(p, ':'); } bufpending = out_after; used = 1; return n; } /* Fill the user-visible portion of the buffer, returning a byte count. */ static int fillbuf() { register char *b, *d, *l; int i, cc; size_t discard, save; prpending(buflim); b = buflim; for (i = 0; i < out_before; ++i) if (b > buflast) do --b; while (b > buflast && b[-1] != '\n'); if (buflast < b) bufgap = 1; if (out_line) nlscan(b); discard = b - buf; save = buflim - b; if (b > buf) { d = buf; l = buf + bufcc; while (b < l) *d++ = *b++; } bufcc -= discard; buftotalcc += discard; do { if (!bufeof) { if (bufcc > bufalloc / 2) buf = xrealloc(buf, bufalloc *= 2); cc = read(bufdesc, buf + bufcc, bufalloc - bufcc); if (cc < 0) { error(buffile, errno); bufeof = 1; } else { bufeof = !cc; bufcc += cc; } } bufbeg = buf + save; for (l = buf + bufcc; l > bufbeg && l[-1] != '\n'; --l) ; buflim = l; buflast = buf; } while (!bufeof && bufbeg == buflim); if (bufeof) buflim = buf + bufcc; return buflim - bufbeg; } /* One-time initialization. */ static void initbuf() { bufalloc = 8192; buf = xmalloc(bufalloc); } /* Reset the buffer for a new file. */ static void DEFUN(resetbuf, (desc, file), int desc AND const char *file) { bufbeg = buf; buflim = buf; bufcc = 0; buftotalcc = 0; buflast = buf; bufgap = 0; buftotalnl = 0; bufpending = 0; bufdesc = desc; bufeof = 0; buffile = file; } /* Scan the user-visible portion of the buffer, calling prtext() for matching lines (or between matching lines if OUT_INVERT is true). Return a count of lines printed. */ static int grepbuf() { int total; register char *p, *b, *l; total = 0; p = bufbeg; while (b = execute(p, buflim - p)) { if (b == buflim && (b > bufbeg && b[-1] == '\n' || b == bufbeg)) break; while (b > bufbeg && b[-1] != '\n') --b; l = b + 1; while (l < buflim && l[-1] != '\n') ++l; if (!out_invert) total += prtext(b, l); else if (p < b) total += prtext(p, b); p = l; } if (out_invert && p < buflim) total += prtext(p, buflim); return total; } /* Scan the given file, returning a count of lines printed. */ static int DEFUN(grep, (desc, file), int desc AND const char *file) { int total; total = 0; resetbuf(desc, file); while (fillbuf()) total += grepbuf(); return total; } static const char version[] = "GNU fgrep, version 1.0"; #define USAGE \ "usage: %s [-[[AB] ]<num>] [-[CVchilnsvwx]] [-[ef]] <expr> [<files...>]\n" static void usage() { fprintf(stderr, USAGE, prog); exit(2); } int DEFUN(main, (argc, argv), int argc AND char *argv[]) { char *keys; size_t keycc, keyalloc; int count_matches, no_filenames, list_files; int opt, cc, desc, count, status; FILE *fp; prog = argv[0]; if (prog && strrchr(prog, '/')) prog = strrchr(prog, '/') + 1; keys = NULL; count_matches = 0; no_filenames = 0; list_files = 0; while ((opt = getopt(argc, argv, "0123456789A:B:CVbce:f:hilnsvwxy")) != EOF) switch (opt) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': out_before = 10 * out_before + opt - '0'; out_after = 10 * out_after + opt - '0'; break; case 'A': out_after = atoi(optarg); if (out_after < 0) usage(); break; case 'B': out_before = atoi(optarg); if (out_before < 0) usage(); break; case 'C': out_before = out_after = 2; break; case 'V': fprintf(stderr, "%s\n", version); break; case 'b': out_byte = 1; break; case 'c': out_silent = 1; count_matches = 1; break; case 'e': if (keys) usage(); keys = optarg; keycc = strlen(keys); break; case 'f': if (keys) usage(); fp = strcmp(optarg, "-") ? fopen(optarg, "r") : stdin; if (!fp) fatal(optarg, errno); keyalloc = 1024; keys = xmalloc(keyalloc); keycc = 0; while (!feof(fp) && (cc = fread(keys + keycc, 1, keyalloc - keycc, fp)) > 0) { keycc += cc; if (keycc == keyalloc) keys = xrealloc(keys, keyalloc *= 2); } if (fp != stdin) fclose(fp); break; case 'h': no_filenames = 1; break; case 'i': case 'y': /* For old-timers . . . */ match_fold = 1; break; case 'l': out_silent = 1; list_files = 1; break; case 'n': out_line = 1; break; case 's': out_silent = 1; break; case 'v': out_invert = 1; break; case 'w': match_words = 1; break; case 'x': match_lines = 1; break; default: usage(); break; } if (!keys) if (optind < argc) { keys = argv[optind++]; keycc = strlen(keys); } else usage(); compile(keys, keycc); if (argc - optind > 1 && !no_filenames) out_file = 1; status = 1; initbuf(); if (optind < argc) while (optind < argc) { desc = strcmp(argv[optind], "-") ? open(argv[optind], 0) : 0; if (desc < 0) error(argv[optind], errno); else { count = grep(desc, argv[optind]); if (count_matches) { if (out_file) printf("%s:", argv[optind]); printf("%d\n", count); } if (count) { status = 0; if (list_files) printf("%s\n", argv[optind]); } } if (desc) close(desc); ++optind; } else { count = grep(0, "<stdin>"); if (count_matches) printf("%d\n", count); if (count) { status = 0; if (list_files) printf("%s\n", argv[optind]); } } return error_seen ? 2 : status; }