diff options
| -rw-r--r-- | main.c | 200 |
1 files changed, 98 insertions, 102 deletions
@@ -1,6 +1,6 @@ // Compilation command: // - release: gcc main.c -Wall -O2 -m64 -lncurses -o ttt -// - debug : gcc main.c -Wall -g3 -m64 -lncurses -o ttt +// - debug : gcc main.c -Wall -g3 -m64 -lncurses -o ttt -D DEBUG #include <assert.h> @@ -16,7 +16,7 @@ #define STR_(X) #X // Convert to string. #define STR(X) STR_(X) // Force argument expansions before converting to string. -#define MAX_TASK_NAME 127 +#define MAX_TASK_NAME 64 #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0) #define LOG_FILE_NAME "log.txt" @@ -24,16 +24,13 @@ #define DB_CSV_PATH_NAME "./database.csv" - - -typedef struct { - char name[MAX_TASK_NAME+1]; // Allow space for null termination char. - uint32_t time[7]; +typedef /*struct __attribute__((__packed__))*/ struct { + char name[MAX_TASK_NAME]; uint64_t modified_on; + uint32_t time[7]; uint8_t state; } task_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); @@ -47,14 +44,13 @@ 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 string should have capacity for 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(char* string, size_t length) { // Check for special cases where no truncation is required. - if (length == 0 || string[length] == '\0') { - string[length] = '\0'; + if (length == 0 || string[length-1] == '\0') { return 0; } @@ -64,16 +60,6 @@ size_t truncate_string_utf8(char* string, size_t length) { idx--; } string[idx] = '\0'; - -#if DEBUG - // Check if there is a null byte before the place where we terminated the string. - size_t idx_dbg = idx; - while (idx_dbg > 0) { - assert(string[idx_dbg-1] != '\0'); - idx_dbg--; - } -#endif - return length - idx; } @@ -205,21 +191,49 @@ uint32_t import_database(task_t** database, const char* path_name) { 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. + fprintf(stderr, "Failed to open file '%s' while importing database: %s.\n", path_name, strerror(errno)); return 0; } uint32_t number_of_entries = 0; uint32_t capacity = 0; task_t task; - int error; + task_t* database_reallocation = NULL; + char *line_buffer = NULL; + size_t line_buffer_size = 0; + ssize_t read_characters = 0; + char* name_delimiter; + + // Skip header line. + fscanf(file, "%*[^\n]\n"); - fscanf(file, "%*[^\n]\n"); // Skip header line. - scan_csv: { + // Parse CSV file. + while(true) { + + read_characters = getline(&line_buffer, &line_buffer_size, file); + + // Check if reached EOF. + if (read_characters == -1) { + break; + } + + // Clear task struct. + memset(&task, '\0', SIZEOF_TASK_T); + + // Find task name string limits. + name_delimiter = strchr(line_buffer, ','); + size_t name_length = (name_delimiter - line_buffer) + 1; + if (name_length > MAX_TASK_NAME) { + name_length = MAX_TASK_NAME; + } - error = fscanf(file, - "%" STR(MAX_TASK_NAME) "[^,],%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 "\n", - task.name, + // Import task name. + memcpy(task.name, line_buffer, name_length); + truncate_string_utf8(task.name, name_length); + + // Parse task times. + if(sscanf(name_delimiter+1, + "%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32, &task.time[0], &task.time[1], &task.time[2], @@ -227,51 +241,47 @@ uint32_t import_database(task_t** database, const char* path_name) { &task.time[4], &task.time[5], &task.time[6] - ); + ) != 7) { + replace_char(line_buffer, '\n', ' '); +// fprintf(stderr, "Discarding invalid line '%s' and continuing.\n", line_buffer); + continue; + } - if (error != EOF) - { - number_of_entries++; - - // Expand tasks capacity if required. - if (number_of_entries > capacity) { - capacity = capacity == 0 ? 16 : capacity << 1; - *database = realloc(*database, capacity * SIZEOF_TASK_T); - if (errno != 0 || errno == ENOMEM) { - ; // TODO realloc failed - } + // Expand database capacity when required. + 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; } - memcpy(&((*database)[number_of_entries-1]), &task, SIZEOF_TASK_T); - - goto scan_csv; + *database = database_reallocation; } + + memcpy(&((*database)[number_of_entries-1]), &task, SIZEOF_TASK_T); } - assert(number_of_entries == 3); - *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)); -// } - - fclose(file); + free(line_buffer); - return number_of_entries; -} - - -void prt(char* str, uint8_t size) { // TODO Debug function... to be removed. - fprintf(stderr, ">"); - for (uint8_t idx = 0; idx < size; idx++) { - if (idx % 2 == 0) { - fprintf(stderr, " "); + // 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; } - fprintf(stderr, "%02x", str[idx]); } - fprintf(stderr, "\n"); + + return number_of_entries; } + enum TEST { T_NONE = 0x00, T_UTC = 0x01, @@ -280,51 +290,12 @@ enum TEST { T_LBIN = 0x08, T_ECSV = 0x10, T_ICSV = 0x20, + T_TTS = 0x40, T_ALL = 0xFF, }; void prototype(int level) { - /* - uint8_t size = 20; - char* test_string; - // C3 A7 - C3 A9 - C2 BA - 2C - F0 92 90 AB - const char* DUMMY = "çéº,,,𒐫"; - test_string = calloc(size, sizeof(char)); - sprintf(test_string, "%s", DUMMY); - - fprintf(stderr, "%s\n", test_string); - prt(test_string, size); - - // C3 A7 - C3 A9 - C2 BA - 2E - F0 92 90 AB - replace_char(test_string, ',', ','); - fprintf(stderr, "%s\n", test_string); - prt(test_string, size); - -// test_string[10] = '\0'; -// prt(test_string, size); -// test_string[1] = '\0'; - -// uint8_t trunc = truncate_string_utf8(test_string, 3); -// fprintf(stderr, "%d:%s\n", trunc, test_string); -// prt(test_string, size); - - return; - - 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"; /////////////////////////////////////////////////////////////////////////// @@ -416,6 +387,15 @@ void prototype(int level) { } /////////////////////////////////////////////////////////////////////////// + // Check task_t size + if (level & T_TTS) { + fprintf(stderr, "# check task_t size --------------------------- \\\n"); + size_t tts = sizeof(task_t); + 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); @@ -605,6 +585,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" }; @@ -629,6 +611,20 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + action = "--t_tts"; + do_action = strncmp(argv[idx], action, strlen(action)) == 0; + if (do_action) { + prototype(T_TTS); + return EXIT_SUCCESS; + } + + action = "--t_icsv"; + do_action = strncmp(argv[idx], action, strlen(action)) == 0; + if (do_action) { + prototype(T_ICSV); + return EXIT_SUCCESS; + } + action = "--test"; do_action = strncmp(argv[idx], action, strlen(action)) == 0; if (do_action) { |
