aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c293
1 files changed, 188 insertions, 105 deletions
diff --git a/main.c b/main.c
index 19b9d23..0441eff 100644
--- a/main.c
+++ b/main.c
@@ -31,16 +31,25 @@ typedef /*struct __attribute__((__packed__))*/ struct {
uint8_t state;
} task_t;
+typedef /*struct __attribute__((__packed__))*/ struct {
+ task_t* tasks;
+ uint32_t count;
+ uint32_t capacity;
+} database_t;
+
const char* DB_BIN_FILE_SIGNATURE = "TTBF:1.0";
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*);
-task_t* tasks = NULL;
-uint32_t tasks_count = 0;
-uint32_t tasks_capacity = 0;
uint32_t selected_task = -1;
+database_t database = {
+ .tasks = NULL,
+ .count = 0,
+ .capacity = 0,
+};
// Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character.
@@ -63,6 +72,8 @@ size_t truncate_string_utf8(char* string, size_t length) {
return length - idx;
}
+// Uses strchr to replace all instances of find by replace.
+// Returns string.
char* replace_char(char* string, char find, char replace){
char* idx = string;
while((idx = strchr(idx, find)) != NULL) {
@@ -83,42 +94,108 @@ void print_task(const task_t* task) {
printf("t[6]: '%" PRIu32 "'\n", task->time[6]);
}
-void initialization() {
- // Make sure architecture uses 8bits per char.
- assert(CHAR_BIT == 8);
+// TODO Maybe inline?
+bool add_task(database_t* db, const task_t* task) {
+ assert(db != NULL);
+ assert(task != NULL);
+
+ uint32_t db_capacity = db->capacity;
+ if (db->count + 1 > db_capacity) {
+ uint32_t new_database_capacity = db_capacity == 0 ? 16 : db_capacity << 1;
+
+ // TODO Funcky way to calculate nex power of two.
+ new_database_capacity--;
+ new_database_capacity |= new_database_capacity >> 1;
+ new_database_capacity |= new_database_capacity >> 2;
+ new_database_capacity |= new_database_capacity >> 4;
+ new_database_capacity |= new_database_capacity >> 8;
+ new_database_capacity |= new_database_capacity >> 16;
+ new_database_capacity++;
+
+ task_t* new_database_tasks = realloc(db->tasks, new_database_capacity * SIZEOF_TASK_T);
+ if (new_database_tasks == NULL) {
+ fprintf(stderr, "Failed to expand database memory.\n");
+ return false;
+ }
+ db->tasks = new_database_tasks;
+ }
+ memcpy(&(db->tasks[db->count]), task, SIZEOF_TASK_T);
+ db->count++;
+
+ return true;
+}
+
+// TODO Maybe inline?
+bool remove_task(database_t* db, uint32_t index) {
+ assert(db != NULL);
+ assert(index < db->count);
+
+ /*
+ | ~ ~ 6
+ 0 1 2 3 4 5 6 - 3 - 1 = 2
+
+ | ~ 6
+ 0 1 2 3 4 5 6 - 4 - 1 = 1
+
+ | ~ 6
+ 0 1 2 3 4 5 6 - 5 - 1 = 0
+ */
+ memmove(&db->tasks[index], &db->tasks[index+1], (db->count - index - 1) * SIZEOF_TASK_T);
+ db->count--;
+
+ if (db->capacity > 16 && db->count < (db->capacity >> 2)) {
+ uint32_t new_tasks_capacity = db->capacity << 1;
+ task_t* new_tasks = realloc(db->tasks, new_tasks_capacity * SIZEOF_TASK_T);
+ if (new_tasks == NULL) {
+ fprintf(stderr, "Failed to shrink database memory.\n");
+ return false;
+ }
+ db->tasks = new_tasks;
+ }
+ return true;
}
-void store_database(const task_t* database, uint32_t number_of_entries, const char* path_name) {
+void clear_database(database_t* db) {
+ free(db->tasks);
+ db->tasks = NULL;
+ db->count = 0;
+ db->capacity = 0;
+}
+
+// Stores data from database into binary file.
+// Returns success.
+bool store_database(const database_t* db, const char* path_name) {
- assert(database != NULL);
+ assert(db != NULL);
assert(path_name != NULL);
// Open file.
FILE* file = fopen(path_name, "w");
if (file == NULL) {
- fprintf(stderr, "Failed to open database file: %s.\n", strerror(errno)); // TODO Fix message.
- return;
+ fprintf(stderr, "Failed to open file '%s' while storing database: %s.\n", path_name, strerror(errno));
+ return false;
}
fwrite(DB_BIN_FILE_SIGNATURE, sizeof(char), DB_BIN_FILE_SIGNATURE_SIZE, file);
- fwrite(&number_of_entries, sizeof(number_of_entries), 1, file);
- fwrite(database, SIZEOF_TASK_T, number_of_entries, file);
+ fwrite(&db->count, sizeof(db->count), 1, file);
+ fwrite(db->tasks, SIZEOF_TASK_T, db->count, file);
fclose(file);
+ return true;
}
// Loads data from binary file into database.
-// Returns number of loaded entries.
-uint32_t load_database(task_t** database, const char* path_name) {
+// Returns success.
+bool load_database(database_t* db, const char* path_name) {
- assert(database != NULL);
+ assert(db != NULL);
assert(path_name != NULL);
// Open file.
FILE* file = fopen(path_name, "r");
if (file == NULL) {
- fprintf(stderr, "Failed to open database file '%s': %s.\n", path_name, strerror(errno)); // TODO Fix message.
- return 0;
+ fprintf(stderr, "Failed to open file '%s' while loading database: %s.\n", path_name, strerror(errno));
+ return false;
}
// Validate file signature.
@@ -126,34 +203,38 @@ uint32_t load_database(task_t** database, const char* path_name) {
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) {
fprintf(stderr, "Invalid file signature.\n");
- return 0;
+ return false;
}
+ // Discard all previous data.
+ clear_database(&database);
+
// Read number of entries.
- uint32_t number_of_entries;
- fread(&number_of_entries, sizeof(number_of_entries), 1, file);
+ fread(&db->count, sizeof(db->count), 1, file);
// Read database entries.
- *database = calloc(number_of_entries, SIZEOF_TASK_T);
- fread(*database, SIZEOF_TASK_T, number_of_entries, file);
+ db->tasks = calloc(db->count, SIZEOF_TASK_T);
+ fread(db->tasks, SIZEOF_TASK_T, db->count, file);
// Make sure we are reading all the file.
assert(fgetc(file) == EOF);
fclose(file);
- return number_of_entries;
+ return true;
}
-void export_database(const task_t* database, uint32_t number_of_entries, const char* path_name) {
+// Exports data into CSV file.
+// Returns success.
+bool export_to_csv(const database_t* db, const char* path_name) {
- assert(database != NULL);
+ assert(db != NULL);
assert(path_name != NULL);
FILE* file = fopen(path_name, "w");
if (file == NULL) {
- fprintf(stderr, "Failed to open database file '%s': %s.\n", path_name, strerror(errno)); // TODO Fix message.
- return;
+ fprintf(stderr, "Failed to open file '%s' while exporting to CSV: %s.\n", path_name, strerror(errno));
+ return false;
}
fprintf(file, "%s,%s,%s,%s,%s,%s,%s,%s\n",
@@ -167,8 +248,8 @@ void export_database(const task_t* database, uint32_t number_of_entries, const c
DAYS_OF_WEEK[6]
);
- for (uint32_t idx = 0; idx < number_of_entries; idx++) {
- const task_t* task = &database[idx];
+ for (uint32_t idx = 0; idx < db->count; idx++) {
+ const task_t* task = &db->tasks[idx];
fprintf(file, "%s,%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 "\n",
task->name,
task->time[0],
@@ -182,23 +263,23 @@ void export_database(const task_t* database, uint32_t number_of_entries, const c
}
fclose(file);
+ return true;
}
-uint32_t import_database(task_t** database, const char* path_name) {
-
- assert(database != NULL);
+// Imports CSV file into database.
+// Returns success.
+bool import_from_csv(database_t* db, const char* path_name) {
+ assert(db != NULL);
assert(path_name != NULL);
FILE* file = fopen(path_name, "r");
if (file == NULL) {
- fprintf(stderr, "Failed to open file '%s' while importing database: %s.\n", path_name, strerror(errno));
- return 0;
+ fprintf(stderr, "Failed to open file '%s' while importing from CSV: %s.\n", path_name, strerror(errno));
+ return false;
}
uint32_t number_of_entries = 0;
- uint32_t capacity = 0;
task_t task;
- task_t* database_reallocation = NULL;
char *line_buffer = NULL;
size_t line_buffer_size = 0;
ssize_t read_characters = 0;
@@ -206,7 +287,7 @@ uint32_t import_database(task_t** database, const char* path_name) {
// Skip header line.
fscanf(file, "%*[^\n]\n");
-
+
// Parse CSV file.
while(true) {
@@ -243,42 +324,19 @@ uint32_t import_database(task_t** database, const char* path_name) {
&task.time[6]
) != 7) {
replace_char(line_buffer, '\n', ' ');
-// fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer);
+ fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer);
continue;
}
- // Expand database capacity when required.
+ // Add new database entry.
number_of_entries++;
- if (number_of_entries > capacity) {
- capacity = capacity == 0 ? 16 : capacity << 1;
- database_reallocation = realloc(*database, capacity * SIZEOF_TASK_T);
- if (database_reallocation == NULL) {
- fprintf(stderr, "Failed to allocate memory while importing database.\n");
- number_of_entries--;
- break;
- }
- *database = database_reallocation;
- }
-
- memcpy(&((*database)[number_of_entries-1]), &task, SIZEOF_TASK_T);
+ add_task(db, &task);
}
fclose(file);
free(line_buffer);
- // Trim any excess memory allocated for database.
- if (capacity > number_of_entries) {
-// *database = realloc(*database, number_of_entries * SIZEOF_TASK_T); // TODO dangerous stuff going on here.
- database_reallocation = realloc(*database, number_of_entries * SIZEOF_TASK_T);
- if (database_reallocation == NULL) {
- fprintf(stderr, "Failed to allocate memory while importing database.\n");
- }
- else {
- *database = database_reallocation;
- }
- }
-
- return number_of_entries;
+ return true;
}
@@ -319,32 +377,34 @@ void prototype(int level) {
///////////////////////////////////////////////////////////////////////////
// Prepare some data for testing.
-
- tasks_count = 3;
- tasks = calloc(tasks_count, SIZEOF_TASK_T);
-
+ /*
task_t tmp[] = {
{
- .name = "TASK-00 : Sample task name.",
- .time = { 3,2,1,0,1,2,3 },
+ .name = "ALPHA-TASK",
+ .time = { 0, 0, 0, 0, 0, 0, 0 },
},
{
- .name = "TASK-01 : A wild task appears",
- .time = { 1,2,3,4,5,6,7 },
+ .name = "BETA-TASK",
+ .time = { 1, 1, 1, 1, 1, 1, 1 },
},
{
- .name = "BAZINGA : Not a task!",
- .time = { 11,22,33,44,55,66,77 },
+ .name = "DELTA-TASK",
+ .time = { 2, 2, 2, 2, 2, 2, 2 },
}
- };
- memcpy(tasks, &tmp, SIZEOF_TASK_T * tasks_count);
+ };
+ uint32_t number_of_items = sizeof(tmp) / sizeof(task_t);
+ database.tasks = calloc(number_of_items, SIZEOF_TASK_T);
+ database.capacity = number_of_items;
+ database.count = number_of_items;
+ memcpy(database.tasks, &tmp, SIZEOF_TASK_T * number_of_items);
+ */
///////////////////////////////////////////////////////////////////////////
// Store database from memory to binary file.
if (level & T_SBIN) {
fprintf(stderr, "# store database ------------------------------ \\\n");
- store_database(tasks, tasks_count, DB_BIN_PATH_NAME);
+ store_database(&database, DB_BIN_PATH_NAME);
fprintf(stderr, done);
}
@@ -352,14 +412,16 @@ void prototype(int level) {
// Load database from binary file to memory.
if (level & T_LBIN) {
fprintf(stderr, "# load database ------------------------------- \\\n");
- task_t* task_load = NULL;
- uint32_t number_of_entries_load = load_database(&task_load, DB_BIN_PATH_NAME);
+ database_t db_load;
+ memset(&db_load, '\0', SIZEOF_DATABASE_T);
- fprintf(stderr, "loaded %" PRIu32 " entries.\n", number_of_entries_load);
- for (uint32_t idx = 0; idx < number_of_entries_load; idx++) {
- print_task(&task_load[idx]);
+ load_database(&db_load, DB_BIN_PATH_NAME);
+ fprintf(stderr, "loaded %" PRIu32 " entries.\n", db_load.count);
+
+ for (uint32_t idx = 0; idx < db_load.count; idx++) {
+ print_task(&db_load.tasks[idx]);
}
- free(task_load);
+ clear_database(&db_load);
fprintf(stderr, done);
}
@@ -367,7 +429,7 @@ void prototype(int level) {
// Export database to CSV file.
if (level & T_ECSV) {
fprintf(stderr, "# export to CSV ------------------------------- \\\n");
- export_database(tasks, tasks_count, DB_CSV_PATH_NAME);
+ export_to_csv(&database, DB_CSV_PATH_NAME);
fprintf(stderr, done);
}
@@ -375,14 +437,16 @@ void prototype(int level) {
// Import database from CSV file.
if (level & T_ICSV) {
fprintf(stderr, "# import from CSV ----------------------------- \\\n");
- task_t* task_import = NULL;
- uint32_t number_of_entries_import = import_database(&task_import, DB_CSV_PATH_NAME);
+ database_t db_import;
+ 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 %" PRIu32 " entries.\n", number_of_entries_import);
- for (uint32_t idx = 0; idx < number_of_entries_import; idx++) {
- print_task(&task_import[idx]);
+ for (uint32_t idx = 0; idx < db_import.count; idx++) {
+ print_task(&db_import.tasks[idx]);
}
- free(task_import);
+ clear_database(&db_import);
fprintf(stderr, done);
}
@@ -394,12 +458,6 @@ void prototype(int level) {
fprintf(stderr, "sizeof(super_t) = %zu bytes (%zu bits : %6.3f W64b)\n", tts, tts*8, ((double)tts)*8.0/64.0);
fprintf(stderr, done);
}
-
- ///////////////////////////////////////////////////////////////////////////
- // Release memory and exit.
-
-// free(tasks);
-// return;
}
@@ -456,10 +514,23 @@ void initialize_layouts() {
// layout->table_headers = { " T ", " M ", " T ", " W ", " T ", " F ", " S ", " S ", " # " };
}
+void initialization() {
+ // Make sure architecture uses 8bits per char.
+ assert(CHAR_BIT == 8);
+
+ // TODO May use the memset or set the properties during instancing.
+ // Prepare memory.
+ memset(&database, '\0', SIZEOF_DATABASE_T);
+}
+
void free_memory() {
+ clear_database(&database);
+
free(line_buffer);
- free(tasks); // TODO Deallocate tasks.
+ line_buffer = NULL;
+
free(layouts);
+ layouts = NULL;
}
void draw_header() {
@@ -521,9 +592,9 @@ void draw_table() {
};
move(1, 0);
- for (uint32_t idx = 0; idx < tasks_count; idx++){
+ for (uint32_t idx = 0; idx < database.count; idx++){
- task = &tasks[idx];
+ task = &database.tasks[idx];
// Enable highlight color on selected entry.
if (idx == selected_task) {
@@ -585,11 +656,8 @@ void destroy_win(WINDOW *local_win);
int main(int argc, char *argv[]) {
initialization();
-
-
// TODO Parse commands using: https://stackoverflow.com/questions/9642732/parsing-command-line-arguments-in-c
if (argc > 1) {
-// const char* command_version = { "--version", "-v" };
char* action;
bool do_action = false;
@@ -625,6 +693,22 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
+ action = "--t_lbin";
+ do_action = strncmp(argv[idx], action, strlen(action)) == 0;
+ if (do_action) {
+ prototype(T_LBIN);
+ return EXIT_SUCCESS;
+ }
+
+ action = "--t_csv2bin";
+ do_action = strncmp(argv[idx], action, strlen(action)) == 0;
+ if (do_action) {
+ clear_database(&database);
+ import_from_csv(&database, DB_CSV_PATH_NAME);
+ store_database(&database, DB_BIN_PATH_NAME);
+ return EXIT_SUCCESS;
+ }
+
action = "--test";
do_action = strncmp(argv[idx], action, strlen(action)) == 0;
if (do_action) {
@@ -638,8 +722,7 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- tasks_count = load_database(&tasks, DB_BIN_PATH_NAME);
- tasks_capacity = tasks_count;
+ load_database(&database, DB_BIN_PATH_NAME);
initialize_layouts();
@@ -691,7 +774,7 @@ int main(int argc, char *argv[]) {
break;
case KEY_DOWN:
- selected_task = (selected_task+1) == tasks_count ? selected_task : selected_task + 1;
+ selected_task = (selected_task+1) == database.count ? selected_task : selected_task + 1;
destroy_win(my_win);
my_win = create_newwin(height, width, ++starty,startx);
break;