diff options
author | Mark Whitley | 2000-06-28 22:00:26 +0000 |
---|---|---|
committer | Mark Whitley | 2000-06-28 22:00:26 +0000 |
commit | d37218941c37795cc8e96ddb3312d83fb2269d5a (patch) | |
tree | f0b2aa0f49da8e0d4fb76a81a491e1d1ee5c4a05 /findutils/grep.c | |
parent | 268b8c4f387b9019bbb3591fb07403925d55d0c5 (diff) | |
download | busybox-d37218941c37795cc8e96ddb3312d83fb2269d5a.zip busybox-d37218941c37795cc8e96ddb3312d83fb2269d5a.tar.gz |
Brand, spankin', new grep that uses libc regex routines instead of the
hand-rolled ones. Sed still needs to be replaced and then the regexp stuff can
be axed.
Diffstat (limited to 'findutils/grep.c')
-rw-r--r-- | findutils/grep.c | 255 |
1 files changed, 155 insertions, 100 deletions
diff --git a/findutils/grep.c b/findutils/grep.c index 0e495ff..aca469e 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -1,10 +1,8 @@ -/* vi: set sw=4 ts=4: */ /* - * Mini grep implementation for busybox - * + * Mini grep implementation for busybox using libc regex. * * Copyright (C) 1999,2000 by Lineo, inc. - * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> + * Written by Mark Whitley <markw@lineo.com>, <markw@enol.com> * * 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 @@ -22,149 +20,206 @@ * */ -/* - 18-Dec-1999 Konstantin Boldyshev <konst@voshod.com> - - + -q option (be quiet) - + exit code depending on grep result (TRUE or FALSE) - (useful for scripts) -*/ - -#include "internal.h" -#include "regexp.h" #include <stdio.h> -#include <dirent.h> +#include <stdlib.h> +#include <unistd.h> /* for getopt() */ +#include <regex.h> +#include <string.h> /* for strerror() */ #include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <time.h> -#include <ctype.h> -#define BB_DECLARE_EXTERN -#define bb_need_too_few_args -#include "messages.c" +#include "internal.h" + +extern int optind; /* in unistd.h */ +extern int errno; /* for use with strerror() */ static const char grep_usage[] = - "grep [OPTIONS]... PATTERN [FILE]...\n" + "grep [-ihHnqvs] pattern [files...]\n" #ifndef BB_FEATURE_TRIVIAL_HELP "\nSearch for PATTERN in each FILE or standard input.\n\n" "OPTIONS:\n" + "\t-H\tprefix output lines with filename where match was found\n" "\t-h\tsuppress the prefixing filename on output\n" "\t-i\tignore case distinctions\n" "\t-n\tprint line number with output lines\n" "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" - "\t-v\tselect non-matching lines\n\n" -#if defined BB_REGEXP - "This version of grep matches full regular expressions.\n"; -#else - "This version of grep matches strings (not regular expressions).\n" -#endif + "\t-v\tselect non-matching lines\n" + "\t-s\tsuppress file open/read error messages\n\n" #endif ; -static int match = FALSE, beQuiet = FALSE; +static const int GROWBY = 80; /* how large we will grow strings by */ + +/* options */ +static int ignore_case = 0; +static int print_filename = 0; +static int print_line_num = 0; +static int be_quiet = 0; +static int invert_search = 0; +static int suppress_err_msgs = 0; + +/* globals */ +static regex_t regex; /* storage space for compiled regular expression */ +static int nmatches = 0; /* keeps track of the number of matches */ +static char *cur_file = NULL; /* the current file we are reading */ + -static void do_grep(FILE * fp, char *needle, char *fileName, int tellName, - int ignoreCase, int tellLine, int invertSearch) +/* This returns a malloc'ed char * which must be stored and free'ed */ +/* XXX: This function should probably go in a 'common'/'util'/'misc' file + * somewhere so it can be used by other folks. */ +static char *get_line_from_file(FILE *file) { - long line = 0; - char *haystack; - int truth = !invertSearch; + int ch; + int idx = 0; + char *linebuf = NULL; + int linebufsz = 0; + + while (1) { + ch = fgetc(file); + if (ch == EOF) + break; + /* grow the line buffer as necessary */ + if (idx > linebufsz-1) + linebuf = realloc(linebuf, linebufsz += GROWBY); + linebuf[idx++] = (char)ch; + if ((char)ch == '\n') + break; + } + + if (idx == 0) + return NULL; + + linebuf[idx] = 0; + return linebuf; +} + +static void print_matched_line(char *line, int linenum) +{ + if (print_filename) + printf("%s:", cur_file); + if (print_line_num) + printf("%i:", linenum); + + printf("%s", line); +} - while ((haystack = cstring_lineFromFile(fp))) { - line++; - if (find_match(haystack, needle, ignoreCase) == truth) { - if (tellName == TRUE) - printf("%s:", fileName); +static void grep_file(FILE *file) +{ + char *line = NULL; + int ret; + int linenum = 0; + + while ((line = get_line_from_file(file)) != NULL) { + linenum++; + ret = regexec(®ex, line, 0, NULL, 0); + if (ret == 0 && !invert_search) { /* match */ + + /* if we found a match but were told to be quiet, stop here and + * return success */ + if (be_quiet) { + regfree(®ex); + exit(0); + } - if (tellLine == TRUE) - printf("%ld:", line); + nmatches++; - if (beQuiet == FALSE) - fputs(haystack, stdout); + print_matched_line(line, linenum); - match = TRUE; + } else if (ret == REG_NOMATCH && invert_search) { + print_matched_line(line, linenum); } - free(haystack); + + free(line); } } - extern int grep_main(int argc, char **argv) { - FILE *fp; - char *needle; - char *fileName; - int tellName = TRUE; - int ignoreCase = FALSE; - int tellLine = FALSE; - int invertSearch = FALSE; - - if (argc < 1) { + int opt; + int reflags; + int ret; + + /* do special-case option parsing */ + if (argv[1] && (strcmp(argv[1], "--help") == 0)) usage(grep_usage); - } - argv++; - while (--argc >= 0 && *argv && (**argv == '-')) { - while (*++(*argv)) { - switch (**argv) { + /* do normal option parsing */ + while ((opt = getopt(argc, argv, "iHhnqvs")) > 0) { + switch (opt) { case 'i': - ignoreCase = TRUE; + ignore_case++; + break; + case 'H': + print_filename++; break; - case 'h': - tellName = FALSE; + print_filename--; break; - case 'n': - tellLine = TRUE; + print_line_num++; break; - case 'q': - beQuiet = TRUE; + be_quiet++; break; - case 'v': - invertSearch = TRUE; + invert_search++; + break; + case 's': + suppress_err_msgs++; break; - - default: - usage(grep_usage); - } } - argv++; } - if (argc == 0 || *argv == NULL) { - fatalError(too_few_args, "grep"); + /* argv[optind] should be the regex pattern; no pattern, no worky */ + if (argv[optind] == NULL) + usage(grep_usage); + + /* compile the regular expression */ + reflags = REG_NOSUB; /* we're not going to mess with sub-expressions */ + if (ignore_case) + reflags |= REG_ICASE; + if ((ret = regcomp(®ex, argv[optind], reflags)) != 0) { + int errmsgsz = regerror(ret, ®ex, NULL, 0); + char *errmsg = malloc(errmsgsz); + if (errmsg == NULL) { + fprintf(stderr, "grep: memory error\n"); + regfree(®ex); + exit(1); + } + regerror(ret, ®ex, errmsg, errmsgsz); + fprintf(stderr, "grep: %s\n", errmsg); + free(errmsg); + regfree(®ex); + exit(1); } - needle = *argv++; - argc--; + /* argv[(optind+1)..(argc-1)] should be names of file to grep through. If + * there is more than one file to grep, we will print the filenames */ + if ((argc-1) - (optind+1) > 0) + print_filename++; - if (argc == 0) { - do_grep(stdin, needle, "stdin", FALSE, ignoreCase, tellLine, invertSearch); + /* If no files were specified, take input from stdin. Otherwise, we grep + * through all the files specified. */ + if (argv[optind+1] == NULL) { + grep_file(stdin); } else { - /* Never print the filename for just one file */ - if (argc == 1) - tellName = FALSE; - while (argc-- > 0) { - fileName = *argv++; - - fp = fopen(fileName, "r"); - if (fp == NULL) { - perror(fileName); - continue; + int i; + FILE *file; + for (i = optind + 1; i < argc; i++) { + cur_file = argv[i]; + file = fopen(cur_file, "r"); + if (file == NULL) { + if (!suppress_err_msgs) + fprintf(stderr, "grep: %s: %s\n", cur_file, strerror(errno)); + } else { + grep_file(file); + fclose(file); } - - do_grep(fp, needle, fileName, tellName, ignoreCase, tellLine, invertSearch); - - if (ferror(fp)) - perror(fileName); - fclose(fp); } } - return(match); -} + regfree(®ex); -/* END CODE */ + if (nmatches == 0) + return 1; + + return 0; +} |