#include "../command.h" #include static struct { bool filename_prefix; bool never_file_prefix; bool line_number; bool only_matching_names; bool only_non_matching_names; bool only_line_count; bool only_matching_part; bool quiet; bool inverse; bool ignore_case; bool is_regex; } flags; static int short_arg(char c, char* next) { UNUSED(next); switch (c) { case 'H': flags.filename_prefix = true; break; case 'h': flags.never_file_prefix = true; break; case 'n': flags.line_number = true; break; case 'l': flags.only_matching_names = true; break; case 'L': flags.only_non_matching_names = true; break; case 'c': flags.only_line_count = true; break; case 'o': flags.only_matching_part = true; break; case 'q': flags.quiet = true; break; case 'v': flags.inverse = true; break; case 'i': flags.ignore_case = true; break; case 'F': flags.is_regex = false; break; case 'E': flags.is_regex = true; break; default: return ARG_INVALID; } return ARG_UNUSED; } static void help(void) { printf("Usage: grep [-HhlLoqviFE] [-m N] PATTERN [FILE]...\n"); printf("Search for PATTERN in FILEs (or stdin)\n"); printf("\t-H\tAdd 'filename:' prefix\n"); printf("\t-h\tDo not add 'filename:' prefix\n"); printf("\t-n\tAdd 'line_no:' prefix\n"); printf("\t-l\tShow only names of files that match\n"); printf("\t-L\tShow only names of files that don't match\n"); printf("\t-c\tShow only count of matching lines\n"); printf("\t-o\tShow only the matching part of line\n"); printf("\t-q\tQuiet. Return 0 if PATTERN is found, 1 otherwise\n"); printf("\t-v\tSelect non-matching lines\n"); printf("\t-i\tIgnore case\n"); printf("\t-F\tPATTERN is a literal (not regexp)\n"); printf("\t-E\tPATTERN is an extended regexp\n"); } static bool match_regex(char** string, re_t pattern) { int len; int index; if ((index = re_matchp(pattern, *string, &len)) < 0) return false; if (flags.only_matching_part) { (*string) += index; (*string)[len] = '\0'; } return true; } static bool match_literal(char** string, char* pattern) { char* match = *string; size_t match_len = strlen(match); size_t pattern_len = strlen(pattern); size_t i; if (match_len < pattern_len) return false; for (i = 0; i < match_len - pattern_len + 1; i++) { if ( (!flags.ignore_case && strncmp(match + i, pattern, pattern_len) == 0) || (flags.ignore_case && strncasecmp(match + i, pattern, pattern_len) == 0) ) { if (flags.only_matching_part) { *string = (*string) + i; (*string)[pattern_len] = '\0'; } return true; } } return false; } static bool match(char** string, void* pattern) { bool result; if (flags.is_regex) { result = match_regex(string, (re_t) pattern); } else { result = match_literal(string, (char*) pattern); } return (flags.inverse ? !result : result); } static bool match_any(char* path, void* pattern) { FILE* file; char* buf = NULL; size_t offset; bool matched = false; int read; file = get_file_s(path, "r"); if (file == NULL) return false; while ((read = getline(&buf, &offset, file)) > 0) { char* save = buf; if (buf[read-1] == '\n') buf[read-1] = '\0'; if (match(&save, pattern)) { matched = true; break; } } if (buf != NULL) free(buf); return matched; } static bool match_file(char* path, void* pattern, bool many) { FILE* file; char* buf = NULL; size_t offset; int num_matched = 0; int line_num = 0; int read; file = get_file_s(path, "r"); if (file == NULL) return false; while((read = getline(&buf, &offset, file)) > 0) { char* matched = buf; if (buf[read-1] == '\n') buf[read-1] = '\0'; line_num++; if (!match(&matched, pattern)) { continue; } num_matched++; if (flags.only_line_count || flags.quiet) continue; if ((many || flags.filename_prefix) && !flags.never_file_prefix) { print_file_path(path); putchar(':'); } if (flags.line_number) { printf("%d:", line_num); } if (flags.only_matching_part) { printf("%s\n", matched); } else { printf("%s\n", buf); } } if (!flags.quiet && flags.only_line_count) { if ((many || flags.filename_prefix) && !flags.never_file_prefix) { print_file_path(path); putchar(':'); } printf("%d\n", num_matched); } if (buf != NULL) free(buf); return num_matched != 0; } static void* compile(char* pattern) { if (flags.is_regex) { return re_compile(pattern); } else { return pattern; } } static bool run_match(char* path, void* pattern, bool many) { bool result; if (flags.only_matching_names || flags.only_non_matching_names) { result = match_any(path, pattern); if (flags.only_non_matching_names) result = !result; if (result && !flags.quiet) { print_file_path(path); putchar('\n'); } return result; } else { return match_file(path, pattern, many); } } COMMAND(grep) { int start, i; char* pattern; bool many, ok; void* compiled; flags.only_matching_part = false; flags.only_non_matching_names = false; flags.only_matching_names = false; flags.only_line_count = false; flags.quiet = false; flags.is_regex = true; flags.line_number = false; flags.never_file_prefix = false; flags.filename_prefix = false; flags.inverse = false; start = parse_args(argc, argv, help, short_arg, NULL); if (argc - start < 1) global_help(help); pattern = argv[start++]; many = argc - start > 0; ok = false; compiled = compile(pattern); if (run_match("-", compiled, many)) ok = true; for (i = start; i < argc; i++) { if (run_match(argv[i], compiled, many)) ok = true; } if (flags.quiet) { return ok ? EXIT_SUCCESS : EXIT_FAILURE; } else { return EXIT_SUCCESS; } }