aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c176
-rw-r--r--readme.md3
2 files changed, 110 insertions, 69 deletions
diff --git a/main.c b/main.c
index 6b38f2d..65d1144 100644
--- a/main.c
+++ b/main.c
@@ -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;
}
diff --git a/readme.md b/readme.md
index 6a6d47e..c54ecd7 100644
--- a/readme.md
+++ b/readme.md
@@ -73,4 +73,5 @@ Task Time Tracker
- [x] Review all code for bugs related to auto-cast on ptrdiff_t (signed/unsigned);
- [x] Review all code for bugs related to auto-cast on size_t (signed/unsigned);
- [x] Go over all `TODO` items;
-- [ ] Hide stderr messages from app screen.
+- [x] Hide stderr messages from app screen.
+- [x] Improve error detection/messages.