From 1676ad3c902d600c3aabdcd5817b02fffa655f38 Mon Sep 17 00:00:00 2001 From: Robby Zambito Date: Sun, 3 Oct 2021 21:18:31 -0400 Subject: Completed major functionality from inspiration program. All the major features from the original Rust program should now work. Created a "process_todo_file" function which minimizes code repetition by iterating over the TODO file in a flexible way. It accepts functions as parameters to achieve the needed flexibility. --- src/todo.c | 243 +++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 156 insertions(+), 87 deletions(-) diff --git a/src/todo.c b/src/todo.c index bb38b62..88def16 100644 --- a/src/todo.c +++ b/src/todo.c @@ -19,111 +19,114 @@ #define _GNU_SOURCE #include -#include +#include #include #include #include +#include +#include -static FILE *todo_file; - -static void puts_strikethrough(char *text) { - static const char *strike_overlay = "\u0336"; - if (text != NULL) { - while (*text != '\0') { - printf("%s%c", strike_overlay, *text); - text++; - } - puts(""); - } -} +#define STREQ(a, b) (strcmp(a, b) == 0) -static void apply_func_to_line(void (*func)(char *), size_t index) { - char *line = NULL; - size_t line_len = 0; +static FILE *todo_file; +static void open_todo_file(char *mode) { errno = 0; - todo_file = fopen("TODO", "r"); + todo_file = fopen("TODO", mode); if (todo_file == NULL) { perror("Could not open the TODO file"); exit(EXIT_FAILURE); } - - errno = 0; - FILE *temp_file = fopen(".TODO", "w"); - if (temp_file == NULL) { - perror("Could not open temp TODO file"); - exit(EXIT_FAILURE); - } - - for (size_t i = 1; getline(&line, &line_len, todo_file) >= 0; i++) { - if (i == index) { - func(line); - } - fprintf(temp_file, "%s", line); - free(line); - } - - fclose(todo_file); - fclose(temp_file); - - // Clobber old TODO with new .TODO - rename(".TODO", "TODO"); } -void todo_list(void) { - static const char *bold_text = "\e[1m"; - static const char *regular_text = "\e[0m"; +/* Assumes the file is open and writeable. + * Assumes the line already has a newline if needed. + */ +static void write_line_to_file(char *line, ssize_t line_len, FILE *file, + size_t ignore) { + fprintf(file, "%s", line); +} +static void process_todo_file(void (*mapper_func)(char *), + size_t *mapped_indices, + size_t mapped_indices_length, + void (*process_func)(char *line, ssize_t line_len, + FILE *output, size_t index), + bool is_write) { char *line = NULL; size_t line_buff_size = 0; ssize_t line_len = 0; - errno = 0; - todo_file = fopen("TODO", "r"); - if (todo_file == NULL) { - if (errno == ENOENT) { - // If the file doesn't exist, the list is simply empty - exit(EXIT_SUCCESS); - } else { - perror("Could not open TODO file"); + FILE *temp_file; + + open_todo_file("r"); + + // Instead of writing directly to the TODO file, we write to another file and + // clobber it at the end. + if (is_write) { + errno = 0; + temp_file = fopen(".TODO", "w"); + if (temp_file == NULL) { + perror("Could not open temp TODO file"); exit(EXIT_FAILURE); } } - for (size_t i = 0; + for (size_t i = 1, j = 0; (line_len = getline(&line, &line_buff_size, todo_file)) >= 0; i++) { - // Remove trailing newline by setting it to NULL char. - line[line_len - 1] = '\0'; - if (line_len > 5) { - // Print the index in bold - printf("%s%zu%s ", bold_text, i + 1, regular_text); - - // Print the task with a strikethrough if completed - if (line[1] == ' ') { - puts(line + 4); - } else if (line[1] == '*') { - puts_strikethrough(line + 4); + if (j < mapped_indices_length && i == mapped_indices[j]) { + j++; + mapper_func(line); } + process_func(line, line_len, temp_file, i); } } free(line); fclose(todo_file); + if (is_write) { + fclose(temp_file); + + // Clobber old TODO with new .TODO + rename(".TODO", "TODO"); + } +} + +static void print_strikethrough(char *text) { + static const char *strike_overlay = "\u0336"; + if (text != NULL) { + while (*text != '\n') { + printf("%s%c", strike_overlay, *text); + text++; + } + puts(""); + } +} + +static void print_list(char *line, ssize_t line_len, FILE *ignored, + size_t index) { + static const char *bold_text = "\e[1m"; + static const char *regular_text = "\e[0m"; + + printf("%s%zu%s ", bold_text, index, regular_text); + + if (line[1] == ' ') { + printf("%s", line + 4); + } else if (line[1] == '*') { + print_strikethrough(line + 4); + } } +void todo_list(void) { process_todo_file(NULL, NULL, 0, print_list, false); } + void todo_add(int argc, char **argv) { if (argc < 1) { fprintf(stderr, "todo add takes at least 1 argument\n"); exit(EXIT_FAILURE); } - errno = 0; - todo_file = fopen("TODO", "a"); - if (todo_file == NULL) { - perror("Could not open the TODO file"); - exit(EXIT_FAILURE); - } + open_todo_file("a"); for (int i = 0; i < argc; i++) { fprintf(todo_file, "[ ] %s\n", argv[i]); @@ -132,6 +135,21 @@ void todo_add(int argc, char **argv) { fclose(todo_file); } +static size_t *parse_indices(int argc, char **argv) { + size_t *res = calloc(sizeof(size_t), argc); + + for (size_t i = 0; i < argc; i++) { + errno = 0; + sscanf(argv[i], "%zu", &(res[i])); + if (errno != 0 || res[i] < 1) { + perror("Could not parse index"); + exit(EXIT_FAILURE); + } + } + + return res; +} + static void blank(char *task) { task[0] = '\0'; } void todo_rm(int argc, char **argv) { @@ -140,43 +158,94 @@ void todo_rm(int argc, char **argv) { exit(EXIT_FAILURE); } - size_t remove_index = -1; + size_t *remove_indices = parse_indices(argc, argv); + process_todo_file(blank, remove_indices, argc, write_line_to_file, true); + free(remove_indices); +} - errno = 0; - sscanf(argv[0], "%zu", &remove_index); - if (errno != 0 || remove_index < 1) { - perror("Could not parse index"); - exit(EXIT_FAILURE); +static void toggle_done(char *task) { + if (task[1] == ' ') { + task[1] = '*'; + } else if (task[1] == '*') { + task[1] = ' '; } - - apply_func_to_line(blank, remove_index); } -static void done(char *task) { task[1] = '*'; } - void todo_done(int argc, char **argv) { if (argc < 1) { fprintf(stderr, "todo done takes at least 1 argument\n"); exit(EXIT_FAILURE); } - size_t done_index = -1; + size_t *done_indices = parse_indices(argc, argv); + process_todo_file(toggle_done, done_indices, argc, write_line_to_file, true); + free(done_indices); +} + +static bool todo_raw_is_done = false; +static bool todo_raw_is_todo = false; - errno = 0; - sscanf(argv[0], "%zu", &done_index); - if (errno != 0 || done_index < 1) { - perror("Could not parse index"); +static void print_raw(char *line, ssize_t line_len, FILE *ignored, + size_t index) { + if ((todo_raw_is_done && line[1] == '*') || + (todo_raw_is_todo && line[1] == ' ')) { + printf("%s", line); + } +} + +void todo_raw(int argc, char **argv) { + if (argc > 1) { + fprintf(stderr, "todo raw takes only 1 argument\n"); + exit(EXIT_FAILURE); + } else if (argc < 1) { + fprintf(stderr, "todo raw takes 1 argument (done/todo)\n"); exit(EXIT_FAILURE); } - apply_func_to_line(done, done_index); -} + todo_raw_is_done = STREQ(argv[0], "done"); + todo_raw_is_todo = STREQ(argv[0], "todo"); + + if (!todo_raw_is_done && !todo_raw_is_todo) { + fprintf(stderr, "todo raw takes 1 argument (done/todo)\n"); + exit(EXIT_FAILURE); + } -void todo_raw(int argc, char **argv) { fclose(todo_file); } + process_todo_file(NULL, NULL, 0, print_raw, false); +} void todo_sort(void) { - puts("sort"); - fclose(todo_file); + + pid_t pid = fork(); + + if (pid == 0) { + open_todo_file("r"); + int input_fd = fileno(todo_file); + + errno = 0; + FILE *temp_file = fopen(".TODO", "w"); + if (temp_file == NULL) { + perror("Could not open temp TODO file"); + exit(EXIT_FAILURE); + } + + int output_fd = fileno(temp_file); + + dup2(input_fd, STDIN_FILENO); + dup2(output_fd, STDOUT_FILENO); + + setenv("LC_ALL", "C", true); + + char *args[] = {"sort", NULL}; + execvp("sort", args); + } + + int exit_status = 0; + wait(&exit_status); + if (exit_status != 0) { + exit(EXIT_FAILURE); + } + + rename(".TODO", "TODO"); } void todo_help(void) { -- cgit v1.2.3