/* * grep.c * Implementation of POSIX 2008 grep 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; size_t lsize = 0; ssize_t llen; 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 (!head) { if (!argv[optind]) usage(); 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, &lsize, 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 (ferror(f) && !(flags & FLAG_s)) { fprintf(stderr, "%s: ", argv[0]); perror(filename); } if (flags & FLAG_c) { if (multifile) printf("%s:%lld\n", filename, count); else printf("%lld\n", count); } fclose(f); } return !any_matches; }