/
solidbase
/
C2_SimpleBashUtils
Обзор
Документация
Войти
/
solidbase
/
C2_SimpleBashUtils
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/grep/process.c
370 строк
9 KB
Dmitrii Isaev
refactor(grep): decompose process.c and options.c per structural programming principles
02 дек 2025, 11:05
02 дек 2025, 11:05
9a23220
Код
Авторство
О чём код?
#include "process.h" #include <errno.h> #include <regex.h> #include <stdio.h> #include <string.h> typedef struct { regex_t* regs; int regex_count; unsigned long long line_no; unsigned long long match_lines; bool matched_any; } GrepState; typedef struct { const char* path; const Options* opts; bool show_filename; } GrepConfig; static bool compile_patterns(const Options* opts, regex_t* regs, int* regex_count) { bool success = true; int cflags = REG_NEWLINE; if (opts->flag_i) { cflags |= REG_ICASE; } *regex_count = 0; for (int i = 0; i < opts->pattern_count && success; ++i) { int rc = regcomp(®s[i], opts->patterns[i], cflags); if (rc != 0) { if (!opts->flag_s) { char errbuf[256]; regerror(rc, ®s[i], errbuf, sizeof errbuf); fprintf(stderr, "s21_grep: %s\n", errbuf); } success = false; } else { *regex_count = *regex_count + 1; } } return success && (*regex_count > 0); } static void free_patterns(regex_t* regs, int regex_count) { for (int i = 0; i < regex_count; ++i) { regfree(®s[i]); } } static bool regex_any_match(const char* line, const regex_t* regs, int regex_count) { bool matched = false; for (int i = 0; i < regex_count && !matched; ++i) { int rc = regexec(®s[i], line, 0, NULL, 0); if (rc == 0) { matched = true; } } return matched; } static void trim_newline(char* line) { size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; } } static bool output_prefix(const char* path, const Options* opts, bool show_filename, unsigned long long line_no) { if (show_filename) { if (opts->flag_n) { if (printf("%s:%llu:", path, line_no) < 0) { return false; } } else { if (printf("%s:", path) < 0) { return false; } } } else if (opts->flag_n) { if (printf("%llu:", line_no) < 0) { return false; } } return true; } static bool print_matched_line(const char* path, const Options* opts, bool show_filename, unsigned long long line_no, const char* line) { if (!output_prefix(path, opts, show_filename, line_no)) { return false; } if (fputs(line, stdout) == EOF) { return false; } if (putchar('\n') == EOF) { return false; } return true; } static bool print_single_match(const char* path, const Options* opts, bool show_filename, unsigned long long line_no, const char* line, size_t from, size_t to) { if (!output_prefix(path, opts, show_filename, line_no)) { return false; } size_t length = to - from; if (fwrite(line + from, 1, length, stdout) != length) { return false; } if (putchar('\n') == EOF) { return false; } return true; } static bool find_best_match(const regex_t* regs, int regex_count, const char* line, size_t offset, regmatch_t* best_match, int* best_index) { *best_index = -1; for (int i = 0; i < regex_count; ++i) { regmatch_t m; int rc = regexec(®s[i], line + offset, 1, &m, 0); if (rc != 0 || m.rm_so == m.rm_eo) { continue; } if (*best_index == -1 || m.rm_so < best_match->rm_so) { *best_match = m; *best_index = i; } } return (*best_index != -1); } static bool print_o_matches(const char* path, const Options* opts, bool show_filename, unsigned long long line_no, const regex_t* regs, int regex_count, const char* line) { if (regex_count <= 0) { return true; } size_t len = strlen(line); size_t offset = 0; while (offset <= len) { regmatch_t best_match; int best_index; if (!find_best_match(regs, regex_count, line, offset, &best_match, &best_index)) { break; } size_t from = offset + (size_t)best_match.rm_so; size_t to = offset + (size_t)best_match.rm_eo; if (!print_single_match(path, opts, show_filename, line_no, line, from, to)) { return false; } offset += (size_t)best_match.rm_eo; } return true; } static bool should_output_match(const Options* opts, bool matched_line) { bool selected = matched_line; if (opts->flag_v) { selected = !selected; } return selected; } static bool should_break_on_match(const Options* opts) { return opts->flag_l && !opts->flag_c && !opts->flag_o; } static bool should_output_line_now(const Options* opts) { return !opts->flag_c && !opts->flag_l; } static bool output_matched_line(const char* path, const Options* opts, bool show_filename, unsigned long long line_no, const regex_t* regs, int regex_count, const char* line) { if (opts->flag_o) { if (!opts->flag_v) { return print_o_matches(path, opts, show_filename, line_no, regs, regex_count, line); } return true; } return print_matched_line(path, opts, show_filename, line_no, line); } static bool process_matched_line(const GrepConfig* cfg, GrepState* state, const char* line, bool* should_break) { state->matched_any = true; state->match_lines++; if (should_break_on_match(cfg->opts)) { *should_break = true; return true; } if (should_output_line_now(cfg->opts)) { return output_matched_line(cfg->path, cfg->opts, cfg->show_filename, state->line_no, state->regs, state->regex_count, line); } return true; } static bool process_single_line(const GrepConfig* cfg, GrepState* state, char* line, bool* should_break) { state->line_no++; trim_newline(line); bool matched_line = regex_any_match(line, state->regs, state->regex_count); if (should_output_match(cfg->opts, matched_line)) { return process_matched_line(cfg, state, line, should_break); } return true; } static FILE* open_grep_file(const char* path, const Options* opts) { FILE* fp = fopen(path, "r"); if (!fp && !opts->flag_s) { fprintf(stderr, "s21_grep: %s: %s\n", path, strerror(errno)); } return fp; } static bool check_read_error(FILE* fp, const char* path, const Options* opts) { if (ferror(fp)) { if (!opts->flag_s) { fprintf(stderr, "s21_grep: %s: read error\n", path); } return false; } return true; } static bool close_grep_file(FILE* fp) { return fclose(fp) == 0; } static bool output_filename_only(const char* path) { return printf("%s\n", path) >= 0; } static bool output_count(const char* path, bool show_filename, unsigned long long count) { if (show_filename) { return printf("%s:%llu\n", path, count) >= 0; } return printf("%llu\n", count) >= 0; } static bool output_summary(const char* path, const Options* opts, bool show_filename, const GrepState* state) { if (opts->flag_l) { if (state->matched_any) { return output_filename_only(path); } } else if (opts->flag_c) { return output_count(path, show_filename, state->match_lines); } return true; } static bool grep_file_content(FILE* fp, const GrepConfig* cfg, GrepState* state) { char buf[4096]; bool success = true; while (success && fgets(buf, sizeof buf, fp)) { bool should_break = false; if (!process_single_line(cfg, state, buf, &should_break)) { success = false; } if (should_break) { break; } } if (success) { success = check_read_error(fp, cfg->path, cfg->opts); } return success; } static void init_grep_state(GrepState* state, regex_t* regs, int regex_count) { state->regs = regs; state->regex_count = regex_count; state->line_no = 0; state->match_lines = 0; state->matched_any = false; } bool grep_file(const char* path, const Options* opts, bool show_filename, bool* matched_any) { bool success = false; FILE* fp = NULL; regex_t regs[MAX_PATTERNS]; int regex_count = 0; GrepState state; GrepConfig cfg; if (!path || !opts || !matched_any) { return false; } *matched_any = false; if (!compile_patterns(opts, regs, ®ex_count)) { free_patterns(regs, regex_count); return false; } fp = open_grep_file(path, opts); if (!fp) { free_patterns(regs, regex_count); return false; } cfg.path = path; cfg.opts = opts; cfg.show_filename = show_filename; init_grep_state(&state, regs, regex_count); success = grep_file_content(fp, &cfg, &state); if (success && close_grep_file(fp)) { success = output_summary(path, opts, show_filename, &state); *matched_any = state.matched_any; } else { success = false; } free_patterns(regs, regex_count); return success; }