From 8066c97c5c6e8fc59222e0c9be2d351c06c5fbc7 Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Thu, 17 Aug 2023 22:27:31 +0300 Subject: [PATCH] Initial commit; add, show, help --- .gitignore | 3 + LICENSE | 9 ++ Makefile | 10 +++ README.md | 33 ++++++++ src/main.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/todo.c | 188 ++++++++++++++++++++++++++++++++++++++++++ src/todo.h | 70 ++++++++++++++++ 7 files changed, 550 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/main.c create mode 100644 src/todo.c create mode 100644 src/todo.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c0002a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +TODOS.bin +clido +.vscode \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e1a6107 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ + The MIT License (MIT) + +Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..730b278 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +all: + gcc -static -O2 -Wall -Werror src/*.c -o clido + +clean: + rm -f clido TODOS.bin + +install: all + + +delete: diff --git a/README.md b/README.md new file mode 100644 index 0000000..850e230 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# clido +## Command Line Interface to DO program + +clido helps to keep "to do" notes on the disk space. + + +## Compilation + +Simply `make` or as simply compile .c files together + + +## Installation + +If you're on Linux - `make install` or simply move the binary to ...TODO!!(figure out where to put the binary) + + +## Usage + +`clido [COMMAND]` + +Commands: + +- `help -> Prints this message and exists` +- `add [todo]... -> Writes a new TODO to a default TODO file` +- `show -> Outputs current TODOs` +- `show-done -> Outputs TODOs which were done previously` +- `done [index OR index1-index2 OR index1,index2] -> Marks specified TODO(s) as done` + + +## License +![GPLv3](https://www.gnu.org/graphics/gplv3-with-text-84x42.png "GPLv3 logo") + +GPLv3 \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7e61987 --- /dev/null +++ b/src/main.c @@ -0,0 +1,237 @@ +/* + The MIT License (MIT) + +Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "todo.h" + + +/* +Closes a todo file and frees up memory allocated to store todos, basically +a clean up function. +*/ +void clean_up(FILE* todo_file, Todo** todos, size_t todo_count, char* todo_file_path) { + fclose(todo_file); + for (size_t i = 0; i < todo_count; i++) { + todo_delete(&todos[i]); + } + free(todo_file_path); +} + + +/* +Opens a TODO file for reading and writing at todo_file_path. If an error occurs - creates a new +file instead. + +Returns EXIT_FAILURE if creating a new file happens to be unsuccessful or one of the arguments are NULL. +*/ +int open_todo_file(const char* todo_file_path, FILE** todo_file) { + if (!todo_file_path || !todo_file) { + return EXIT_FAILURE; + } + + *todo_file = fopen(todo_file_path, "r+"); + if (!(*todo_file)) { + // Create a new one + printf("[INFO] Failed to read TODO file at \"%s\": %s. Creating a new one...\n", todo_file_path, strerror(errno)); + + *todo_file = fopen(todo_file_path, "w+"); + if (!(*todo_file)) { + fprintf(stderr, "[ERR] Failed to create a new TODO file at the same path: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + printf("[INFO] Successfully created a new TODO file\n"); + } + + return EXIT_SUCCESS; +} + + +int main(int argc, char** argv) { + char* todo_file_name = "TODOS.bin"; + char* todo_file_dir_path = "./"; + char* todo_file_path = malloc(sizeof(char) * (strlen(todo_file_dir_path)+strlen(todo_file_name)+1)); + todo_file_path = strcat(todo_file_path, todo_file_dir_path); + todo_file_path = strcat(todo_file_path, todo_file_name); + size_t todo_count = 0; + Todo** todos = NULL; + char* arg = NULL; + FILE* todo_file = NULL; + int i = 0; + + + // Open TODO file + open_todo_file(todo_file_path, &todo_file); + + + if (argc < 2) { + printf( + "A command is required!\ + \nRun clido help to look at usage overview\n" + ); + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_FAILURE; + } + + + // Collect and work out arguments + while (i < argc) { + i++; + arg = argv[i]; + + + // Help command + if (strcmp(arg, "help") == 0 && i == 1) { + printf( + "clido [COMMAND]\ + \n\n[COMMAND]:\ + \nhelp -> Prints this message and exists\ + \nadd [todo] -> Writes a new TODO to a default TODO file\ + \nshow -> Outputs current TODOs\ + \nshow-done -> Outputs TODOs which were done previously\ + \ndone [index OR index1-index2 OR index1,index2] -> Marks specified TODO(s) as done\ + \n" + ); + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_SUCCESS; + } + + + // Add command + if (strcmp(arg, "add") == 0) { + i++; + arg = argv[i]; + if (i >= argc) { + // No actual text was given + fprintf(stderr, "No TODO text was given!\n"); + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_FAILURE; + } + + // Todo text will be everything from now on until the end + size_t todo_text_length = 0; + char* todo_text = NULL; + + for (size_t j = i; j < argc; j++) { + char* todo_text_part = argv[j]; + + todo_text_length += strlen(todo_text_part) + 2; + todo_text = (char*) realloc(todo_text, todo_text_length); + + todo_text = strcat(todo_text, todo_text_part); + todo_text = strcat(todo_text, " "); + } + + Todo* new_todo = todo_new(strlen(todo_text), todo_text); + if (todo_write(todo_file, new_todo) == EXIT_FAILURE) { + fprintf(stderr, "[ERR] Failed to write a new todo: %s\n", strerror(errno)); + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_FAILURE; + } + + printf("Added!\n"); + + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_SUCCESS; + } + + + // Show command + if (strcmp(arg, "show") == 0) { + int result = todos_read(todo_file, &todos, &todo_count); + if (result == EXIT_FAILURE && todo_count == 0) { + printf("No TODOs yet!\n"); + return EXIT_SUCCESS; + } + + for (size_t j = 0; j < todo_count; j++) { + if (todos[j]->done) { + continue; + } + printf("[%ld] %s\n", j, todos[j]->text); + } + + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_SUCCESS; + } + + + // Show-done command + if (strcmp(arg, "show-done") == 0) { + int result = todos_read(todo_file, &todos, &todo_count); + if (result == EXIT_FAILURE && todo_count == 0) { + printf("No TODOs yet!\n"); + return EXIT_SUCCESS; + } + + size_t printed = 0; + for (size_t j = 0; j < todo_count; j++) { + if (!todos[j]->done) { + continue; + } + printf("[%ld] %s\n", j, todos[j]->text); + printed++; + } + + if (printed == 0) { + printf("No TODOs were done yet!\n"); + } + + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_SUCCESS; + } + + + // Done command TODO!!!!! + if (strcmp(arg, "done") == 0) { + int result = todos_read(todo_file, &todos, &todo_count); + if (result == EXIT_FAILURE && todo_count == 0) { + printf("No TODOs yet!\n"); + return EXIT_SUCCESS; + } + + i++; + if (i >= argc) { + // Not one index was specified + // Mark the first one as DONE + + } + arg = argv[i]; + + + for (size_t j = 0; j < todo_count; j++) { + if (todos[j]->done) { + continue; + } + printf("[%ld] %s\n", j, todos[j]->text); + } + + + clean_up(todo_file, todos, todo_count, todo_file_path); + return EXIT_SUCCESS; + } + } + + printf( + "No valid sequence of commands was specified!\ + \nRun clido help to look at usage overview\n" + ); + clean_up(todo_file, todos, todo_count, todo_file_path); + + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/src/todo.c b/src/todo.c new file mode 100644 index 0000000..53c7b01 --- /dev/null +++ b/src/todo.c @@ -0,0 +1,188 @@ +/* + The MIT License (MIT) + +Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "todo.h" + + +size_t file_get_size(FILE* file) { + size_t current_pos = 0; + size_t size = 0; + + if (file) { + current_pos = ftell(file); + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, current_pos, SEEK_SET); + } + + return size; +} + + +/* +Allocates memory to store a new TODO in it. + +Returns a pointer to TODO. +*/ +Todo* todo_new(uint32_t text_len, char* text) { + Todo* new_todo = malloc(sizeof(Todo)); + if (new_todo) { + new_todo->text_len = text_len; + new_todo->text = text; + } + + return new_todo; +} + + +/* +Frees memory allocated for storing a todo. + +Sets pointer to todo to NULL; does nothing if NULL is received +*/ +void todo_delete(Todo** todo) { + if (!todo) { + return; + } else if (!*todo) { + return; + } + + free((*todo)->text); + free(*todo); + + *todo = NULL; + return; +} + + +/* +Writes a provided todo to a file at current position in binary representation. + +Returns EXIT_FAILURE if an error has occured. +*/ +int todo_write(FILE* file, const Todo* todo) { + size_t items_written = 0; + + if (!file) { + return EXIT_FAILURE; + } + + // Append to the end of the file + fseek(file, 0, SEEK_END); + + // Text length + items_written = fwrite(&todo->text_len, sizeof(uint32_t), 1, file); + if (items_written != 1) { + return EXIT_FAILURE; + } + + // Text + items_written = fwrite(todo->text, sizeof(char), todo->text_len, file); + if (items_written != todo->text_len) { + return EXIT_FAILURE; + } + + // Done flag + items_written = fwrite(&todo->done, sizeof(uint8_t), 1, file); + if (items_written != 1) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + +/* +Reads the next TODO in provided file, puts it in todo. + +Returns EXIT_FAILURE if an error has occured. +*/ +int todo_read(FILE* file, Todo* todo) { + size_t read_items = 0; + + if (!file || !todo) { + return EXIT_FAILURE; + } + + // Read text length + read_items = fread(&todo->text_len, 1, sizeof(uint32_t), file); + if (read_items != sizeof(uint32_t)) { + return EXIT_FAILURE; + } + + // Read text + todo->text = malloc(todo->text_len); + read_items = fread(todo->text, 1, todo->text_len, file); + if (read_items != todo->text_len) { + return EXIT_FAILURE; + } + + // Read whether it's been done or not + read_items = fread(&todo->done, 1, sizeof(uint8_t), file); + if (read_items != 1) { + return EXIT_FAILURE; + } + + + if (feof(file)) { + return EXIT_SUCCESS; + } + + return EXIT_SUCCESS; +} + + +/* +Tries to parse TODOs from a given TODO FILE. + +todos is a pointer to the beginning of a future TODO array and todos_read +is the amount of TODOs read from file. + +Returns EXIT_FAILURE if an error occured somewhere. +*/ +int todos_read(FILE* file, Todo*** todos, size_t* todos_read) { + *todos_read = 0; + *todos = NULL; + + if (!file) { + return EXIT_FAILURE; + } + + while (1) { + Todo* read_todo = todo_new(0, ""); + int result = todo_read(file, read_todo); + if (result == EXIT_FAILURE && *todos_read == 0) { + return EXIT_FAILURE; + } else if (result == EXIT_FAILURE && feof(file)) { + break; + } + + *todos_read = (*todos_read) + 1; + *todos = realloc(*todos, sizeof(Todo) * (*todos_read)); + (*todos)[(*todos_read)-1] = read_todo; + } + + return EXIT_SUCCESS; +} + + +//TODO +int todo_mark_done() { + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/todo.h b/src/todo.h new file mode 100644 index 0000000..99a4f0c --- /dev/null +++ b/src/todo.h @@ -0,0 +1,70 @@ +/* + The MIT License (MIT) + +Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +// TODO representation +typedef struct Todo { + uint32_t text_len; + char* text; + uint8_t done; +} Todo; + + +/* +Allocates memory to store a new TODO in it. + +Returns a pointer to TODO. +*/ +Todo* todo_new(uint32_t text_len, char* text); + + +/* +Frees memory allocated for storing a todo. + +Sets pointer to todo to NULL; does nothing if NULL is received. +*/ +void todo_delete(Todo** todo); + + +/* +Writes a provided todo to a file at current position in binary representation. + +Returns EXIT_FAILURE if an error has occured. +*/ +int todo_write(FILE* file, const Todo* todo); + + + +/* +Reads the next TODO in provided file, puts it in todo. + +Returns EXIT_FAILURE if an error has occured. +*/ +int todo_read(FILE* file, Todo* todo); + + +/* +Tries to parse TODOs from a given TODO FILE. + +todos is a pointer to the beginning of a future TODO array and todos_read +is the amount of TODOs read from file. + +Returns EXIT_FAILURE if an error occured somewhere. +*/ +int todos_read(FILE* file, Todo*** todos, size_t* todos_read); + + +//TODO +int todo_mark_done(); \ No newline at end of file