aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c (renamed from task_tracker.c)214
-rw-r--r--misc.c66
-rw-r--r--readme.md29
3 files changed, 194 insertions, 115 deletions
diff --git a/task_tracker.c b/main.c
index 02ed8c3..878e42c 100644
--- a/task_tracker.c
+++ b/main.c
@@ -1,6 +1,6 @@
// Compilation command:
-// - release: gcc task_tracker.c -Wall -O2 -m64 -lncurses -o task_tracker.x64
-// - debug : gcc task_tracker.c -Wall -g3 -m64 -lncurses -o task_tracker.x64
+// - release: gcc main.c -Wall -O2 -m64 -lncurses -o ttt
+// - debug : gcc main.c -Wall -g3 -m64 -lncurses -o ttt
#include <assert.h>
@@ -29,7 +29,6 @@ typedef struct {
char name[MAX_TASK_NAME+1]; // Allow space for null termination char.
uint32_t time[7];
uint8_t state;
- uint32_t hash; // TODO Is this an overkill? Maybe decouple from task_t?
} task_t;
@@ -45,6 +44,38 @@ uint32_t tasks_capacity = 0;
uint32_t selected_task = -1;
+// Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character.
+// The string should have, at least length number of items.
+// The terminating null byte ('\0') is included in length.
+// The function returns the amount of items that got discarded counting from length.
+size_t truncate_string_utf8(uint8_t* string, size_t length) {
+
+ size_t idx = length - 1;
+
+ // Check for special case where no truncation is required.
+ if (string[idx] == '\0') {
+ return 0;
+ }
+
+ // Search for a non-UTF8-sequence-item so we can truncate the string.
+ while(idx > 0 && ((string[idx] & 0xC0) == 0x80)) {
+ idx--;
+ }
+
+ string[idx] = '\0';
+
+#if DEBUG
+ // TODO Check if there is a null byte before the place where we terminated the string.
+ size_t idx_dbd = idx - 1;
+ while(idx_dbd >= 0) {
+ assert(string[idx_dbd] != '\0' && "Found unexpected null byte. The user is at fault.");
+ idx_dbd--;
+ }
+#endif
+
+ return length - idx;
+}
+
char* replace_char(char* string, char find, char replace){
char *idx = string;
while((idx = strchr(idx, find)) != NULL) {
@@ -54,75 +85,8 @@ char* replace_char(char* string, char find, char replace){
return string;
}
-uint32_t CRC32(const uint8_t data[], size_t data_length) {
-
- static const uint32_t crc32_table[] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
- 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
- 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
- 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
- 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
- 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
- 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
- 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
- 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
- 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
- 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
- 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
- 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
- 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
- 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
- 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
- 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
- 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
- };
-
- uint32_t crc32 = 0xFFFFFFFFu;
-
- for (size_t i = 0; i < data_length; i++) {
- const uint32_t lookupIndex = (crc32 ^ data[i]) & 0xff;
- crc32 = (crc32 >> 8) ^ crc32_table[lookupIndex];
- }
-
- // Finalize the CRC-32 value by inverting all the bits
- crc32 ^= 0xFFFFFFFFu;
- return crc32;
-}
-
-uint32_t hash_string(const char* string) {
- size_t data_length = strlen(string);
- uint8_t* data = (uint8_t*)string;
- return CRC32(data, data_length);
-}
-
void print_task(const task_t* task) {
printf("name: '%s'\n", task->name);
- printf("hash: '%" PRIu32 "'\n", task->hash);
printf("t[0]: '%" PRIu32 "'\n", task->time[0]);
printf("t[1]: '%" PRIu32 "'\n", task->time[1]);
printf("t[2]: '%" PRIu32 "'\n", task->time[2]);
@@ -132,7 +96,7 @@ void print_task(const task_t* task) {
printf("t[6]: '%" PRIu32 "'\n", task->time[6]);
}
-void store_database(const task_t* database, uint32_t number_of_entries, const char* path_name) {
+void store_database(const task_t* database, uint32_t number_of_entries, const uint8_t* path_name) {
assert(database != NULL);
assert(path_name != NULL);
@@ -259,15 +223,14 @@ uint32_t import_database(task_t** database, const char* path_name) {
if (error != EOF)
{
- task.hash = hash_string(task.name);
number_of_entries++;
// Expand tasks capacity if required.
if (number_of_entries > capacity) {
capacity = capacity == 0 ? 16 : capacity << 1;
- *database = reallocarray(*database, capacity, SIZEOF_TASK_T);
+ *database = realloc(*database, capacity * SIZEOF_TASK_T);
if (errno != 0 || errno == ENOMEM) {
- ; // TODO reallocarray failed
+ ; // TODO realloc failed
}
}
memcpy(&((*database)[number_of_entries-1]), &task, SIZEOF_TASK_T);
@@ -277,7 +240,7 @@ uint32_t import_database(task_t** database, const char* path_name) {
}
assert(number_of_entries == 3);
- *database = reallocarray(*database, number_of_entries, SIZEOF_TASK_T);
+ *database = realloc(*database, number_of_entries * SIZEOF_TASK_T);
// TODO check for allocation errors.
// if (error != EOF) {
// fprintf(stderr, "Failed to parse database file: %s.\n", strerror(errno));
@@ -289,37 +252,50 @@ uint32_t import_database(task_t** database, const char* path_name) {
return number_of_entries;
}
-void add_task(char* name) {
-
- // Exit if task has already been added.
- uint32_t name_hash = CRC32((uint8_t*)name, sizeof(name));
- for (uint32_t idx = 0; idx < tasks_count; idx++)
- {
- if (name_hash == tasks[idx].hash && strcmp(name, tasks[idx].name) == 0)
- {
- return;
+
+void prt(uint8_t* str, uint8_t size) { // TODO Debug function... to be removed.
+ fprintf(stderr, ">");
+ int count = 0;
+ for (uint8_t idx = 0; idx < size; idx++) {
+ if (idx % 2 == 0) {
+ fprintf(stderr, " ");
}
+ fprintf(stderr, "%02x", str[idx]);
}
-
- // Expand tasks capacity if required.
- if (tasks_count >= tasks_capacity)
- {
- tasks_capacity = tasks_capacity == 0 ? 16 : tasks_capacity << 1;
- tasks = realloc(tasks, SIZEOF_TASK_T * tasks_capacity);
- }
-
- // Add new task entry.
- task_t* new_task = &tasks[tasks_count];
- memset(new_task, '0', SIZEOF_TASK_T);
- strncpy(new_task->name, name, MAX_TASK_NAME);
- new_task->name[MAX_TASK_NAME] = '\0';
- printf(">>> %s\n", new_task->name); // TODO debug
- new_task->hash = name_hash;
- tasks_count++;
+ fprintf(stderr, "\n");
}
void prototype() {
+ int count;
+ uint8_t size = 20;
+ uint8_t* test_string;
+ test_string = calloc(size, sizeof(uint8_t));
+ fprintf(stderr, "%s", "çéº𒐫\n"); // C3 A7 - C3 A9 - C2 BA - F0 92 90 AB
+ sprintf(test_string, "%s", "çéº𒐫\nxpto ");
+
+ prt(test_string, size);
+// test_string[10] = '\0';
+// prt(test_string, size);
+ uint8_t trunc = truncate_string_utf8(test_string, 3);
+ prt(test_string, size);
+ fprintf(stderr, ">>> %d : '%s'\n", trunc, test_string);
+
+
+ FILE* file = fopen("./test.txt", "w");
+
+ uint8_t length = strlen(test_string);
+ fwrite(test_string, sizeof(uint8_t), length, file);
+
+// fwrite(test_string, sizeof(uint8_t), size, file);
+
+ fclose(file);
+// truncate_string(test_string, 8);
+// fprintf(stderr, "%s", (char*)test_string);
+ return;
+
+
+
const char* done = "# -- done -- -- -- /\n";
///////////////////////////////////////////////////////////////////////////
@@ -351,17 +327,14 @@ void prototype() {
{
.name = "TASK-00 : Sample task name.",
.time = { 3,2,1,0,1,2,3 },
- .hash = hash_string("TASK-00 : Sample task name.")
},
{
.name = "TASK-01 : A wild task appears",
.time = { 1,2,3,4,5,6,7 },
- .hash = hash_string("TASK-01 : A wild task appears")
},
{
.name = "BAZINGA : Not a task!",
.time = { 11,22,33,44,55,66,77 },
- .hash = hash_string("BAZINGA : Not a task!")
}
};
memcpy(tasks, &tmp, SIZEOF_TASK_T * tasks_count);
@@ -434,17 +407,7 @@ typedef struct {
} layout_t;
layout_t* layouts = NULL;
-// {
-// .table_size = 8,
-// .table_headers_size = 9,
-// .table_headers = { " Task ", " Mon ", " Tue ", " Wed ", " Thu ", " Fri ", " Sat ", " Sun ", " Total " }
-// },
-// {
-// .table_size = 6,
-// .table_headers_size = 9,
-// .table_headers = { " T ", " M ", " T ", " W ", " T ", " F ", " S ", " S ", " # " }
-// }
-// };
+
void initialize_layouts() {
layout_t* layout = NULL;
@@ -455,6 +418,7 @@ void initialize_layouts() {
layout = &layouts[0];
layout->table_size = 8;
layout->table_headers_size = TABLE_HEADERS_SIZE;
+ // TODO Headers must be dynamic according to FIRST_DAY_OF_WEEK
layout->table_headers[0] = " Task ";
layout->table_headers[1] = " Mon ";
layout->table_headers[2] = " Tue ";
@@ -470,6 +434,7 @@ void initialize_layouts() {
layout = &layouts[1];
layout->table_size = 6;
layout->table_headers_size = TABLE_HEADERS_SIZE;
+ // TODO Headers must be dynamic according to FIRST_DAY_OF_WEEK
layout->table_headers[0] = " Task ";
layout->table_headers[1] = " M ";
layout->table_headers[2] = " T ";
@@ -611,7 +576,26 @@ void destroy_win(WINDOW *local_win);
int main(int argc, char *argv[]) {
+ // TODO Parse commands using: https://stackoverflow.com/questions/9642732/parsing-command-line-arguments-in-c
+ if (argc > 1) {
+// const char* command_version = { "--version", "-v" };
+
+ for (int idx = 1; idx < argc; idx++) {
+ if (strncmp(argv[idx], "-v", 2) == 0 || strncmp(argv[idx], "--version", 9) == 0) {
+ fprintf(stdout, "Task Time Tracker v1.0\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+
+ fprintf(stdout, "Unkown command '%s'.\nUse '%s --help' for list of commands.\n", argv[1], argv[0]);
+ return EXIT_FAILURE;
+ }
+
+
+
prototype(); // TODO
+ return EXIT_FAILURE;
initialize_layouts();
WINDOW *my_win;
@@ -687,7 +671,7 @@ int main(int argc, char *argv[]) {
free_memory();
endwin();
- return 0;
+ return EXIT_SUCCESS;
}
WINDOW *create_newwin(int height, int width, int starty, int startx) {
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..a192f18
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,66 @@
+
+uint32_t CRC32(const uint8_t data[], size_t data_length) {
+
+ static const uint32_t crc32_table[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+
+ uint32_t crc32 = 0xFFFFFFFFu;
+
+ for (size_t i = 0; i < data_length; i++) {
+ const uint32_t lookupIndex = (crc32 ^ data[i]) & 0xff;
+ crc32 = (crc32 >> 8) ^ crc32_table[lookupIndex];
+ }
+
+ // Finalize the CRC-32 value by inverting all the bits
+ crc32 ^= 0xFFFFFFFFu;
+ return crc32;
+}
+
+uint32_t hash_string(const char* string) {
+ size_t data_length = strlen(string);
+ uint8_t* data = (uint8_t*)string;
+ return CRC32(data, data_length);
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..d008702
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,29 @@
+Task Time Tracker
+=================
+
+# notes
+- Only one task may be active;
+- The log will be a circular array. During app startup the array will be loaded from a csv file. The log array should have a fixed length. Each string in the array should also have fixed length. Loading and storing it to a file will be implemented using readline and writeline operations. In order for the log file to be CSV compatible, we must always use the same format when writing to the log array. Best approach is to use a function that enfores the CSV format. The log should be written to a file every 5 minutes (not set in stone), if required; such may be done using a `log_is_dirty` flag. A possible structure for the log entries is: {uint64_t timestamp; uint8_t action[16]; uint8_t task_name[MAX_TASK_NAME+1]; uint8_t notes[16]; }.
+- Truncate UTF8 string:
+ - If null-termination is found at or before the max_size;
+
+# to-do list
+- [ ] Include check on number of char bits;
+- [x] maybe rename to task-time-tracker?
+- [ ] Remove hash stuff;
+- [ ] Tasks should have a `modified_on` timestamp field;
+- [ ] Status of task will allow to keep counting time even when the process gets terminated forcefully;
+- [ ] Make sure task names don't include commas ',';
+- [ ] Review code: char !uint8_t;
+- [ ] Change task order (use task_t tmp_task + memcpy);
+- [ ] Create task using keys: `c` and `C`;
+- [ ] Delete task using keys: `d` and `D`;
+- [ ] Change task name using keys: `F2`;
+- [ ] Add/remove time using keys: `F3`;
+- [ ] Add/remove time for any day of week;
+- [ ] Toggle task visibility using keys: `v` and `V`; when pressed, all tasks are shown and hidden tasks are shown in a ghosty theme; Ruggin task cannot be hidden;
+- [ ] Use TASK_STATUS enum [NONE, RUNNING, ARCHIVED];
+- [ ] Clone (replicate) task using keys: `r` and `R`; If task is active, mark newly created task as inactive;
+- [ ] Space invaders on konami code;
+- [ ] Rethink keys;
+- [ ] Make sure that only one task is running at each time;