123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- /* grep - search files for regular expression matches.
- Copyright (C) 1985 Dave Curry
- This program is distributed in the hope that it will be useful,
- but without any warranty. No author or distributor
- accepts responsibility to anyone for the consequences of using it
- or for whether it serves any particular purpose or works at all,
- unless he says so in writing.
- Permission is granted to anyone to distribute verbatim copies
- of this program's source code as received, in any medium, provided that
- the copyright notice, the nonwarraty notice above
- and this permission notice are preserved,
- and that the distributor grants the recipient all rights
- for further redistribution as permitted by this notice,
- and informs him of these rights.
- Permission is granted to distribute modified versions of this
- program's source code, or of portions of it, under the above
- conditions, plus the conditions that all changed files carry
- prominent notices stating who last changed them and that the
- derived material, including anything packaged together with it and
- conceptually functioning as a modification of it rather than an
- application of it, is in its entirety subject to a permission
- notice identical to this one.
- Permission is granted to distribute this program (verbatim or
- as modified) in compiled or executable form, provided verbatim
- redistribution is permitted as stated above for source code, and
- A. it is accompanied by the corresponding machine-readable
- source code, under the above conditions, or
- B. it is accompanied by a written offer, with no time limit,
- to distribute the corresponding machine-readable source code,
- under the above conditions, to any one, in return for reimbursement
- of the cost of distribution. Verbatim redistribution of the
- written offer must be permitted. Or,
- C. it is distributed by someone who received only the
- compiled or executable form, and is accompanied by a copy of the
- written offer of source code which he received along with it.
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
- /*
- * cgrep - context grep
- *
- * This program is very similar to grep, except that it prints lines
- * of context around matching lines. The default is one line before
- * and one line after, but this may be changed.
- *
- * Flags understood are:
- *
- * -A # print # lines of context before matching lines
- * -B # print # lines of context after matching lines
- * -C turn on context grep (above flags do it too)
- * -E print in modified -n format for EMACS (JOVE) such that
- * only the matching lines (and not the context lines) are
- * parsed by the parse-some-errors command.
- * -F print the file name at the beginning of each matching line
- * -L do not separate matches with '-----' or blank lines
- * -b print block numbers in front of lines
- * -c count matching lines only
- * -e next argument is expression (for exprs starting with '-')
- * -f read expression from file
- * -i ignore case
- * -l print names of files containing matching lines only
- * -n number lines on output
- * -s silent mode - error messages only
- * -v all lines but those matching are printed (grep only)
- * -w match expression as if surrounded by \<...\>
- * -? print usage message and special characters
- *
- * David A. Curry, 7/9/84
- * davy@purdue-ecn
- * decvax!pur-ee!davy
- *
- * 4/20/85 - added -C flag, made -# and +# turn on cgrep. Cleaned up
- * and converted to use GNU regex routines.
- */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/file.h>
- #include <stdio.h>
- #include <ctype.h>
- #include "regex.h"
- #define LBSIZE 256 /* starting size of buffers */
- #define EXPSIZE 256 /* starting regexp buffer size */
- #define NCONTEXT 1 /* default # of context lines */
- #ifndef BYTEWIDTH
- #define BYTEWIDTH 8
- #endif
- /*
- * We save (2 * ncontext + 1) lines at a time in
- * structures like these.
- */
- struct line {
- long len; /* length of this line */
- long size; /* size of this buffer */
- long blkno; /* block number of line */
- long lineno; /* line number in the file */
- char *linebuf; /* the line itself */
- };
- char print_context;
- char print_for_emacs;
- char print_line_file_names;
- char dont_print_separators;
- char print_block_numbers;
- char print_count_only;
- char ignore_case;
- char only_mention_file_if_match;
- char print_line_numbers;
- char silent;
- char negate_condition;
- char match_as_word;
- int nfile; /* number of files to do */
- int nsucc; /* 1 if found anything */
- long nmatch; /* number of matching lines */
- long nbytes; /* number of bytes read */
- long lineno; /* line number in the file */
- long nlines; /* number of lines saved */
- int retcode; /* exit code */
- int input_fd; /* input file descriptor */
- int blocksize; /* block size of input file */
- char *readbufp; /* ptr to next char to fetch */
- int readbufcount; /* number of characters of */
- /* buffer actually filled with */
- /* data. -1 means have yet to */
- /* fill it; 0 = end of file. */
- char readbuf[8192]; /* buffer for reading from file */
- int precontext;
- int aftcontext;
- char *expression; /* the compiled expression */
- struct line **lines; /* the array of lines we save */
- struct re_pattern_buffer re_comp_buf = {0,};
- char re_comp_fastmap[256];
- char downcase_table[256];
- char *malloc (), *xmalloc (), *calloc (), *xcalloc (), *realloc (), *xrealloc ();
- char *progname; /* For error messages */
- main (argc, argv)
- int argc;
- char **argv;
- {
- register int i;
- struct stat sbuf;
- register char *s;
- extern char _sobuf[];
- char *rindex ();
- register int argn;
- int fd;
-
- retcode = 0;
- expression = NULL;
- precontext = aftcontext = NCONTEXT;
- print_context = print_for_emacs = 0;
- print_line_file_names = dont_print_separators = 0;
- print_block_numbers = print_count_only = 0;
- ignore_case = only_mention_file_if_match = print_line_numbers = 0;
- silent = negate_condition = match_as_word = 0;
- progname = argv[0];
- /*
- * Figure out what mode we're in.
- */
- if ((s = rindex (argv[0], '/')) == NULL)
- s = argv[0];
- else
- s++;
- if (*s == 'c')
- print_context = 1;
- /*
- * Process the arguments.
- */
- for (argn = 1; argn < argc && argv[argn][0] == '-'; argn++)
- {
- for (s = argv[argn]+1; *s; s++)
- {
- switch (*s)
- {
- case 'A': /* number of post-context lines */
- print_context = 1;
- if (isdigit (*(s+1)))
- {
- aftcontext = atoi (++s);
- while (isdigit (*(s+1)))
- s++;
- }
- else
- {
- if (argn + 1 >= argc)
- {
- error ("must give number after -A.\n", 0);
- exit (2);
- }
- aftcontext = atoi (argv[++argn]);
- }
- continue;
- case 'B': /* number of pre-context lines */
- print_context = 1;
- if (isdigit (*(s+1)))
- {
- precontext = atoi (++s);
- while (isdigit (*(s+1)))
- s++;
- }
- else
- {
- if (argn + 1 >= argc)
- {
- error ("must give number after -B.\n", 0);
- exit (2);
- }
- precontext = atoi (argv[++argn]);
- }
- continue;
- case 'C': /* do context */
- print_context++;
- continue;
- case 'E': /* EMACS mode */
- print_for_emacs++;
- continue;
- case 'F': /* print filenames */
- print_line_file_names++;
- continue;
- case 'L': /* no separators */
- dont_print_separators++;
- continue;
- case 'b': /* block numbers */
- print_block_numbers++;
- continue;
- case 'c': /* count matching lines */
- print_count_only++;
- continue;
- case 'e': /* next arg is expr */
- if (argn + 1 >= argc)
- {
- error ("-e with no following expression.\n", 0);
- exit (2);
- }
- expression = argv[++argn];
- continue;
- case 'f': /* read expression from file */
- if (argn + 1 >= argc)
- {
- error ("-f with no following filename.\n", 0);
- exit (2);
- }
- if ((fd = open (argv[++argn], 0)) < 0)
- {
- error ("", 0);
- perror (argv[argn]);
- exit (2);
- }
- fstat (fd, &sbuf);
- expression = xmalloc (sbuf.st_size + 1);
- read (fd, expression, sbuf.st_size);
- expression[sbuf.st_size] = NULL;
- close (fd);
- continue;
- case '?': /* usage */
- usage ();
- exit (0);
- case 'i': /* ignore case */
- ignore_case++;
- continue;
- case 'l': /* file names only */
- only_mention_file_if_match++;
- continue;
- case 'n': /* print line numbers */
- print_line_numbers++;
- continue;
- case 's': /* silent mode */
- silent++;
- continue;
- case 'v': /* non-matching lines */
- negate_condition++;
- continue;
- case 'w': /* match as \<...\> */
- match_as_word++;
- continue;
- default:
- error ("unknown option \"%c\".\n", *s);
- exit (2);
- }
- }
- }
- if (!print_context)
- {
- precontext = 0;
- aftcontext = 0;
- }
- if (expression == NULL)
- {
- if (argn >= argc)
- {
- error ("no regexp to match specified.\n", 0);
- exit (2);
- }
- expression = argv[argn++];
- }
- /*
- * Buffer the output. Changed this to only do it if printing context.
- * so that otherwise you can interrupt out
- * of grep as soon as you see what you want instead
- * of waiting for the buffer to flush.
- */
- if (print_context)
- setbuf (stdout, _sobuf);
-
- if (match_as_word)
- {
- s = xmalloc (strlen (expression) + 5);
- sprintf (s, "\\<%s\\>", expression);
- expression = s;
- }
- nlines = precontext + aftcontext + 1;
-
- /*
- * Get the pointers to the line buffers.
- */
- lines = (struct line **) xcalloc (nlines, sizeof (struct line *));
- /*
- * Get the line buffer structures.
- */
- for (i=0; i < nlines; i++)
- {
- lines[i] = (struct line *) xcalloc (1, sizeof (struct line));
- }
-
- /*
- * Get the line buffers.
- */
- for (i=0; i < nlines; i++)
- {
- lines[i]->linebuf = xmalloc (LBSIZE);
- lines[i]->size = LBSIZE;
- }
- /*
- * Compile the regular expression.
- */
- /*
- * We do "ignore case" by using the translate table.
- */
- if (ignore_case)
- {
- for (i=0; i <= 256; i++)
- downcase_table[i] = i;
- for (i='A'; i <= 'Z'; i++)
- downcase_table[i] = i - 'A' + 'a';
- re_comp_buf.translate = downcase_table;
- }
-
- re_comp_buf.fastmap = re_comp_fastmap;
- expression = re_compile_pattern (expression, strlen (expression),
- &re_comp_buf);
- if (expression)
- {
- error ("regular expression error: ", expression);
- exit (2);
- }
-
- nfile = argc - argn;
- /*
- * Process the files.
- */
- if (argn == argc)
- {
- execute (NULL);
- }
- else
- {
- while (argn < argc)
- execute (argv[argn++]);
- }
-
- exit (retcode != 0 ? retcode : nsucc == 0);
- }
- /*
- * execute - look for expression in file
- */
- execute (file)
- char *file;
- {
- register int gotone;
- struct stat statbuf;
-
- /*
- * Get file as standard input.
- */
- if (file)
- {
- if ((input_fd = open (file, O_RDONLY)) < 0)
- {
- error ("", 0);
- perror (file );
- retcode = 2;
- return;
- }
- }
- else
- {
- input_fd = fileno (stdin);
- }
-
- fstat (input_fd, &statbuf);
- blocksize = statbuf.st_blksize;
- if (blocksize == 0) blocksize = 512;
- gotone = 0;
- nmatch = 0;
- nbytes = 0;
- lineno = 1;
- readbufcount = -1;
- readbufp = readbuf;
- /*
- * Load in the initial set of lines.
- */
- loadup ();
- /*
- * lines[precontext]->lineno will be -1 when
- * we've hit the end of the file.
- */
- while (lines[precontext]->lineno > 0)
- {
- /*
- * Look for a match on this line.
- */
- if ((0 <= re_search (&re_comp_buf, lines[precontext]->linebuf,
- lines[precontext]->len, 0,
- lines[precontext]->len, 0))
- == !negate_condition)
- {
- /*
- * If we need to, print a separator.
- */
- if (((precontext > 0) || (aftcontext > 0))
- && gotone && !dont_print_separators
- && !print_count_only && !only_mention_file_if_match && !silent)
- {
- if (!print_line_numbers && !print_for_emacs)
- printf ("-----\n");
- else
- printf ("\n");
- gotone = 0;
- }
- output (file);
- gotone = 1;
- }
- /*
- * Get the next line.
- */
- nextline ();
- }
- if (print_count_only)
- {
- if ((nfile > 1) || print_line_file_names)
- printf ("%s:", file);
- printf ("%ld\n", nmatch);
- }
- if (file)
- close (input_fd);
- }
- /*
- * loadup - loads nlines lines, starting at lines[precontext].
- */
- loadup ()
- {
- register int i;
- /*
- * Initialize.
- */
- for (i = 0; i < nlines; i++)
- {
- lines[i]->len = 0;
- lines[i]->blkno = 0;
- lines[i]->lineno = -1;
- lines[i]->linebuf[0] = NULL;
- }
- /*
- * Load lines.
- */
- for (i = precontext; i < nlines; i++)
- getline (lines[i]);
- }
- /*
- * nextline - diddle the pointers to "shift" the buffers up, and
- * get a new line in last buffer.
- */
- nextline ()
- {
- register int i;
- register struct line *tmp;
-
- /*
- * Save first line.
- */
- tmp = lines[0];
- /*
- * Copy lines.
- */
- for (i=1; i < nlines; i++)
- lines[i-1] = lines[i];
-
- /*
- * Get last line.
- */
- lines[--i] = tmp;
-
- getline (lines[i]);
- }
- getline (l)
- register struct line *l;
- {
- register int c;
- int maxread, spaceleft;
- register char *p, *q, *endp;
- l->len = 0;
- l->blkno = nbytes / blocksize;
- /*
- * Already at end of file, nothing to read.
- */
- if (readbufcount == 0)
- {
- l->lineno = -1;
- return;
- }
- l->lineno = lineno++;
- for (;;)
- {
- maxread = readbuf + readbufcount - readbufp;
- if (maxread <= 0)
- {
- readbufcount = read (input_fd, readbuf, sizeof (readbuf));
- if (readbufcount == 0)
- break;
- readbufp = readbuf;
- maxread = readbufcount;
- }
- spaceleft = l->size - l->len - 1;
- if (spaceleft <= 0)
- {
- p = xrealloc (l->linebuf, l->size *= 2);
- l->linebuf = p;
- spaceleft = l->size - l->len - 1;
- }
- if (maxread > spaceleft)
- maxread = spaceleft;
- p = readbufp;
- q = l->linebuf + l->len;
- endp = readbufp + maxread;
- while (p != endp)
- {
- c = *p++;
- *q++ = c;
- if (c == '\n')
- break;
- }
- l->len += p - readbufp;
- nbytes += p - readbufp;
- readbufp = p;
- if (c == '\n')
- {
- l->len--;
- break;
- }
- }
- l->linebuf[l->len] = NULL;
- }
- /*
- * output - prints the window of context, and the line itself
- */
- output (file)
- char *file;
- {
- register int i;
-
- nsucc = 1;
- if (silent)
- return;
- if (print_count_only)
- {
- nmatch++;
- return;
- }
-
- if (only_mention_file_if_match)
- {
- printf ("%s\n", file);
- fflush (stdout);
- lseek (input_fd, 0L, 2);
- return;
- }
-
- for (i=0; i < nlines; i++)
- {
- if (lines[i]->lineno == -1)
- continue;
- if ((nfile > 1) || print_line_file_names || print_for_emacs)
- {
- printf ("%s", file);
- if (print_for_emacs)
- {
- if (i < precontext)
- putchar ('-');
- else if (i == precontext)
- putchar (':');
- else
- putchar ('+');
- }
- else
- {
- putchar (':');
- }
- }
- if (print_block_numbers)
- printf ("%ld:", lines[i]->blkno);
- if (print_line_numbers || print_for_emacs)
- {
- printf ("%ld", lines[i]->lineno);
- if (print_for_emacs)
- {
- if (i < precontext)
- putchar ('-');
- else if (i == precontext)
- putchar (':');
- else
- putchar ('+');
- }
- else
- {
- putchar (':');
- }
- }
- fwrite (lines[i]->linebuf, 1, lines[i]->len, stdout);
- putchar ('\n');
- }
- }
- error (fmt, arg)
- char *fmt, *arg;
- {
- fprintf (stderr, "%s: ", progname);
- fprintf (stderr, fmt, arg);
- }
- usage ()
- {
- printf ("Usage: grep [-#] [+#] [-A #] [-B #] [-CELbcefhilnsvw] expression file (s)\n");
- printf("Special characters: . $ + * ^ \\| [ - ] \\( \\) \\b \\B \\s \\S \\w \\W\n");
- }
- out_of_memory ()
- {
- error ("out of memory.\n", 0);
- exit (2);
- }
- char *
- xmalloc (size)
- int size;
- {
- register char *val;
- if (val = malloc (size)) return (val);
- out_of_memory ();
- }
- char *
- xrealloc (ptr, size)
- char *ptr;
- int size;
- {
- register char *val;
- if (val = realloc (ptr, size)) return (val);
- out_of_memory ();
- }
- char *
- xcalloc (num, size)
- int num, size;
- {
- register char *val;
- if (val = calloc (num, size)) return (val);
- out_of_memory ();
- }
|