summaryrefslogtreecommitdiff
path: root/src/commands/grep.c
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-05-04 12:24:47 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-05-04 12:24:47 -0400
commitbd471562c72035d588b78e3c80c9eb0e9a9c18ec (patch)
tree5728bcfcbd81fac687d33d423c83d76ab6809b3e /src/commands/grep.c
parenti cant spell (diff)
downloadlazysphere-bd471562c72035d588b78e3c80c9eb0e9a9c18ec.tar.gz
lazysphere-bd471562c72035d588b78e3c80c9eb0e9a9c18ec.tar.bz2
lazysphere-bd471562c72035d588b78e3c80c9eb0e9a9c18ec.zip
grep
Diffstat (limited to 'src/commands/grep.c')
-rw-r--r--src/commands/grep.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/commands/grep.c b/src/commands/grep.c
new file mode 100644
index 0000000..ffa1a41
--- /dev/null
+++ b/src/commands/grep.c
@@ -0,0 +1,250 @@
+#include "../command.h"
+#include <stdio.h>
+
+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);
+
+ if (match_len < pattern_len) return false;
+
+ for (size_t 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 = get_file_s(path, "r");
+ if (file == NULL) return false;
+
+ char* buf = NULL;
+ size_t offset;
+ bool matched = false;
+
+ int read;
+ while ((read = getline(&buf, &offset, file)) > 0) {
+ if (buf[read-1] == '\n') buf[read-1] = '\0';
+ char* save = buf;
+ 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 = get_file_s(path, "r");
+ if (file == NULL) return false;
+
+ int num_matched = 0;
+ int line_num = 0;
+ char* buf = NULL;
+ size_t offset;
+
+ int read;
+ while((read = getline(&buf, &offset, file)) > 0) {
+ if (buf[read-1] == '\n') buf[read-1] = '\0';
+ char* matched = buf;
+ 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) {
+
+ 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;
+
+ int start = parse_args(argc, argv, help, short_arg, NULL);
+
+ if (argc - start < 1) global_help(help);
+
+ char* pattern = argv[start++];
+
+ bool many = argc - start > 0;
+ bool ok = false;
+
+ void* compiled = compile(pattern);
+ if (run_match("-", compiled, many)) ok = true;
+
+ for (int 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;
+ }
+}