2023-05-04 16:24:47 +00:00
|
|
|
#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);
|
2023-05-04 20:10:37 +00:00
|
|
|
size_t i;
|
2023-05-04 16:24:47 +00:00
|
|
|
|
|
|
|
if (match_len < pattern_len) return false;
|
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
for (i = 0; i < match_len - pattern_len + 1; i++) {
|
2023-05-04 16:24:47 +00:00
|
|
|
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) {
|
2023-05-04 20:10:37 +00:00
|
|
|
FILE* file;
|
2023-05-04 16:24:47 +00:00
|
|
|
char* buf = NULL;
|
|
|
|
size_t offset;
|
|
|
|
bool matched = false;
|
|
|
|
int read;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
|
|
|
file = get_file_s(path, "r");
|
|
|
|
if (file == NULL) return false;
|
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
while ((read = getline(&buf, &offset, file)) > 0) {
|
|
|
|
char* save = buf;
|
2023-05-04 20:10:37 +00:00
|
|
|
if (buf[read-1] == '\n') buf[read-1] = '\0';
|
2023-05-04 16:24:47 +00:00
|
|
|
if (match(&save, pattern)) {
|
|
|
|
matched = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf != NULL) free(buf);
|
|
|
|
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool match_file(char* path, void* pattern, bool many) {
|
2023-05-04 20:10:37 +00:00
|
|
|
FILE* file;
|
2023-05-04 16:24:47 +00:00
|
|
|
char* buf = NULL;
|
|
|
|
size_t offset;
|
2023-05-04 20:10:37 +00:00
|
|
|
int num_matched = 0;
|
|
|
|
int line_num = 0;
|
2023-05-04 16:24:47 +00:00
|
|
|
int read;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
|
|
|
file = get_file_s(path, "r");
|
|
|
|
if (file == NULL) return false;
|
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
while((read = getline(&buf, &offset, file)) > 0) {
|
|
|
|
char* matched = buf;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
|
|
|
if (buf[read-1] == '\n') buf[read-1] = '\0';
|
2023-05-04 16:24:47 +00:00
|
|
|
line_num++;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
|
|
|
if (!match(&matched, pattern)) {
|
2023-05-04 16:24:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-05-04 20:10:37 +00:00
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
num_matched++;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
if (flags.only_line_count || flags.quiet) continue;
|
2023-05-04 20:10:37 +00:00
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
if ((many || flags.filename_prefix) && !flags.never_file_prefix) {
|
|
|
|
print_file_path(path);
|
|
|
|
putchar(':');
|
|
|
|
}
|
2023-05-04 20:10:37 +00:00
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
if (flags.line_number) {
|
|
|
|
printf("%d:", line_num);
|
|
|
|
}
|
2023-05-04 20:10:37 +00:00
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
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) {
|
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
int start, i;
|
|
|
|
char* pattern;
|
|
|
|
bool many, ok;
|
|
|
|
void* compiled;
|
|
|
|
|
2023-05-04 16:24:47 +00:00
|
|
|
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;
|
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
start = parse_args(argc, argv, help, short_arg, NULL);
|
2023-05-04 16:24:47 +00:00
|
|
|
|
|
|
|
if (argc - start < 1) global_help(help);
|
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
pattern = argv[start++];
|
2023-05-04 16:24:47 +00:00
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
many = argc - start > 0;
|
|
|
|
ok = false;
|
2023-05-04 16:24:47 +00:00
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
compiled = compile(pattern);
|
2023-05-04 16:24:47 +00:00
|
|
|
if (run_match("-", compiled, many)) ok = true;
|
|
|
|
|
2023-05-04 20:10:37 +00:00
|
|
|
for (i = start; i < argc; i++) {
|
2023-05-04 16:24:47 +00:00
|
|
|
if (run_match(argv[i], compiled, many)) ok = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags.quiet) {
|
|
|
|
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
} else {
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|