/
solidbase
/
C2_SimpleBashUtils
Обзор
Документация
Войти
/
solidbase
/
C2_SimpleBashUtils
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/cat/process.c
297 строк
6 KB
Dmitrii Isaev
refactor(cat): decompose process.c according to structural programming principles
02 дек 2025, 10:19
02 дек 2025, 10:19
9065e1b
Код
Авторство
О чём код?
#include "process.h" #include <errno.h> #include <stdio.h> #include <string.h> typedef struct { bool number_nonblank; bool number_all; bool squeeze_blank; bool show_ends; bool show_tabs; bool show_nonprinting; } CatConfig; typedef struct { bool is_line_start; int consecutive_newlines; unsigned long long line_counter; } LineState; static bool format_control_char(int ch, char* buf, size_t buf_size) { if (buf_size < 3) { return false; } buf[0] = '^'; buf[1] = (char)(ch + 64); buf[2] = '\0'; return true; } static bool format_delete_char(char* buf, size_t buf_size) { if (buf_size < 3) { return false; } buf[0] = '^'; buf[1] = '?'; buf[2] = '\0'; return true; } static bool format_high_bit_control(int lo, char* buf, size_t buf_size) { if (buf_size < 5) { return false; } buf[0] = 'M'; buf[1] = '-'; buf[2] = '^'; buf[3] = (char)(lo + 64); buf[4] = '\0'; return true; } static bool format_high_bit_delete(char* buf, size_t buf_size) { if (buf_size < 5) { return false; } buf[0] = 'M'; buf[1] = '-'; buf[2] = '^'; buf[3] = '?'; buf[4] = '\0'; return true; } static bool format_high_bit_normal(int lo, char* buf, size_t buf_size) { if (buf_size < 4) { return false; } buf[0] = 'M'; buf[1] = '-'; buf[2] = (char)lo; buf[3] = '\0'; return true; } static bool format_nonprinting_char(int ch, char* buf, size_t buf_size) { if (ch >= 0 && ch < 32 && ch != '\t') { return format_control_char(ch, buf, buf_size); } if (ch == 127) { return format_delete_char(buf, buf_size); } if (ch >= 128) { int lo = ch & 0x7F; if (lo >= 0 && lo < 32) { return format_high_bit_control(lo, buf, buf_size); } if (lo == 127) { return format_high_bit_delete(buf, buf_size); } return format_high_bit_normal(lo, buf, buf_size); } if (buf_size < 2) { return false; } buf[0] = (char)ch; buf[1] = '\0'; return true; } static bool output_line_number(unsigned long long number) { int printed = printf("%6llu\t", number); return printed >= 0; } static bool output_character(int ch, const CatConfig* cfg) { if (ch == '\t' && cfg->show_tabs) { if (putchar('^') == EOF || putchar('I') == EOF) { return false; } return true; } if (cfg->show_nonprinting) { char formatted[8]; if (!format_nonprinting_char(ch, formatted, sizeof(formatted))) { return false; } if (fputs(formatted, stdout) == EOF) { return false; } return true; } if (putchar(ch) == EOF) { return false; } return true; } static bool output_line_ending(const CatConfig* cfg) { if (cfg->show_ends) { if (putchar('$') == EOF) { return false; } } if (putchar('\n') == EOF) { return false; } return true; } static bool should_skip_blank_line(const CatConfig* cfg, const LineState* state) { return cfg->squeeze_blank && state->is_line_start && state->consecutive_newlines >= 1; } static bool process_newline(const CatConfig* cfg, LineState* state) { bool was_line_start = state->is_line_start; if (should_skip_blank_line(cfg, state)) { return true; } if (was_line_start && cfg->number_all) { if (!output_line_number(state->line_counter)) { return false; } state->line_counter++; } if (!output_line_ending(cfg)) { return false; } state->is_line_start = true; if (cfg->squeeze_blank && was_line_start) { state->consecutive_newlines++; } else { state->consecutive_newlines = 0; } return true; } static bool process_regular_char(int ch, const CatConfig* cfg, LineState* state) { state->consecutive_newlines = 0; if (state->is_line_start) { if (cfg->number_nonblank || cfg->number_all) { if (!output_line_number(state->line_counter)) { return false; } state->line_counter++; } state->is_line_start = false; } if (!output_character(ch, cfg)) { return false; } return true; } static bool process_single_char(int ch, const CatConfig* cfg, LineState* state) { if (ch == '\n') { return process_newline(cfg, state); } return process_regular_char(ch, cfg, state); } static FILE* open_file(const char* path) { FILE* fp = fopen(path, "r"); if (!fp) { fprintf(stderr, "cat: %s: %s\n", path, strerror(errno)); } return fp; } static bool close_file(FILE* fp) { if (fclose(fp) != 0) { return false; } return true; } static void init_cat_config(CatConfig* cfg, const Options* opts) { cfg->number_nonblank = (opts->b != 0); cfg->number_all = (opts->n != 0) && !cfg->number_nonblank; cfg->squeeze_blank = (opts->s != 0); cfg->show_ends = (opts->e != 0) || (opts->E != 0); cfg->show_tabs = (opts->t != 0) || (opts->T != 0); cfg->show_nonprinting = (opts->v != 0) || (opts->e != 0) || (opts->t != 0); } static void init_line_state(LineState* state, unsigned long long* line_counter) { state->is_line_start = true; state->consecutive_newlines = 0; state->line_counter = *line_counter; } static void finalize_line_state(LineState* state, unsigned long long* line_counter) { *line_counter = state->line_counter; } static bool process_file_content(FILE* fp, const char* path, const CatConfig* cfg, LineState* state) { int ch; bool success = true; while (success && (ch = fgetc(fp)) != EOF) { if (!process_single_char(ch, cfg, state)) { fprintf(stderr, "cat: write error\n"); success = false; } } if (ferror(fp)) { fprintf(stderr, "cat: %s: read error\n", path); success = false; } return success; } bool print_file(const char* path, const Options* opts, unsigned long long* line_counter) { FILE* fp = NULL; CatConfig cfg; LineState state; bool success = true; if (!path || !opts || !line_counter) { return false; } fp = open_file(path); if (!fp) { return false; } init_cat_config(&cfg, opts); init_line_state(&state, line_counter); success = process_file_content(fp, path, &cfg, &state); if (!close_file(fp)) { success = false; } if (success) { finalize_line_state(&state, line_counter); } return success; }