summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Zambito <contact@robbyzambito.me>2021-10-03 21:18:31 -0400
committerRobby Zambito <contact@robbyzambito.me>2021-10-03 21:18:31 -0400
commit1676ad3c902d600c3aabdcd5817b02fffa655f38 (patch)
tree8081b0eeb238db3ddea5f7fdd0b43cf0e0dfaf38
parentb780db3979f269549cd0c05d8157b5dd222b58fa (diff)
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.
-rw-r--r--src/todo.c243
1 files 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 <errno.h>
-#include <locale.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
-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) {