aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c198
1 files changed, 118 insertions, 80 deletions
diff --git a/main.c b/main.c
index daac495..b4756f0 100644
--- a/main.c
+++ b/main.c
@@ -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;
}