diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 198 |
1 files changed, 118 insertions, 80 deletions
@@ -25,6 +25,7 @@ #include <limits.h> #include <locale.h> #include <ncurses.h> +#include <signal.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> @@ -72,20 +73,30 @@ const int64_t SECONDS_IN_YEAR = (int64_t)365*SECONDS_IN_DAY; const size_t MAX_DATABASE_TASKS = (PTRDIFF_MAX < (SIZE_MAX / SIZEOF_TASK_ST)) ? PTRDIFF_MAX : (SIZE_MAX / SIZEOF_TASK_ST); -database_st database = { .tasks = NULL }; -database_st archive = { .tasks = NULL }; -database_st *db = NULL; -char *app_folder = NULL; -char *db_file_path = NULL; -char *ar_file_path = NULL; -char *string_buffer = NULL; -size_t string_buffer_size = 0; +database_st database = { .tasks = NULL }; +database_st archive = { .tasks = NULL }; +database_st *db = NULL; +bool is_autosave_enabled = true; +int countdown_to_autosave = -1; +char *app_folder = NULL; +char *db_file_path = NULL; +char *ar_file_path = NULL; +char *string_buffer = NULL; +size_t string_buffer_size = 0; int size_x, size_y, pos_x, pos_y; void inline static clear_string_buffer() { memset(string_buffer, 0, string_buffer_size); } +void inline static trigger_autosave() { + countdown_to_autosave = 13375; // ms +} + +void inline static show_processing() { + mvaddch(0, 0, ACS_DIAMOND); + refresh(); +} // Checks if file is exists and is accessible. // Returns true when the file exists and is accessible. @@ -247,10 +258,12 @@ task_st *get_selected_task(database_st *db) { } -// Creates new task returned in the pointer. If necessary, expands database capacity. +// Creates new task stored at location given by task pointer. +// If necessary, expands database capacity. // Returns success. bool create_task(database_st *db, task_st **task) { assert(db != NULL); + assert(task != NULL); if (db->count == MAX_DATABASE_TASKS) { fprintf(stderr, "Database reached maximum capacity.\n"); @@ -288,9 +301,9 @@ bool create_task(database_st *db, task_st **task) { return true; } -// Adds the given task to the database using (using create_task and memcpy). +// Duplicates the given task. Duplicated task is appended to the database. // Returns success. -bool add_task(database_st *db, task_st *task) { +bool duplicate_task(database_st *db, task_st *task) { assert(db != NULL); assert(task != NULL); @@ -301,16 +314,17 @@ bool add_task(database_st *db, task_st *task) { memcpy(new_task, task, SIZEOF_TASK_ST); - // Add task timer values to total timers. + // Add task time values to total times. for (int idx = 0; idx < NUM_WEEK_DAYS; idx++) { // db->total_times[idx] += task->times[idx]; TODO - db->total_times[idx] = add_int64(db->total_times[idx], task->times[idx]); + db->total_times[idx] = add_int64(db->total_times[idx], new_task->times[idx]); } return true; } -// Deletes the provided task. If possible, shrinks the database capacity. +// Deletes the provided task. +// If possible, shrinks the database capacity. // Returns success. bool delete_task(database_st *db, task_st *task) { assert(db != NULL); @@ -531,33 +545,6 @@ bool store_database(const database_st *db, const char *path) { return true; } -// Writes only the database core structure and the provided task if not null. -// Returns success. -bool store_database_partial(const database_st *db, const task_st *task, const char *path) { - assert(db != NULL); - assert(path != NULL); - - // Open file. - FILE *file = fopen(path, "r+b"); - if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while partially storing database: %s.\n", path, strerror(errno)); - return false; - } - - fseek(file, DB_FILE_SIGN_LENGTH, SEEK_SET); - fwrite(db, SIZEOF_DATABASE_ST, 1, file); - - if (task != NULL) { - assert(task >= db->tasks && task < &db->tasks[db->count]); - ptrdiff_t offset = task - db->tasks; - fseek(file, offset * SIZEOF_TASK_ST, SEEK_CUR); - fwrite(task, SIZEOF_TASK_ST, 1, file); - } - - fclose(file); - return true; -} - // Loads data from binary file into database. // Returns success. bool load_database(database_st *db, const char *path) { @@ -1100,6 +1087,11 @@ bool initialize_app_folder() { return true; } +void exit_gracefully(int signal) { + flushinp(); + ungetch('q'); +} + int main(int argc, char *argv[]) { if (initialize_app_folder() == false) { @@ -1121,58 +1113,83 @@ int main(int argc, char *argv[]) { if (argc > 1) { char *action; - bool do_action = false; - for (int idx = 1; idx < argc; idx++) { + bool is_action = false; + bool is_exit_requested = false; + for (unsigned idx = 1; idx < argc; idx++) { action = "--help"; - do_action = strncmp(argv[idx], action, strlen(action)+1) == 0; - if (do_action) { + is_action = strncmp(argv[idx], action, strlen(action)+1) == 0; + if (is_action) { fprintf(stdout, "TO BE IMPLEMENTED\n"); // TODO return EXIT_SUCCESS; } action = "--version"; - do_action = strncmp(argv[idx], action, strlen(action)+1) == 0; - if (do_action) { - fprintf(stdout, "Task Time Tracker " VERSION "\n"); + is_action = strncmp(argv[idx], action, strlen(action)+1) == 0; + if (is_action) { + fprintf(stdout, "Task Time Tracker v" VERSION "\n"); free_memory(); return EXIT_SUCCESS; } - action = "--icsv"; - do_action = strncmp(argv[idx], action, strlen(action)+1) == 0; - if (do_action) { - if (idx+1 >= argc) { + action = "--import-csv"; + is_action = strncmp(argv[idx], action, strlen(action)+1) == 0; + if (is_action) { + idx++; + if (idx >= argc) { fprintf(stdout, "Missing CSV file path to import.\n"); + free_memory(); return EXIT_FAILURE; } + // TODO No error checking... oh noes! load_database(&database, db_file_path); - import_from_csv(&database, argv[idx+1]); + import_from_csv(&database, argv[idx]); store_database(&database, db_file_path); - free_memory(); - return EXIT_SUCCESS; + reset_database(&database); + is_exit_requested = true; + continue; } - action = "--ecsv"; - do_action = strncmp(argv[idx], action, strlen(action)+1) == 0; - if (do_action) { - if (idx+1 >= argc) { + action = "--export-csv"; + is_action = strncmp(argv[idx], action, strlen(action)+1) == 0; + if (is_action) { + idx++; + if (idx >= argc) { fprintf(stdout, "Missing CSV file path to export.\n"); + free_memory(); return EXIT_FAILURE; } + // TODO No error checking... oh noes! load_database(&database, db_file_path); - export_to_csv(&database, argv[idx+1]); - free_memory(); - return EXIT_SUCCESS; + export_to_csv(&database, argv[idx]); + reset_database(&database); + is_exit_requested = true; + continue; + } + + action = "--no-autosave"; + is_action = strncmp(argv[idx], action, strlen(action)+1) == 0; + if (is_action) { + is_autosave_enabled = false; + continue; } + + fprintf(stdout, "Unkown option '%s'.\nUse '%s --help' for list of options.\n", argv[idx], argv[0]); + return EXIT_FAILURE; } - fprintf(stdout, "Unkown command '%s'.\nUse '%s --help' for list of commands.\n", argv[1], argv[0]); - return EXIT_FAILURE; + if (is_exit_requested) { + return EXIT_SUCCESS; + } } initialize_tui(); + signal(SIGTERM, exit_gracefully); + signal(SIGINT, exit_gracefully); + signal(SIGQUIT, exit_gracefully); + signal(SIGHUP, exit_gracefully); + load_database(&database, db_file_path); flushinp(); @@ -1192,6 +1209,16 @@ int main(int argc, char *argv[]) { // When getch() times out. case ERR: { + if (is_autosave_enabled && countdown_to_autosave > 0) { + countdown_to_autosave -= INPUT_TIMEOUT_MS; + if (countdown_to_autosave <= 0) { + show_processing(); + if (db == &archive) { + export_to_csv(&archive, ar_file_path); + } + store_database(&database, db_file_path); + } + } break; } @@ -1228,8 +1255,7 @@ int main(int argc, char *argv[]) { selected_task = new_task; db->selected_task = selected_task - db->tasks; - // TODO - store_database_partial(db, selected_task, db_file_path); + trigger_autosave(); // Force rename action. flushinp(); @@ -1255,6 +1281,7 @@ int main(int argc, char *argv[]) { mvgetnstr(selected_task_row, 1, string_buffer, MAX_TASK_NAME-1); noecho(); curs_set(0); + attrset(A_NORMAL); // Apply new task name. if (is_empty_string(string_buffer) == false) { @@ -1263,9 +1290,11 @@ int main(int argc, char *argv[]) { replace_char(string_buffer, '\f', ' '); replace_char(string_buffer, '\r', ' '); memcpy(selected_task->name, string_buffer, MAX_TASK_NAME); + + trigger_autosave(); } - attrset(A_NORMAL); + break; } @@ -1286,6 +1315,8 @@ int main(int argc, char *argv[]) { if (getch() == '\n') { reset_task_times(db, selected_task); + + trigger_autosave(); } break; @@ -1397,7 +1428,7 @@ int main(int argc, char *argv[]) { // Adust time. set_task_time(db, selected_task, day, time); - store_database_partial(db, selected_task, db_file_path); + trigger_autosave(); break; } @@ -1419,6 +1450,8 @@ int main(int argc, char *argv[]) { if (getch() == '\n') { delete_task(db, selected_task); + + trigger_autosave(); } break; } @@ -1460,6 +1493,8 @@ int main(int argc, char *argv[]) { input; move_task(db, selected_task, target); + trigger_autosave(); + break; } @@ -1507,12 +1542,14 @@ int main(int argc, char *argv[]) { break; } - add_task(db, selected_task); + duplicate_task(db, selected_task); + trigger_autosave(); break; } case KEY_F(5): { update_total_times(db); + trigger_autosave(); break; } @@ -1538,7 +1575,8 @@ int main(int argc, char *argv[]) { db->active_task = next_task - db->tasks; } db->modified_on = time(NULL); - store_database(db, db_file_path); + active_task = get_active_task(db); + trigger_autosave(); break; } @@ -1563,7 +1601,7 @@ int main(int argc, char *argv[]) { } append_to_csv(selected_task, ar_file_path); delete_task(db, selected_task); - // TODO Maybe save stuff? Shoulw we? + trigger_autosave(); break; } @@ -1572,9 +1610,9 @@ int main(int argc, char *argv[]) { if (db != &archive || selected_task == NULL) { break; } - add_task(&database, selected_task); + duplicate_task(&database, selected_task); delete_task(db, selected_task); - // TODO Maybe save stuff? Shoulw we? + trigger_autosave(); break; } @@ -1641,17 +1679,17 @@ int main(int argc, char *argv[]) { timeout(INPUT_TIMEOUT_MS); } - update_times(&database); - store_database(&database, db_file_path); -// task_st *active_task = get_active_task(&database); -// if (active_task != NULL) { -// update_times(&database); -// store_database_partial(&database, active_task, db_file_path); -// } + // Save any unsaved changes. + show_processing(); if (db == &archive) { export_to_csv(&archive, ar_file_path); } + if (countdown_to_autosave > 0 || is_autosave_enabled == false) { + store_database(&database, db_file_path); + } + free_memory(); endwin(); + return EXIT_SUCCESS; } |
