grep-stub.c (5397B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #define _GNU_SOURCE // strcasestr
- #include <assert.h>
- #include <errno.h>
- #include <regex.h>
- #include <stdbool.h>
- #include <stdio.h> // getline
- #include <stdlib.h> // free
- #include <string.h> // strstr, strerror
- #include <unistd.h> // getopt
- static char *argv0 = "grep";
- struct char_list
- {
- char *data;
- struct char_list *next;
- };
- static struct char_list *patterns;
- static struct char_list *files;
- static size_t patterns_r_len = 0;
- static regex_t *patterns_r = NULL;
- static bool fixed_str = false, invert = false, count_only = false, opt_n = false, quiet = false;
- static bool multiple_files = false;
- static char *(*find_substring)(const char *, const char *) = &strstr;
- static bool found_any = false;
- static int
- do_grep(FILE *stream, char *filename)
- {
- char *line = NULL;
- size_t len = 0;
- size_t ln = 0;
- int count = 0;
- ssize_t nread = 0;
- while((nread = getline(&line, &len, stream)) != -1)
- {
- ln++;
- if(line[nread - 1] == '\n') line[nread - 1] = 0;
- bool found = false;
- if(fixed_str)
- {
- for(struct char_list *pattern = patterns; pattern != NULL; pattern = pattern->next)
- {
- if(find_substring(line, pattern->data) != NULL)
- {
- found = true;
- break;
- }
- }
- }
- else
- {
- assert(patterns_r != NULL);
- assert(patterns_r_len != 0);
- for(size_t i = 0; i < patterns_r_len; i++)
- {
- if(regexec(&patterns_r[i], line, 0, 0, 0) == 0)
- {
- found = true;
- break;
- }
- }
- }
- if(found != invert)
- {
- found_any = true;
- count++;
- if(!quiet)
- {
- if(multiple_files) printf("%s:", filename);
- if(opt_n) printf("%zd:", ln);
- if(!count_only) printf("%s\n", line);
- }
- }
- }
- if(!quiet && count_only) printf("%d\n", count);
- free(line);
- if(ferror(stream))
- {
- fprintf(stderr, "%s: Error while reading '%s': %s\n", argv0, filename, strerror(errno));
- return -1;
- }
- return 0;
- }
- static void
- usage()
- {
- fprintf(stderr,
- "Usage: %s [-EFcilnqsvx] [-f patterns_file... | -e pattern... | pattern] [file...]\n",
- argv0);
- }
- int
- main(int argc, char *argv[])
- {
- argv0 = argv[0];
- bool opt_l = false;
- int regcomp_flags = REG_NOSUB;
- struct char_list *patterns_last = NULL;
- struct char_list *files_last = NULL;
- size_t patterns_len = 0;
- switch(argv0[0])
- {
- case 'e':
- regcomp_flags |= REG_EXTENDED;
- break;
- case 'f':
- fixed_str = true;
- break;
- }
- int c = -1;
- while((c = getopt(argc, argv, ":EFce:f:ilnqsvx")) != -1)
- {
- switch(c)
- {
- case 'E':
- regcomp_flags |= REG_EXTENDED;
- break;
- case 'F':
- fixed_str = true;
- break;
- case 'c':
- count_only = true;
- break;
- case 'e':
- if(patterns_last == NULL)
- {
- patterns = &(struct char_list){
- .data = optarg,
- .next = NULL,
- };
- patterns_last = patterns;
- patterns_len++;
- }
- else
- {
- patterns->next = &(struct char_list){
- .data = optarg,
- .next = NULL,
- };
- patterns_last = patterns->next;
- patterns_len++;
- }
- break;
- case 'f':
- if(files_last == NULL)
- {
- files = &(struct char_list){
- .data = optarg,
- .next = NULL,
- };
- files_last = files;
- }
- else
- {
- files->next = &(struct char_list){
- .data = optarg,
- .next = NULL,
- };
- files_last = files->next;
- }
- break;
- case 'i':
- regcomp_flags |= REG_ICASE;
- find_substring = &strcasestr;
- break;
- case 'l':
- // (standard input)
- opt_l = true;
- break;
- case 'n':
- opt_n = true;
- break;
- case 'q':
- quiet = true;
- break;
- case 's':
- //no_warn = true;
- break;
- case 'v':
- invert = true;
- break;
- case 'x':
- //whole_line = true;
- break;
- }
- }
- argc -= optind;
- argv += optind;
- if(patterns_last == NULL && files_last == NULL)
- {
- if(argc == 0)
- {
- fprintf(stderr, "%s: No pattern given\n", argv0);
- usage();
- return 2;
- }
- patterns = &(struct char_list){
- .data = argv[0],
- .next = NULL,
- };
- patterns_last = patterns;
- patterns_len++;
- argc -= 1;
- argv += 1;
- }
- int err = 0;
- if(!fixed_str)
- {
- patterns_r = calloc(patterns_len, sizeof(regex_t));
- if(patterns_r == NULL)
- {
- fprintf(stderr, "%s: Failed allocating memory for patterns: %s\n", argv0, strerror(errno));
- return 2;
- }
- for(struct char_list *pattern = patterns; pattern != NULL; pattern = pattern->next)
- {
- int ret = regcomp(&patterns_r[patterns_r_len], pattern->data, regcomp_flags);
- if(ret != 0)
- {
- char errbuf[100] = "";
- regerror(ret, &patterns_r[patterns_r_len], errbuf, sizeof(errbuf));
- fprintf(stderr, "%s: Error compiling regex /%s/: %s\n", pattern->data, errbuf, argv0);
- err = 1;
- goto cleanup;
- }
- patterns_r_len++;
- }
- }
- if(argc == 0)
- {
- if(do_grep(stdin, "(standard input)") < 0) return 2;
- goto cleanup;
- }
- if(argc > 1) multiple_files = true;
- for(int i = 0; i < argc; i++)
- {
- FILE *stream = fopen(argv[i], "r");
- if(stream == NULL)
- {
- fprintf(stderr, "%s: Failed opening '%s' for reading: %s\n", argv0, argv[i], strerror(errno));
- err = 1;
- goto cleanup;
- }
- if(do_grep(stream, argv[i]) < 0) return 2;
- }
- cleanup:
- if(!found_any) err++;
- for(size_t i = 0; i < patterns_r_len; i++)
- {
- regfree(&patterns_r[i]);
- }
- return err;
- }