aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c227
1 files changed, 114 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;
}