diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 176 |
1 files changed, 108 insertions, 68 deletions
@@ -37,7 +37,7 @@ #include <sys/types.h> #include <time.h> -#define VERSION "0.01" // Use only 3 chars (to fit layouts). +#define VERSION "1.0" // Use only 3 chars (to fit layouts). #define TASK_NAME_LENGTH 57 // Task name length. #define TASK_NAME_BYTES (TASK_NAME_LENGTH+1) #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0). @@ -111,40 +111,31 @@ time_t error_time_limit = 0; void print_error(const char *format, ...) { va_list args; va_start(args, format); - - if (stdscr != NULL) { // If ncurses is active. - + if (stdscr == NULL || isendwin() == true) { // Not in ncurses mode. + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + } + else { int w_size_x = size_x > 120 ? 120 : size_x - 2; - int w_size_y = 3; + int w_size_y = 4; if (error_window == NULL) { error_window = newwin(w_size_y, w_size_x, (size_y - w_size_y) / 2, (size_x - w_size_x) / 2); + wattron(error_window, COLOR_PAIR(STYLE_ERROR)); + wborder(error_window, ' ', ' ', 0, 0, ACS_HLINE, ACS_HLINE, ACS_HLINE, ACS_HLINE); + mvwprintw(error_window, 0, 1, " Error "); + wmove(error_window, 1, 0); } else { - wresize(error_window, w_size_y, w_size_x); - werase(error_window); + waddch(error_window, ' '); } - - // Apply theme. - wattron(error_window, COLOR_PAIR(STYLE_ERROR)); - - box(error_window, 0, 0); - mvwprintw(error_window, 0, 1, " Error "); - wmove(error_window, 1, 1); vw_printw(error_window, format, args); -// wrefresh(error_window); - error_time_limit = time(NULL) + 30; - - // Reset theme. - wattrset(error_window, A_NORMAL); - } - else { - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + error_time_limit = time(NULL) + 5; } + va_end(args); } -void update_error_window() { +void draw_error_window() { if (error_window == NULL) { return; } @@ -172,8 +163,6 @@ void update_error_window() { wrefresh(error_window); } - - void trigger_autosave() { countdown_to_autosave = 13375; // ms } @@ -368,8 +357,8 @@ 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"); + if (db->count >= MAX_DATABASE_TASKS) { + print_error("Database reached maximum capacity."); return false; } @@ -383,7 +372,7 @@ bool create_task(database_st *db, task_st **task) { task_st *new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_ST); if (new_tasks == NULL) { - fprintf(stderr, "Failed to expand database.\n"); + print_error("Failed to expand database."); return false; } @@ -470,7 +459,7 @@ bool delete_task(database_st *db, task_st *task) { size_t new_capacity = current_capacity >> 1; task_st *new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_ST); if (new_tasks == NULL && new_capacity > 0) { - fprintf(stderr, "Failed to shrink database.\n"); + print_error("Failed to shrink database."); return false; } db->capacity = new_capacity; @@ -644,7 +633,7 @@ bool store_database(const database_st *db, const char *path) { // Open file. FILE *file = fopen(path, "wb"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while storing database: %s.\n", path, strerror(errno)); + print_error("Failed to open file '%s' while storing database: %s.", path, strerror(errno)); return false; } @@ -665,7 +654,7 @@ bool load_database(database_st *db, const char *path) { // Open file. FILE *file = fopen(path, "rb"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while loading database: %s.\n", path, strerror(errno)); + print_error("Failed to open file '%s' while loading database: %s.", path, strerror(errno)); return false; } @@ -673,7 +662,7 @@ bool load_database(database_st *db, const char *path) { char file_signature[DB_FILE_SIGN_LENGTH]; fread(&file_signature, SIZEOF_CHAR, DB_FILE_SIGN_LENGTH, file); if (strncmp(file_signature, DB_FILE_SIGN, DB_FILE_SIGN_LENGTH) != 0) { - fprintf(stderr, "Invalid file signature.\n"); + print_error("Invalid file signature."); fclose(file); return false; } @@ -685,7 +674,7 @@ bool load_database(database_st *db, const char *path) { size_t capacity_bytes = db->capacity * SIZEOF_TASK_ST; db->tasks = malloc(capacity_bytes); if (db->tasks == NULL && capacity_bytes > 0) { - fprintf(stderr, "Failed to allocate memory while loading database: %s.\n", strerror(errno)); + print_error("Failed to allocate memory while loading database: %s.", strerror(errno)); return false; } @@ -707,7 +696,7 @@ bool export_to_csv(const database_st *db, const char *path) { FILE *file = fopen(path, "w"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while exporting to CSV: %s.\n", path, strerror(errno)); + print_error("Failed to open file '%s' while exporting to CSV: %s.", path, strerror(errno)); return false; } @@ -737,7 +726,7 @@ bool import_from_csv(database_st *db, const char *path) { FILE *file = fopen(path, "r"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while importing from CSV: %s.\n", path, strerror(errno)); + print_error("Failed to open file '%s' while importing from CSV: %s.", path, strerror(errno)); return false; } @@ -747,7 +736,6 @@ bool import_from_csv(database_st *db, const char *path) { // Parse CSV file. char *csv_buffer = NULL; size_t csv_buffer_size = 0; - bool success = true; while(getline(&csv_buffer, &csv_buffer_size, file) != -1) { // Check if reached EOF. // Find task name string limits. @@ -762,7 +750,9 @@ bool import_from_csv(database_st *db, const char *path) { // Prepare new task. task_st *task; - success &= create_task(db, &task); + if (create_task(db, &task) == false) { + return false; + } // Import task name. memcpy(task->name, csv_buffer, name_length); @@ -775,7 +765,7 @@ bool import_from_csv(database_st *db, const char *path) { ) != NUM_WEEK_DAYS ) { replace_char(csv_buffer, '\n', ' '); - fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", csv_buffer); + print_error("Discarding invalid line '%s' and continuing.", csv_buffer); delete_task(db, task); continue; } @@ -788,7 +778,7 @@ bool import_from_csv(database_st *db, const char *path) { fclose(file); free(csv_buffer); - return success; + return true; } // Appends task to the end of the CSV file. @@ -799,7 +789,7 @@ bool append_to_csv(task_st *task, const char *path) { FILE *file = fopen(path, "a+"); if (file == NULL) { - fprintf(stderr, "Failed to open file '%s' while appending to CSV: %s.\n", path, strerror(errno)); + print_error("Failed to open file '%s' while appending to CSV: %s.", path, strerror(errno)); return false; } @@ -857,6 +847,12 @@ void set_active_task(database_st *db, task_st *task) { db->active_task = (task == NULL) ? -1 : task - db->tasks; } +// Returns true when database is full. +bool is_database_full(database_st *db) { + assert(db != NULL); + return db->count >= MAX_DATABASE_TASKS; +} + #define INPUT_TIMEOUT_MS 1000 #define INPUT_AWAIT_INF -1 @@ -1155,7 +1151,7 @@ void draw_tui(database_st *db, layout_st *layout) { void *mem_alloc(size_t mem_size, const char *error_tag) { void *mem_pointer = malloc(mem_size); if (mem_pointer == NULL && mem_size > 0) { - fprintf(stderr, "Failed to allocate memory (%s): %s.\n", (error_tag == NULL ? "undefined" : error_tag), strerror(errno)); + print_error("Failed to allocate memory (%s): %s.", (error_tag == NULL ? "undefined" : error_tag), strerror(errno)); exit(EXIT_FAILURE); } return mem_pointer; @@ -1185,7 +1181,7 @@ bool initialize_app_folder() { // Create app folder. mkdir(app_folder, 0740); if (errno != 0 && errno != EEXIST) { - fprintf(stderr, "Failed to create app folder '%s': %s.\n", app_folder, strerror(errno)); + print_error("Failed to create app folder '%s': %s.", app_folder, strerror(errno)); return false; } @@ -1271,6 +1267,7 @@ bool read_enter_confirmation(int row, int style, const char *message) { int main(int argc, char *argv[]) { if (initialize_app_folder() == false) { + print_error("Failed to initialize app folder."); free_memory(); return EXIT_FAILURE; } @@ -1281,6 +1278,7 @@ int main(int argc, char *argv[]) { if (is_file_accessible(db_file_path) == false) { if (store_database(&database, db_file_path) == false) { + print_error("Failed to initialize database."); free_memory(); return EXIT_FAILURE; } @@ -1288,6 +1286,7 @@ int main(int argc, char *argv[]) { if (is_file_accessible(ar_file_path) == false) { if (export_to_csv(&archive, ar_file_path) == false) { + print_error("Failed to initialize archive."); free_memory(); return EXIT_FAILURE; } @@ -1356,19 +1355,22 @@ int main(int argc, char *argv[]) { if (is_equal_to_any(argv[idx], "--import-csv", "-i")) { idx++; if (idx >= argc) { - fprintf(stdout, "Missing CSV file path to import.\n"); + print_error("Missing CSV file path to import."); free_memory(); return EXIT_FAILURE; } if (load_database(&database, db_file_path) == false) { + print_error("Failed to load database."); free_memory(); return EXIT_FAILURE; } if (import_from_csv(&database, argv[idx]) == false) { + print_error("Failed to import CSV file."); free_memory(); return EXIT_FAILURE; } if (store_database(&database, db_file_path) == false) { + print_error("Failed to store database."); free_memory(); return EXIT_FAILURE; } @@ -1380,15 +1382,17 @@ int main(int argc, char *argv[]) { if (is_equal_to_any(argv[idx], "--export-csv", "-e")) { idx++; if (idx >= argc) { - fprintf(stdout, "Missing CSV file path to export.\n"); + print_error("Missing CSV file path to export."); free_memory(); return EXIT_FAILURE; } if (load_database(&database, db_file_path) == false) { + print_error("Failed to load database."); free_memory(); return EXIT_FAILURE; } if (export_to_csv(&database, argv[idx]) == false) { + print_error("Failed to export CSV file."); free_memory(); return EXIT_FAILURE; } @@ -1402,7 +1406,7 @@ int main(int argc, char *argv[]) { continue; } - fprintf(stdout, "%s: invalid option '%s'.\nTry '%s --help' for more information.\n", argv[0], argv[idx], argv[0]); + print_error("%s: invalid option '%s'.\nTry '%s --help' for more information.", argv[0], argv[idx], argv[0]); free_memory(); return EXIT_FAILURE; } @@ -1413,6 +1417,12 @@ int main(int argc, char *argv[]) { } } + if (load_database(&database, db_file_path) == false) { + print_error("Failed to load database."); + free_memory(); + return EXIT_FAILURE; + } + initialize_tui(); signal(SIGTERM, exit_gracefully); @@ -1420,8 +1430,6 @@ int main(int argc, char *argv[]) { signal(SIGQUIT, exit_gracefully); signal(SIGHUP, exit_gracefully); - load_database(&database, db_file_path); - flushinp(); ungetch(KEY_RESIZE); for (int key; ((key = getch()) != 'q') && (key != 'Q'); ) { @@ -1430,6 +1438,7 @@ int main(int argc, char *argv[]) { task_st *active_task = get_active_task(db); task_st *selected_task = get_selected_task(db); int action_style = A_BOLD | COLOR_PAIR(selected_task == active_task && selected_task != NULL ? STYLE_ACTIVE : STYLE_SELECTED_INVERTED); + int error_style = A_BOLD | COLOR_PAIR(STYLE_ERROR); int selected_task_row = is_terminal_too_small ? 0 : (db->selected_task < 0) ? 1 : (db->selected_task % layout_tasks_rows) + NUM_HEADER_ROWS; @@ -1464,7 +1473,7 @@ int main(int argc, char *argv[]) { string_buffer_size = new_size; string_buffer = realloc(string_buffer, string_buffer_size); if (string_buffer == NULL && string_buffer_size > 0) { - fprintf(stderr, "Failed to allocate memory for string buffer: %s.\n", strerror(errno)); + print_error("Failed to allocate memory for string buffer: %s.", strerror(errno)); flushinp(); ungetch('q'); break; @@ -1477,14 +1486,16 @@ int main(int argc, char *argv[]) { case 'n': case 'N':{ - if (db->count == MAX_DATABASE_TASKS) { - read_enter_confirmation(selected_task_row, action_style, " Database is full. "); + if (is_database_full(db)) { + read_enter_confirmation(selected_task_row, error_style, " Unable to create entry: database is full. "); break; } // Create new task. task_st *new_task; - create_task(db, &new_task); + if (create_task(db, &new_task) == false) { + break; + } // Set new task name. time_t now_utc = time(NULL); @@ -1508,17 +1519,14 @@ int main(int argc, char *argv[]) { } read_input_to_string_buffer_with_space(selected_task_row, 1, action_style, TASK_NAME_LENGTH, size_x - 2); - if (is_empty_string(string_buffer) == false) { replace_char(string_buffer, '\t', ' '); replace_char(string_buffer, '\v', ' '); replace_char(string_buffer, '\f', ' '); replace_char(string_buffer, '\r', ' '); - memcpy(selected_task->name, string_buffer, TASK_NAME_BYTES); trigger_autosave(); } - break; } @@ -1670,7 +1678,15 @@ int main(int argc, char *argv[]) { if (selected_task == NULL) { break; } - duplicate_task(db, selected_task); + + if (is_database_full(db)) { + read_enter_confirmation(selected_task_row, error_style, " Unable to duplicate entry: database is full. "); + break; + } + + if (duplicate_task(db, selected_task) == false) { + break; + } trigger_autosave(); break; } @@ -1705,13 +1721,16 @@ int main(int argc, char *argv[]) { if (db == &database) { if (import_from_csv(&archive, ar_file_path) == false) { reset_database(&archive); - read_enter_confirmation(selected_task_row, action_style, " Archive too big. "); + print_error("Failed to load archive."); break; } db = &archive; } else { - export_to_csv(&archive, ar_file_path); + if (export_to_csv(&archive, ar_file_path) == false) { + print_error("Failed to store archive."); + break; + } reset_database(&archive); db = &database; } @@ -1723,10 +1742,12 @@ int main(int argc, char *argv[]) { if (db != &database || selected_task == NULL || selected_task == active_task) { break; } - if (append_to_csv(selected_task, ar_file_path) == true) { - delete_task(&database, selected_task); - trigger_autosave(); + if (append_to_csv(selected_task, ar_file_path) == false) { + print_error("Failed to archive entry."); + break; } + delete_task(&database, selected_task); + trigger_autosave(); break; } @@ -1735,10 +1756,16 @@ int main(int argc, char *argv[]) { if (db != &archive || selected_task == NULL) { break; } - if (duplicate_task(&database, selected_task) == true) { - delete_task(&archive, selected_task); - trigger_autosave(); + if (is_database_full(&database)) { + read_enter_confirmation(selected_task_row, error_style, " Unable to unarchive entry: database is full. "); + break; + } + if (duplicate_task(&database, selected_task) == false) { + print_error("Failed to unarchive entry."); + break; } + delete_task(&archive, selected_task); + trigger_autosave(); break; } @@ -1780,7 +1807,7 @@ int main(int argc, char *argv[]) { } else { draw_tui(db, layout); - update_error_window(); + draw_error_window(); } timeout(INPUT_TIMEOUT_MS); @@ -1788,14 +1815,27 @@ int main(int argc, char *argv[]) { // Save any unsaved changes. show_processing(); + bool error_saving = false; if (db == &archive) { - export_to_csv(&archive, ar_file_path); + if (export_to_csv(&archive, ar_file_path) == false) { + print_error("Failed to save archive."); + error_saving |= true; + } } if (countdown_to_autosave > 0 || is_autosave_enabled == false) { - store_database(&database, db_file_path); + if (store_database(&database, db_file_path) == false) { + print_error("Failed to save database."); + error_saving |= true; + } + } + if (error_saving) { + print_error("Press any key to close."); + draw_error_window(); + timeout(INPUT_AWAIT_INF); + getch(); } endwin(); free_memory(); - return EXIT_SUCCESS; + return error_saving ? EXIT_FAILURE : EXIT_SUCCESS; } |
