From e9fc779171f0cc5734d0d649eeec1081c354618a Mon Sep 17 00:00:00 2001 From: dam Date: Wed, 31 Aug 2022 16:28:05 +0000 Subject: Removed unused database helper functions. Reworked database to use size_t and ptrdiff_t types to avoid being bound by indexes types constrains. Print task's times. --- main.c | 227 +++++++++++++++++++++++++++++++------------------------------- misc.c | 69 +++++++++++++++++++ readme.md | 4 ++ 3 files changed, 187 insertions(+), 113 deletions(-) diff --git a/main.c b/main.c index a030447..5445157 100644 --- a/main.c +++ b/main.c @@ -17,13 +17,14 @@ #define STR_(X) #X // Convert to string. #define STR(X) STR_(X) // Force argument expansions before converting to string. -#define MAX_TASK_NAME 64 // Maximum task name length, including null-terminator. +#define MAX_TASK_NAME 58 // Maximum task name length, including null-terminator. #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0) -#define LOG_FILE_NAME "log.txt" -#define DB_BIN_PATH_NAME "./database.bin" -#define DB_CSV_PATH_NAME "./database.csv" +#define LOG_FILE_NAME "log.txt" +#define DB_BIN_PATH_NAME "./database.bin" +#define DB_CSV_PATH_NAME "./database.csv" +#define DB_MAX_CAP PTRDIFF_MAX typedef struct /*__attribute__((__packed__))*/ { uint32_t time[7]; @@ -32,22 +33,22 @@ typedef struct /*__attribute__((__packed__))*/ { typedef struct /*__attribute__((__packed__))*/ { uint64_t modified_on; - uint32_t count; - uint32_t capacity; + size_t count; + size_t capacity; task_t* tasks; - task_t* active_task; - task_t* selected_task; // TODO Maybe use this instead of indexes? + ptrdiff_t active_task; + ptrdiff_t selected_task; // TODO Maybe use this instead of indexes? } database_t; -const char* DB_BIN_FILE_SIGNATURE = "TTT:B:01"; -const uint8_t DB_BIN_FILE_SIGNATURE_SIZE = sizeof(DB_BIN_FILE_SIGNATURE); -const size_t SIZEOF_TASK_T = sizeof(task_t); -const size_t SIZEOF_DATABASE_T = sizeof(database_t); -const char* DAYS_OF_WEEK[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; -const uint8_t DAYS_ON_WEEK = sizeof(DAYS_OF_WEEK)/sizeof(char*); +const char* DB_FILE_SIGN = "TTT:B:01"; +const size_t SIZEOF_DB_FILE_SIGN = sizeof(DB_FILE_SIGN); +const size_t SIZEOF_TASK_T = sizeof(task_t); +const size_t SIZEOF_DATABASE_T = sizeof(database_t); +const char* DAYS_OF_WEEK[] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; +const uint8_t DAYS_ON_WEEK = sizeof(DAYS_OF_WEEK)/sizeof(char*); -task_t* selected_task; database_t database; +database_t archive; // TODO To be implemented in the future. // Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character. // The string should have capacity for at least length number of items. @@ -94,56 +95,22 @@ void print_task(const task_t* task) { // Macro used to calculate index of task on a database. #define task_idx(db,task) ((ptrdiff_t)(task - db->tasks)) -// Adds task to database. If necessary, expands database capacity. -// Returns success. -bool add_task(database_t* db, const task_t* task) { - assert(db != NULL); - assert(task != NULL); - - if (db->count == UINT32_MAX) { - fprintf(stderr, "Database reached maximum capacity, discarding task '%s'.\n", task->name); - return false; - } - - // If necessary, expand database capacity. - uint32_t current_capacity = db->capacity; - if((db->count + 1) > current_capacity) { - uint32_t new_capacity = current_capacity == 0 ? 2 : - current_capacity >= UINT32_MAX >> 2 ? UINT32_MAX : - current_capacity << 1; - - task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); - if (new_tasks == NULL) { - fprintf(stderr, "Failed to expand database, discarding task '%s'.\n", task->name); - return false; - } - db->capacity = new_capacity; - db->tasks = new_tasks; - } - - // Store new task. - memcpy(&(db->tasks[db->count]), task, SIZEOF_TASK_T); - db->count++; - - return true; -} // Creates new task returned in the pointer. If necessary, expands database capacity. // Returns success. bool create_task(database_t* db, task_t** task) { assert(db != NULL); - if (db->count == UINT32_MAX) { + if (db->count == DB_MAX_CAP) { fprintf(stderr, "Database reached maximum capacity.\n"); return false; } // If necessary, expand database capacity. - uint32_t current_capacity = db->capacity; + size_t current_capacity = db->capacity; if((db->count + 1) > current_capacity) { - ptrdiff_t selected_task_offset = db->tasks - db->active_task; - uint32_t new_capacity = current_capacity == 0 ? 2 : - current_capacity >= UINT32_MAX >> 2 ? UINT32_MAX : + size_t new_capacity = current_capacity == 0 ? 2 : + current_capacity >= DB_MAX_CAP >> 2 ? DB_MAX_CAP : current_capacity << 1; task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); @@ -154,8 +121,6 @@ bool create_task(database_t* db, task_t** task) { db->capacity = new_capacity; db->tasks = new_tasks; - db->active_task = db->tasks + selected_task_offset; - // TODO Validate selected_task (may now be above count. } // Prepare new task. @@ -164,29 +129,41 @@ bool create_task(database_t* db, task_t** task) { db->count++; + // Adjust selected task. + if (db->selected_task < 0) { + db->selected_task = db->count-1; + } + return true; } -// Removes task by index from database. If possible, shrinks database capacity. -// Returns success. -bool remove_task(database_t* db, uint32_t index) { +bool delete_task(database_t* db, task_t* task) { assert(db != NULL); - assert(index < db->count); + assert(task != NULL); + assert(task >= db->tasks && task < &db->tasks[db->count]); + + // Move tasks after the index position to their new positions. + ptrdiff_t index = task - db->tasks; + memmove(task, task+1, (db->count - index - 1) * SIZEOF_TASK_T); + db->count--; - if (index >= db->count) { - fprintf(stderr, "Failed to remove out-of-bounds index '%" PRIu32 "'.", index); - return false; + // Adjust selected task. + if (db->selected_task >= db->count) { + db->selected_task--; } - // Move tasks after the index position to their new positions. - memmove(&db->tasks[index], &db->tasks[index+1], (db->count - index - 1) * SIZEOF_TASK_T); - db->count--; + // Adjust active task. + if (db->active_task > index) { + db->active_task--; + } + else if (db->active_task == index) { + db->active_task = -1; + } // If possible, shrink database capacity. - uint32_t current_capacity = db->capacity; + size_t current_capacity = db->capacity; if (db->count <= (current_capacity >> 2)) { - ptrdiff_t selected_task_offset = db->tasks - db->active_task; - uint32_t new_capacity = current_capacity >> 1; + size_t new_capacity = current_capacity >> 1; task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); if (new_tasks == NULL) { fprintf(stderr, "Failed to shrink database.\n"); @@ -194,18 +171,11 @@ bool remove_task(database_t* db, uint32_t index) { } db->capacity = new_capacity; db->tasks = new_tasks; - db->active_task = db->tasks + selected_task_offset; - // TODO Validate selected_task (may now be above count. } return true; } -bool delete_task(database_t* db, task_t* task) { - assert(task > db->tasks && task < &db->tasks[db->count]); - return remove_task(db, task - db->tasks); -} - void clear_database(database_t* db) { free(db->tasks); memset(db, 0, SIZEOF_TASK_T); @@ -225,7 +195,7 @@ bool store_database(const database_t* db, const char* path_name) { return false; } - fwrite(DB_BIN_FILE_SIGNATURE, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); + fwrite(DB_FILE_SIGN, sizeof(char), SIZEOF_DB_FILE_SIGN, file); fwrite(db, SIZEOF_DATABASE_T, 1, file); fwrite(db->tasks, SIZEOF_TASK_T, db->count, file); @@ -248,9 +218,9 @@ bool load_database(database_t* db, const char* path_name) { } // Validate file signature. - char file_signature[DB_BIN_FILE_SIGNATURE_SIZE]; - fread(&file_signature, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file); - if (strncmp(file_signature, DB_BIN_FILE_SIGNATURE, DB_BIN_FILE_SIGNATURE_SIZE) != 0) { + char file_signature[SIZEOF_DB_FILE_SIGN]; + fread(&file_signature, sizeof(char), SIZEOF_DB_FILE_SIGN, file); + if (strncmp(file_signature, DB_FILE_SIGN, SIZEOF_DB_FILE_SIGN) != 0) { fprintf(stderr, "Invalid file signature.\n"); return false; } @@ -258,17 +228,9 @@ bool load_database(database_t* db, const char* path_name) { // Read database structure. fread(db, SIZEOF_DATABASE_T, 1, file); - // Keep track of last active task. - ptrdiff_t active_task_index = db->active_task - db->tasks; - // Restore database capacity. db->tasks = calloc(db->capacity, SIZEOF_TASK_T); - // Restore last active task. - if (db->active_task != NULL) { - db->active_task = db->tasks + active_task_index; - } - // Read database entries. fread(db->tasks, SIZEOF_TASK_T, db->count, file); @@ -471,7 +433,7 @@ void prototype(int level) { memset(&db_load, 0, SIZEOF_DATABASE_T); load_database(&db_load, DB_BIN_PATH_NAME); - fprintf(stderr, "loaded %" PRIu32 " entries.\n", db_load.count); + fprintf(stderr, "loaded %zu entries.\n", db_load.count); for (uint32_t idx = 0; idx < db_load.count; idx++) { print_task(&db_load.tasks[idx]); @@ -496,14 +458,14 @@ void prototype(int level) { memset(&db_import, 0, SIZEOF_DATABASE_T); import_from_csv(&db_import, DB_CSV_PATH_NAME); - fprintf(stderr, "imported %" PRIu32 " entries.\n", db_import.count); + fprintf(stderr, "imported %zu entries.\n", db_import.count); for (uint32_t idx = 0; idx < db_import.count; idx++) { print_task(&db_import.tasks[idx]); } clear_database(&db_import); fprintf(stderr, done); - } + } /////////////////////////////////////////////////////////////////////////// // Check size of structs @@ -579,13 +541,9 @@ void initialize_layouts() { } void initialization() { - - selected_task = NULL; - if (load_database(&database, DB_BIN_PATH_NAME) == false) { memset(&database, 0, SIZEOF_DATABASE_T); } - selected_task = database.tasks; } void free_memory() { @@ -633,6 +591,7 @@ void draw_header() { void draw_table() { task_t* task; task_t* active_task; + task_t* selected_task; layout_t* layout = &layouts[selected_layout]; int table_size = layout->table_size; @@ -662,7 +621,13 @@ void draw_table() { // Draw rows with tasks. move(1, 0); - active_task = database.active_task; + active_task = database.tasks + database.active_task; + selected_task = database.tasks + database.selected_task; + + + uint64_t total = 0; + uint64_t totals[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + for (uint32_t idx = 0; idx < database.count; idx++){ task = &database.tasks[idx]; @@ -693,6 +658,25 @@ void draw_table() { size_t max_column_length = start_columns - 2; size_t copy_length = task_name_length < max_column_length ? task_name_length : max_column_length; memcpy(line_buffer+2, task->name, copy_length); + + uint32_t *time = task->time; + sprintf(line_buffer + columns[1], "%8d", time[0]); + sprintf(line_buffer + columns[2], "%8d", time[1]); + sprintf(line_buffer + columns[3], "%8d", time[2]); + sprintf(line_buffer + columns[4], "%8d", time[3]); + sprintf(line_buffer + columns[5], "%8d", time[4]); + sprintf(line_buffer + columns[6], "%8d", time[5]); + sprintf(line_buffer + columns[7], "%8d", time[6]); + + // Update totals. + total = 0; + for(int idx = DAYS_ON_WEEK-1; idx >= 0; idx--) { + total += time[idx]; + totals[idx] += time[idx]; + } + totals[DAYS_ON_WEEK] += total; + sprintf(line_buffer + columns[8], "%10" PRIu64, total); + addstr(line_buffer); // Disable highlight color on selected entry. @@ -711,6 +695,19 @@ void draw_table() { move(pos_y, 0); } + // Print totals + memset(line_buffer, ' ', size_x * sizeof(char)); + line_buffer[size_x-1] = '\0'; + sprintf(line_buffer + columns[1], "%8" PRIu64, totals[0]); + sprintf(line_buffer + columns[2], "%8" PRIu64, totals[1]); + sprintf(line_buffer + columns[3], "%8" PRIu64, totals[2]); + sprintf(line_buffer + columns[4], "%8" PRIu64, totals[3]); + sprintf(line_buffer + columns[5], "%8" PRIu64, totals[4]); + sprintf(line_buffer + columns[6], "%8" PRIu64, totals[5]); + sprintf(line_buffer + columns[7], "%8" PRIu64, totals[6]); + mvaddstr(pos_y, 0, line_buffer); + pos_y++; + // Draw empty rows. // TODO Do this properly. int dummy = 100; @@ -873,12 +870,15 @@ int main(int argc, char *argv[]) { width = 10; starty = (LINES - height) / 2; // Calculating for a center placement of the window. startx = (COLS - width) / 2; // - printw("Press F1 to exit"); + refresh(); my_win = create_newwin(height, width, starty, startx); ch = KEY_RESIZE; do { + task_t* active_task = database.tasks + database.active_task; + task_t* selected_task = database.tasks + database.selected_task; + switch(ch) { case KEY_F(1): @@ -914,10 +914,12 @@ int main(int argc, char *argv[]) { case KEY_F(2): { + if (database.selected_task < 0) { + break; + } // rename stuff - int row = selected_task - database.tasks + 1; + int row = database.selected_task + 1; mvaddch(row, 0, ACS_DIAMOND); -// move(row, 2); clrtoeol(); mvaddch(row, size_x-1, ACS_VLINE); curs_set(1); @@ -927,26 +929,25 @@ int main(int argc, char *argv[]) { } case KEY_F(3): - delete_task(&database, selected_task); - if (selected_task >= database.tasks+database.count) { - selected_task = database.tasks+database.count-1; + { + if (database.selected_task < 0) { + break; } -// if (selected_task >= database.count) { -// selected_task = database.count-1; -// } + delete_task(&database, selected_task); break; + } case '\n': case ' ': if (true) { - task_t* current_task = database.active_task; + task_t* next_task = selected_task; - if (current_task != NULL) { + if (active_task > 0) { // TODO Add remaining time to task. - database.active_task = NULL; + database.active_task = -1; } - if (current_task != next_task) { - database.active_task = next_task; + if (active_task != next_task) { + database.active_task = next_task - database.tasks; } database.modified_on = time(NULL); store_database(&database, DB_BIN_PATH_NAME); @@ -973,14 +974,14 @@ int main(int argc, char *argv[]) { break; case KEY_UP: - if (selected_task > database.tasks) { - selected_task--; + if (database.selected_task > 0) { + database.selected_task--; } break; case KEY_DOWN: - if (selected_task < database.tasks + database.count - 1) { - selected_task++; + if (database.selected_task < database.count - 1) { + database.selected_task++; } break; } diff --git a/misc.c b/misc.c index a192f18..2c734bd 100644 --- a/misc.c +++ b/misc.c @@ -1,4 +1,73 @@ +// Adds task to database. If necessary, expands database capacity. +// Returns success. +bool add_task(database_t* db, const task_t* task) { + assert(db != NULL); + assert(task != NULL); + + if (db->count == UINT32_MAX) { + fprintf(stderr, "Database reached maximum capacity, discarding task '%s'.\n", task->name); + return false; + } + + // If necessary, expand database capacity. + uint32_t current_capacity = db->capacity; + if((db->count + 1) > current_capacity) { + uint32_t new_capacity = current_capacity == 0 ? 2 : + current_capacity >= UINT32_MAX >> 2 ? UINT32_MAX : + current_capacity << 1; + + task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); + if (new_tasks == NULL) { + fprintf(stderr, "Failed to expand database, discarding task '%s'.\n", task->name); + return false; + } + db->capacity = new_capacity; + db->tasks = new_tasks; + } + + // Store new task. + memcpy(&(db->tasks[db->count]), task, SIZEOF_TASK_T); + db->count++; + + return true; +} + +// TODO Remove this task and move code to delete_task. +// Removes task by index from database. If possible, shrinks database capacity. +// Returns success. +bool remove_task(database_t* db, uint32_t index) { + assert(db != NULL); + assert(index < db->count); + + if (index >= db->count) { + fprintf(stderr, "Failed to remove out-of-bounds index '%" PRIu32 "'.", index); + return false; + } + + // Move tasks after the index position to their new positions. + memmove(&db->tasks[index], &db->tasks[index+1], (db->count - index - 1) * SIZEOF_TASK_T); + db->count--; + + // If possible, shrink database capacity. + size_t current_capacity = db->capacity; + if (db->count <= (current_capacity >> 2)) { + ptrdiff_t selected_task_offset = db->tasks - db->active_task; + size_t new_capacity = current_capacity >> 1; + task_t* new_tasks = realloc(db->tasks, new_capacity * SIZEOF_TASK_T); + if (new_tasks == NULL) { + fprintf(stderr, "Failed to shrink database.\n"); + return false; + } + db->capacity = new_capacity; + db->tasks = new_tasks; + db->active_task = db->tasks + selected_task_offset; + // TODO Validate selected_task (may now be above count. + } + + return true; +} + uint32_t CRC32(const uint8_t data[], size_t data_length) { static const uint32_t crc32_table[] = { diff --git a/readme.md b/readme.md index 6a398cd..bff27b5 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,10 @@ Task Time Tracker - [x] maybe rename to task-time-tracker? - [x] Remove hash stuff; - [x] Tasks should have a `modified_on` timestamp field; +- [x] Change capacity to size_t. +- [x] Change active_task to active_task_ptrdiff. +- [x] use selected_task_ptrdiff? +- [ ] On compact layout, start task names right after the vertical line, otherwise, add a space (as is now). - [ ] Allow two view modes: one to iew non-archived tasks; another to view archived tasks. - [ ] To add a new task, create it and enter the task-name-edit mode. To edit, use the ACS_CKBOARD to show empty spaces and ACS_LARROW and ACS_RARROW to show continuation on horizontal direction. - [ ] Status of task will allow to keep counting time even when the process gets terminated forcefully; -- cgit v1.2.3