From 66c3fa5e8e3f32f6d04f4a78ed93d251731baa9c Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 10 May 2012 02:22:20 -0400 Subject: add grep utility this should conform to POSIX 2008. -i does not work with -F, but it's not clear to me that it's intended/required to. todo list: make -i work with -F, and add the nonstandard by highly desirable -r option --- Makefile | 2 +- src/grep.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 src/grep.c diff --git a/Makefile b/Makefile index 259f782..fedc7a2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ LDFLAGS = -s -include config.mak -B = cat true false pwd +B = cat true false pwd grep UB = strings basename dirname link wc fold iconv ALL = $(B:%=bin/%) $(UB:%=bin/%) diff --git a/src/grep.c b/src/grep.c new file mode 100644 index 0000000..ae5e941 --- /dev/null +++ b/src/grep.c @@ -0,0 +1,197 @@ +/* + * grep.c + * Implementation of POSIX 2008 ls utility + * Copyright © 2012 Rich Felker + * Licensed under the terms of the GNU General Public License, v2 or later + */ + +#include +#include +#include +#include +#include + +struct pattern { + struct pattern *next; + char *str; + regex_t re; +}; + +static struct pattern *head, *tail; + +static void usage() +{ + exit(1); +} + +static void addpats(const char *prog, int mode, const char *src) +{ + FILE *f; + char *line = 0; + ssize_t llen = 0; + struct pattern *pat; + f = (mode=='f') ? fopen(src, "rb") + : fmemopen((void *)src, strlen(src), "rb"); + if (!f) { + fprintf(stderr, "%s: ", prog); + perror(mode=='f' ? src : 0); + exit(1); + } + while ((llen = getline(&line, (size_t[]){0}, f)) >= 0) { + if (llen && line[llen-1]=='\n') + line[--llen] = 0; + pat = malloc(sizeof *pat); + if (!pat) { + perror(prog); + exit(1); + } + pat->str = line; + pat->next = 0; + if (tail) tail->next = pat; + else head = tail = pat; + line = 0; + llen = 0; + } + fclose(f); +} + +#define FLAG_E (1U<<0) +#define FLAG_F (1U<<1) +#define FLAG_c (1U<<2) +#define FLAG_i (1U<<7) +#define FLAG_l (1U<<8) +#define FLAG_n (1U<<9) +#define FLAG_q (1U<<10) +#define FLAG_s (1U<<11) +#define FLAG_v (1U<<12) +#define FLAG_x (1U<<13) + +int main(int argc, char **argv) +{ + static const char *optpat = "EFce:f:ilnqsvx"; + FILE *f; + char *line = 0; + ssize_t llen = 0; + int b; + int multifile; + struct pattern *pat; + int match, any_matches = 0; + long long count, lineno; + unsigned flags = 0; + const char *filename; + int err; + + while ((b=getopt(argc, argv, optpat))!=EOF) { + flags |= 1U << strchr(optpat, b)-optpat; + if (b-'e'<2U) addpats(argv[0], b, optarg); + } + if (!argv[optind]) usage(); + if (!head) addpats(argv[0], 'e', argv[optind++]); + + if (!(flags & FLAG_F)) { + int re_opts = (flags & FLAG_E) + ? REG_NOSUB|REG_EXTENDED + : REG_NOSUB; + if (flags & FLAG_i) re_opts |= REG_ICASE; + for (pat=head; pat; pat=pat->next) { + if (flags & FLAG_x) { + char *tmp = malloc(strlen(pat->str)+3); + if (!tmp) { + perror(argv[0]); + exit(1); + } + sprintf(tmp, "%s%s%s", + pat->str[0]=='^'?"":"^", pat->str, + pat->str[strlen(pat->str)-1]=='$'?"":"$"); + free(pat->str); + pat->str = tmp; + } + if ((err=regcomp(&pat->re, pat->str, re_opts))) { + char errstr[256]; + regerror(err, &pat->re, errstr, sizeof errstr); + fprintf(stderr, "%s: %s\n", argv[0], errstr); + exit(1); + } + free(pat->str); + } + } + + if (!argv[optind]) { + optind--; + filename = "(standard input)"; + f = stdin; + multifile = 0; + goto process_stdin; + } + + multifile = !!argv[optind+1]; + + for (; argv[optind]; optind++) { + filename = argv[optind]; + f = fopen(filename, "rb"); + if (!f) { + if (!(flags & FLAG_s)) { + fprintf(stderr, "%s: ", argv[0]); + perror(filename); + } + continue; + } +process_stdin: + count = 0; + lineno = 0; + while ((llen = getline(&line, (size_t[]){0}, f)) >= 0) { + lineno++; + if (llen && line[llen-1]=='\n') + line[--llen] = 0; + match = 0; + for (pat=head; pat; pat=pat->next) { + if (flags & FLAG_F) { + if (flags & FLAG_x) { + if (!strcmp(line, pat->str)) { + match = 1; + break; + } + } else if (strstr(line, pat->str)) { + match = 1; + break; + } + } else if (!regexec(&pat->re, line, 0, 0, 0)) { + match = 1; + break; + } + } + if (flags & FLAG_v) match = !match; + if (match) { + any_matches = 1; + if (flags & FLAG_q) exit(0); + if (flags & FLAG_l) { + puts(filename); + break; + } + if (flags & FLAG_c) { + count++; + continue; + } + if (multifile) { + if (flags & FLAG_n) + printf("%s:%lld:%s\n", filename, lineno, line); + else + printf("%s:%s\n", filename, line); + } else { + if (flags & FLAG_n) + printf("%lld:%s\n", lineno, line); + else + puts(line); + } + } + } + if (flags & FLAG_c) { + if (multifile) + printf("%s:%lld\n", filename, count); + else + printf("%lld\n", count); + } + fclose(f); + } + return !any_matches; +} -- cgit v1.2.1