diff --git a/lab_2/vlad/Makefile b/lab_2/vlad/Makefile deleted file mode 100644 index f1b28f8..0000000 --- a/lab_2/vlad/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -# Makefile - -CC = gcc -CFLAGS = -Wall -Wextra -O2 -PICFLAGS = -fPIC - -.PHONY: all dynamic static test-dynamic test-static clean - -all: dynamic static - -# --- Dynamic (shared) build --- -dynamic: libtext.so main_d - -libtext.so: lib_d.o - $(CC) -shared -o $@ $^ - -main_d: main_d.o - $(CC) -o $@ $^ -ldl - -lib_d.o: lib_d.c - $(CC) $(CFLAGS) $(PICFLAGS) -c $< -o $@ - -# --- Static build --- -static: libtext.a main_s - -libtext.a: lib_s.o - ar rcs $@ $^ - -main_s: main_s.o libtext.a - $(CC) -o $@ main_s.o libtext.a - -# Generic rule for other .o files -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -# --- Test targets --- -# Creates a small `test_input.txt`, runs the program, and shows `out.txt` -test-dynamic: dynamic - printf "Hello123456789\n!!@@2211122\n" > test_input.txt - ./main_d test_input.txt out.txt 100 ./libtext.so - @echo "---- out.txt ----" - cat out.txt - -test-static: static - printf "Hello123456789\n!!@@2211122\n" > test_input.txt - ./main_s test_input.txt out.txt 100 - @echo "---- out.txt ----" - cat out.txt - -# --- Cleanup --- -clean: - rm -f *.o *.so *.a main_d main_s test_input.txt out.txt diff --git a/lab_2/vlad/lib_d.c b/lab_2/vlad/lib_d.c deleted file mode 100644 index 1d8b32d..0000000 --- a/lab_2/vlad/lib_d.c +++ /dev/null @@ -1,35 +0,0 @@ -#include - -int replace_char(char *buf, int key, int *replacements_left) { - (void)key; - int replaced = 0; - size_t i = 0; - - while (buf[i] != '\0') { - if (buf[i] == '\n') { - i++; - continue; - } - - // Detect start of a symbol run - char symbol = buf[i]; - size_t run_start = i; - size_t run_len = 1; - - // Count length of run - while (buf[i + run_len] == symbol && buf[i + run_len] != '\n' && buf[i + run_len] != '\0') { - run_len++; - } - - // For pairs and longer runs: replace every 2nd, 4th, ... - for (size_t j = 1; j < run_len && *replacements_left > 0; j += 2) { - buf[run_start + j] = ' '; - replaced++; - (*replacements_left)--; - if (*replacements_left == 0) break; - } - - i += run_len; - } - return replaced; -} diff --git a/lab_2/vlad/lib_s.c b/lab_2/vlad/lib_s.c deleted file mode 100644 index 1d8b32d..0000000 --- a/lab_2/vlad/lib_s.c +++ /dev/null @@ -1,35 +0,0 @@ -#include - -int replace_char(char *buf, int key, int *replacements_left) { - (void)key; - int replaced = 0; - size_t i = 0; - - while (buf[i] != '\0') { - if (buf[i] == '\n') { - i++; - continue; - } - - // Detect start of a symbol run - char symbol = buf[i]; - size_t run_start = i; - size_t run_len = 1; - - // Count length of run - while (buf[i + run_len] == symbol && buf[i + run_len] != '\n' && buf[i + run_len] != '\0') { - run_len++; - } - - // For pairs and longer runs: replace every 2nd, 4th, ... - for (size_t j = 1; j < run_len && *replacements_left > 0; j += 2) { - buf[run_start + j] = ' '; - replaced++; - (*replacements_left)--; - if (*replacements_left == 0) break; - } - - i += run_len; - } - return replaced; -} diff --git a/lab_2/vlad/main_d.c b/lab_2/vlad/main_d.c deleted file mode 100644 index 04e4e4b..0000000 --- a/lab_2/vlad/main_d.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -#define MAX_LINE 4096 - -typedef int (*replace_func_t)(char*, int, int*); - -int main(int argc, char *argv[]) { - if (argc != 5) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - - FILE *fin = fopen(argv[1], "r"); - if (!fin) { perror("fopen input"); return 1; } - FILE *fout = fopen(argv[2], "w"); - if (!fout) { perror("fopen output"); fclose(fin); return 1; } - - int cap = atoi(argv[3]); - if (cap < 0) { - fprintf(stderr, "invalid cap\n"); - fclose(fin); - fclose(fout); - return 1; - } - - void *lib = dlopen(argv[4], RTLD_LAZY); - if (!lib) { - fprintf(stderr, "dlopen error: %s\n", dlerror()); - fclose(fin); - fclose(fout); - return 1; - } - - replace_func_t replace = (replace_func_t)dlsym(lib, "replace_char"); - if (!replace) { - fprintf(stderr, "dlsym error: %s\n", dlerror()); - dlclose(lib); - fclose(fin); - fclose(fout); - return 1; - } - - int total = 0; - char line[MAX_LINE]; - - while (fgets(line, sizeof(line), fin)) { - if (cap > 0) { - int key = (unsigned char)line[0]; - int repl_line = replace(line, key, &cap); - total += repl_line; - } - fputs(line, fout); - } - - dlclose(lib); - fclose(fin); - fclose(fout); - printf("total_replacements: %d\n", total); - return 0; -} diff --git a/lab_2/vlad/main_s.c b/lab_2/vlad/main_s.c deleted file mode 100644 index 227a189..0000000 --- a/lab_2/vlad/main_s.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -#define MAX_LINE 4096 - -int replace_char(char *buf, int key, int *replacements_left); - -int main(int argc, char *argv[]) { - if (argc != 4) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - - FILE *fin = fopen(argv[1], "r"); - if (!fin) { perror("fopen input"); return 1; } - FILE *fout = fopen(argv[2], "w"); - if (!fout) { perror("fopen output"); fclose(fin); return 1; } - - int cap = atoi(argv[3]); - if (cap < 0) { - fprintf(stderr, "invalid cap\n"); - fclose(fin); - fclose(fout); - return 1; - } - - int total = 0; - char line[MAX_LINE]; - - while (fgets(line, sizeof(line), fin)) { - if (cap > 0) { - // key is unused, but pass the first byte for symmetry - int key = (unsigned char)line[0]; - int repl_line = replace_char(line, key, &cap); - total += repl_line; - } - fputs(line, fout); - } - - fclose(fin); - fclose(fout); - printf("total_replacements: %d\n", total); - return 0; -} diff --git a/lab_3/README-lab3.md b/lab_3/README-lab3.md deleted file mode 100644 index ea73e79..0000000 --- a/lab_3/README-lab3.md +++ /dev/null @@ -1,231 +0,0 @@ -# Инструкция по сборке и запуску - -## Лабораторная работа №3: Многозадачное программирование в Linux - -### Описание задания -Программа запускает программу из лабораторной работы №1 в нескольких дочерних процессах для обработки множества входных файлов параллельно. - -**Вариант 12**: Замена первого символа каждой строки на пробелы (до достижения лимита замен). - ---- - -## Структура проекта - -``` -lab3/ -├── parent.c # Родительская программа (многозадачная) -├── lab1_var12.c # Программа из лабораторной работы №1 -├── Makefile # Makefile для сборки -└── README.md # Эта инструкция -``` - ---- - -## Сборка проекта - -### 1. Компиляция всех программ -```bash -make -``` - -Будут созданы: -- `parent` — родительская программа -- `lab1_var12` — программа обработки текста из лабы №1 - -### 2. Сборка отдельных программ -```bash -make parent # только родительская программа -make lab1_var12 # только программа из лабы №1 -``` - ---- - -## Использование - -### Формат запуска -```bash -./parent <программа> <вход1> <выход1> [<вход2> <выход2> ...] -``` - -**Параметры:** -- `<программа>` — путь к программе lab1_var12 -- `` — максимальное количество замен символов -- `<вход1>` `<выход1>` — пары входной/выходной файл (можно несколько) - -### Пример 1: Обработка трёх файлов -```bash -./parent ./lab1_var12 10 input1.txt output1.txt input2.txt output2.txt input3.txt output3.txt -``` - -### Пример 2: Обработка одного файла -```bash -./parent ./lab1_var12 5 test.txt result.txt -``` - ---- - -## Автоматические тесты - -### Базовый тест (создание тестовых файлов и запуск) -```bash -make test -``` -Создаёт три тестовых входных файла, запускает обработку и показывает результаты. - -### Тест обработки ошибок: несуществующий входной файл -```bash -make test_error -``` - -### Тест обработки ошибок: неверная программа -```bash -make test_bad_program -``` - -### Тест обработки ошибок: неверные аргументы -```bash -make test_bad_args -``` - ---- - -## Примеры работы программы - -### Успешное выполнение - -``` -$ ./parent ./lab1_var12 10 input1.txt output1.txt input2.txt output2.txt - -=== Запуск родительского процесса === -Родительский PID: 12345 -Программа для запуска: ./lab1_var12 -Максимум замен: 10 -Количество файловых пар: 2 - -Создание процесса 1 для файлов: input1.txt -> output1.txt - -> Дочерний процесс PID=12346 запускает обработку input1.txt -Создание процесса 2 для файлов: input2.txt -> output2.txt - -> Дочерний процесс PID=12347 запускает обработку input2.txt - -=== Ожидание завершения дочерних процессов === - -Процесс PID=12346 завершен (input1.txt -> output1.txt) - Код завершения: 0 - Статус: SUCCESS - -Процесс PID=12347 завершен (input2.txt -> output2.txt) - Код завершения: 0 - Статус: SUCCESS - -=== Итоговая статистика === -Всего запущено процессов: 2 -Успешно завершено: 2 -Завершено с ошибкой: 0 - -ОБЩИЙ СТАТУС: Все процессы завершены успешно -``` - -### Обработка ошибок - -#### 1. Недостаточно аргументов -``` -$ ./parent ./lab1_var12 5 - -ERROR: Недостаточное или неверное количество аргументов -Usage: ./parent [ ...] -Example: ./parent ./lab1_var12 5 in1.txt out1.txt in2.txt out2.txt -``` - -#### 2. Несуществующий входной файл -``` -$ ./parent ./lab1_var12 5 nonexistent.txt output.txt - -=== Запуск родительского процесса === -... -Создание процесса 1 для файлов: nonexistent.txt -> output.txt - -> Дочерний процесс PID=12351 запускает обработку nonexistent.txt -open input failed: nonexistent.txt: No such file or directory - -Процесс PID=12351 завершен (nonexistent.txt -> output.txt) - Код завершения: 255 - Статус: ERROR (не удалось выполнить) - -=== Итоговая статистика === -Всего запущено процессов: 1 -Успешно завершено: 0 -Завершено с ошибкой: 1 - -ОБЩИЙ СТАТУС: Завершено с ошибками -``` - -#### 3. Невозможность запустить программу -``` -$ ./parent ./nonexistent_program 5 input.txt output.txt - -=== Запуск родительского процесса === -... -ERROR: Не удалось запустить программу ./nonexistent_program: No such file or directory - -Процесс PID=12355 завершен (input.txt -> output.txt) - Код завершения: 255 - Статус: ERROR (не удалось выполнить) -``` - ---- - -## Функциональность родительской программы - -### Основные возможности: -1. **Создание дочерних процессов** — используется `fork()` -2. **Запуск программы в дочернем процессе** — используется `execl()` -3. **Ожидание завершения процессов** — используется `waitpid()` -4. **Получение кодов завершения** — макросы `WIFEXITED()`, `WEXITSTATUS()`, `WIFSIGNALED()` -5. **Обработка исключительных ситуаций**: - - Недостаточное количество аргументов - - Невозможность создать процесс (fork failed) - - Невозможность запустить программу (exec failed) - - Ошибки при обработке файлов - -### Коды завершения: -- **0** — успешное выполнение -- **-1 (255)** — ошибка (невозможно открыть файл, неверные аргументы и т.д.) -- **Другие коды** — различные ошибки - ---- - -## Очистка - -```bash -make clean -``` - -Удалит: -- Исполняемые файлы (`parent`, `lab1_var12`) -- Все тестовые входные/выходные файлы - ---- - -## Технические детали - -### Системные вызовы: -- `fork()` — создание дочернего процесса -- `execl()` — запуск программы в дочернем процессе -- `waitpid()` — ожидание завершения дочернего процесса -- `getpid()` — получение PID текущего процесса - -### Обработка статусов: -- `WIFEXITED(status)` — процесс завершился нормально -- `WEXITSTATUS(status)` — код завершения процесса -- `WIFSIGNALED(status)` — процесс завершён сигналом -- `WTERMSIG(status)` — номер сигнала - -### Компилятор: -- GCC версии 4.0 или выше -- Флаги: `-Wall -Wextra -std=c99 -g` - ---- - -## Автор -Лабораторная работа №3 -Системное программирование в Linux -Вариант 12 \ No newline at end of file diff --git a/lab_3/demo_output.txt b/lab_3/demo_output.txt deleted file mode 100644 index ad3f62b..0000000 --- a/lab_3/demo_output.txt +++ /dev/null @@ -1,91 +0,0 @@ - -=== ПРИМЕР ВЫВОДА ПРОГРАММЫ === - -$ make test - -=== Запуск теста с тремя файлами === -./parent ./lab1_var12 10 input1.txt output1.txt input2.txt output2.txt input3.txt output3.txt - -=== Запуск родительского процесса === -Родительский PID: 12345 -Программа для запуска: ./lab1_var12 -Максимум замен: 10 -Количество файловых пар: 3 - -Создание процесса 1 для файлов: input1.txt -> output1.txt - -> Дочерний процесс PID=12346 запускает обработку input1.txt -Создание процесса 2 для файлов: input2.txt -> output2.txt - -> Дочерний процесс PID=12347 запускает обработку input2.txt -Создание процесса 3 для файлов: input3.txt -> output3.txt - -> Дочерний процесс PID=12348 запускает обработку input3.txt - -=== Ожидание завершения дочерних процессов === - -Процесс PID=12346 завершен (input1.txt -> output1.txt) - Код завершения: 0 - Статус: SUCCESS - -Процесс PID=12347 завершен (input2.txt -> output2.txt) - Код завершения: 0 - Статус: SUCCESS - -Процесс PID=12348 завершен (input3.txt -> output3.txt) - Код завершения: 0 - Статус: SUCCESS - -=== Итоговая статистика === -Всего запущено процессов: 3 -Успешно завершено: 3 -Завершено с ошибкой: 0 - -ОБЩИЙ СТАТУС: Все процессы завершены успешно - -=== Результаты обработки === ---- output1.txt --- - bb bb bb bbaabbaabbaabbaabba -xyzxyzxyzxyzxyzxyzxyzxyz -hello world hello - ---- output2.txt --- -testtest -aaaaaaa - ---- output3.txt --- -programming -ppppython - -========================================== -=== ПРИМЕР ОБРАБОТКИ ОШИБОК === - -$ ./parent ./lab1_var12 5 - -ERROR: Недостаточное или неверное количество аргументов -Usage: ./parent [ ...] -Example: ./parent ./lab1_var12 5 in1.txt out1.txt in2.txt out2.txt - -========================================== - -$ ./parent ./lab1_var12 5 nonexistent.txt output.txt - -=== Запуск родительского процесса === -Родительский PID: 12350 -Программа для запуска: ./lab1_var12 -Максимум замен: 5 -Количество файловых пар: 1 - -Создание процесса 1 для файлов: nonexistent.txt -> output.txt - -> Дочерний процесс PID=12351 запускает обработку nonexistent.txt -open input failed: nonexistent.txt: No such file or directory - -=== Ожидание завершения дочерних процессов === - -Процесс PID=12351 завершен (nonexistent.txt -> output.txt) - Код завершения: 255 - Статус: ERROR (не удалось выполнить) - -=== Итоговая статистика === -Всего запущено процессов: 1 -Успешно завершено: 0 -Завершено с ошибкой: 1 - -ОБЩИЙ СТАТУС: Завершено с ошибками diff --git a/lab_3/kirill/Makefile b/lab_3/kirill/Makefile new file mode 100644 index 0000000..c0fd577 --- /dev/null +++ b/lab_3/kirill/Makefile @@ -0,0 +1,55 @@ +# Компилятор и флаги +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 -pedantic + +# Целевые файлы +TARGET_PARENT = parent +TARGET_LAB1 = task18 + +# Исходные файлы +SRC_PARENT = parent.c +SRC_LAB1 = task18.c + +# Объектные файлы +OBJ_PARENT = $(SRC_PARENT:.c=.o) +OBJ_LAB1 = $(SRC_LAB1:.c=.o) + +.PHONY: all clean help test + +all: $(TARGET_PARENT) $(TARGET_LAB1) + +$(TARGET_PARENT): $(OBJ_PARENT) + $(CC) $(CFLAGS) -o $@ $^ + @echo "Родительская программа собрана: $@" + +$(TARGET_LAB1): $(OBJ_LAB1) + $(CC) $(CFLAGS) -o $@ $^ + @echo "Программа лаб. работы №1 собрана: $@" + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(TARGET_PARENT) $(TARGET_LAB1) $(OBJ_PARENT) $(OBJ_LAB1) + @echo "Очистка завершена" + +help: + @echo "Доступные цели:" + @echo " all - собрать все" + @echo " clean - удалить все скомпилированные файлы" + @echo " test - запустить тестирование программы" + +test: all + @echo "Создаем тестовые файлы..." + @echo "22222222222222222222" > input1.txt + @echo "2222222222222222222" >> input1.txt + @echo "22222222222222222222" >> input1.txt + @echo "Test line one in second file" > input2.txt + @echo "Second line in second file" >> input2.txt + @echo "Third line in second file" >> input2.txt + @echo "Запуск parent..." + @./$(TARGET_PARENT) 5 input1.txt output1.txt input2.txt output2.txt + @echo "Содержимое output1.txt:" + @cat output1.txt || echo "Файл output1.txt не найден" + @echo "Содержимое output2.txt:" + @cat output2.txt || echo "Файл output2.txt не найден" diff --git a/lab_3/kirill/parent.c b/lab_3/kirill/parent.c new file mode 100644 index 0000000..5083535 --- /dev/null +++ b/lab_3/kirill/parent.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + // Проверка аргументов командной строки + if (argc < 4) { + const char *usage = + "Usage: parent " + "[ ...]\n" + " max_replacements: максимальное количество замен\n" + " input/output: пары входных и выходных файлов\n"; + fprintf(stderr, "%s", usage); + return -1; + } + + // Проверка четности количества файлов (пары input/output) + if ((argc - 2) % 2 != 0) { + fprintf(stderr, "Error: количество файлов должно быть четным (пары input/output)\n"); + return -1; + } + + const char *max_replacements_arg = argv[1]; + int num_files = (argc - 2) / 2; // количество пар файлов + + printf("Родительский процесс: PID=%d\n", getpid()); + printf("Будет запущено %d дочерних процессов\n", num_files); + printf("Максимальное количество замен: %s\n\n", max_replacements_arg); + + // Массив для хранения PID дочерних процессов + pid_t *child_pids = malloc(num_files * sizeof(pid_t)); + if (child_pids == NULL) { + perror("malloc failed"); + return -1; + } + + // Запуск дочерних процессов + for (int i = 0; i < num_files; i++) { + const char *input_file = argv[2 + i * 2]; + const char *output_file = argv[2 + i * 2 + 1]; + + pid_t pid = fork(); + + if (pid < 0) { + // Ошибка при создании процесса + perror("fork failed"); + + // Ждем завершения уже запущенных процессов + for (int j = 0; j < i; j++) { + int status; + waitpid(child_pids[j], &status, 0); + } + + free(child_pids); + return -1; + + } else if (pid == 0) { + // Код дочернего процесса + printf("Дочерний процесс %d: PID=%d, обработка %s -> %s\n", + i + 1, getpid(), input_file, output_file); + + // Запуск программы из лабораторной работы №1 + execl("./task18", "task18", input_file, output_file, max_replacements_arg, (char *)NULL); + + // Если execl вернул управление, произошла ошибка + fprintf(stderr, "Дочерний процесс %d: execl failed для %s: %s\n", + i + 1, input_file, strerror(errno)); + _exit(-1); + + } else { + // Код родительского процесса + child_pids[i] = pid; + } + } + + // Ожидание завершения всех дочерних процессов + printf("\nРодительский процесс ожидает завершения дочерних процессов...\n\n"); + + for (int i = 0; i < num_files; i++) { + int status; + pid_t terminated_pid = waitpid(child_pids[i], &status, 0); + + if (terminated_pid < 0) { + perror("waitpid failed"); + continue; + } + + // Проверка статуса завершения + if (WIFEXITED(status)) { + int exit_code = WEXITSTATUS(status); + printf("Процесс %d (PID=%d) завершился нормально\n", + i + 1, terminated_pid); + + if (exit_code == 0) { + printf(" Код завершения: %d (успех)\n", exit_code); + } else { + printf(" Код завершения: %d (ошибка)\n", exit_code); + } + } else if (WIFSIGNALED(status)) { + int signal = WTERMSIG(status); + printf("Процесс %d (PID=%d) был прерван сигналом %d\n", + i + 1, terminated_pid, signal); + } else { + printf("Процесс %d (PID=%d) завершился с неизвестным статусом\n", + i + 1, terminated_pid); + } + } + + printf("\nВсе дочерние процессы завершены\n"); + + free(child_pids); + return 0; +} diff --git a/lab_3/kirill/task18.c b/lab_3/kirill/task18.c new file mode 100644 index 0000000..76976b6 --- /dev/null +++ b/lab_3/kirill/task18.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + +#define RBUFSZ 4096 +#define WBUFSZ 4096 + +static void die_perror(const char *what, const char *path, int exit_code) { + int saved = errno; + char msg[512]; + int n; + if (path) { + n = snprintf(msg, sizeof(msg), "%s: %s: %s\n", what, path, strerror(saved)); + } else { + n = snprintf(msg, sizeof(msg), "%s: %s\n", what, strerror(saved)); + } + if (n > 0) (void) write(STDERR_FILENO, msg, (size_t) n); + _exit(exit_code); +} + +static void xwrite_all(int fd, const char *buf, size_t len, const char *path) { + size_t off = 0; + while (off < len) { + ssize_t n = write(fd, buf + off, len - off); + if (n < 0) { + if (errno == EINTR) continue; + die_perror("write failed", path, -1); + } + off += (size_t) n; + } +} + +static long long parse_ll(const char *s) { + char *end = NULL; + errno = 0; + long long v = strtoll(s, &end, 10); + if (errno != 0 || end == s || *end != '\0' || v < 0) { + errno = EINVAL; + return -1; + } + return v; +} + +int main(int argc, char *argv[]) { + if (argc != 4) { + const char *usage = + "Usage: lab1_var_thirds_line \n" + "Replace every third non-newline byte in each line with a space; counter resets after LF.\n"; + (void) write(STDERR_FILENO, usage, strlen(usage)); + return -1; + } + + const char *in_path = argv[1]; + const char *out_path = argv[2]; + long long cap = parse_ll(argv[3]); + if (cap < 0) die_perror("invalid max_replacements", argv[3], -1); + + int in_fd = open(in_path, O_RDONLY); + if (in_fd < 0) die_perror("open input failed", in_path, -1); + + mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + int out_fd = open(out_path, O_CREAT | O_WRONLY | O_TRUNC, perms); + if (out_fd < 0) die_perror("open output failed", out_path, -1); + + char rbuf[RBUFSZ]; + char wbuf[WBUFSZ]; + size_t wlen = 0; + + long long total = 0; + long long col = 0; + int replacing_enabled = 1; + + for (;;) { + ssize_t n = read(in_fd, rbuf, sizeof(rbuf)); + if (n > 0) { + for (ssize_t i = 0; i < n; i++) { + unsigned char c = (unsigned char) rbuf[i]; + + if (c == '\n') { + if (wlen == sizeof(wbuf)) { + xwrite_all(out_fd, wbuf, wlen, out_path); + wlen = 0; + } + wbuf[wlen++] = '\n'; + col = 0; + continue; + } + + unsigned char outc = c; + + col++; + + if (replacing_enabled && (col % 3 == 0) && total < cap) { + outc = ' '; + total++; + if (total == cap) replacing_enabled = 0; + } + + if (wlen == sizeof(wbuf)) { + xwrite_all(out_fd, wbuf, wlen, out_path); + wlen = 0; + } + wbuf[wlen++] = (char) outc; + } + } else if (n == 0) { + if (wlen > 0) { + xwrite_all(out_fd, wbuf, wlen, out_path); + wlen = 0; + } + break; + } else { + if (errno == EINTR) continue; + die_perror("read failed", in_path, -1); + } + } + + if (close(in_fd) < 0) die_perror("close input failed", in_path, -1); + if (close(out_fd) < 0) die_perror("close output failed", out_path, -1); + + char res[64]; + int m = snprintf(res, sizeof(res), "%lld\n", total); + if (m > 0) (void) write(STDOUT_FILENO, res, (size_t) m); + + return 0; +}