diff options
| author | dam <dam@gudinoff> | 2022-11-13 02:30:31 +0000 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2022-11-13 02:30:31 +0000 |
| commit | bbc11330984382e926e2a8e936e494193a0e2e23 (patch) | |
| tree | 31e892128d74203f320dcf67f9c305352d7a41b7 /main.c | |
| parent | d9a4fcd19f5e50eb916b6892464e3d97185306a5 (diff) | |
| download | task-time-tracker-bbc11330984382e926e2a8e936e494193a0e2e23.tar.zst task-time-tracker-bbc11330984382e926e2a8e936e494193a0e2e23.zip | |
Fixed truncate_string_utf8. Implemented read_input_to_string_buffer to simplify getting input.
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 146 |
1 files changed, 61 insertions, 85 deletions
@@ -36,7 +36,8 @@ #include <time.h> #define VERSION "1.0" // Use only 3 chars (to fit layouts). -#define MAX_TASK_NAME 58 // Maximum task name length (includes NUL). +#define TASK_NAME_LENGTH 57 // Task name length. +#define TASK_NAME_BYTES (TASK_NAME_LENGTH+1) #define FIRST_DAY_OF_WEEK 1 // (0-6, Sunday = 0). #define NUM_WEEK_DAYS 7 // Just to avoid magic numbers. @@ -47,7 +48,7 @@ typedef struct { int64_t times[NUM_WEEK_DAYS]; - char name[MAX_TASK_NAME]; + char name[TASK_NAME_BYTES]; } task_st; typedef struct { @@ -113,24 +114,33 @@ bool inline static is_equal_to_any(const char *to_compare, const char *test_a, c || strncmp(to_compare, test_b, strlen(test_b)+1) == 0; } -// Given an UTF8 encoded string, truncate it to length without breaking any UTF8 character. +// Given an UTF8 encoded string, truncate it to length bytes without breaking any UTF8 character. // The string should have capacity for at least length + 1. // The terminating null byte ('\0') is not included in length. -// Returns the amount of items discarded. -size_t truncate_string_utf8(char *string, size_t length) { +// Returns the truncated string length. +size_t truncate_string_utf8(char *string, size_t length) { // TODO Rename to truncate_utf_string + assert(string != NULL); - // Check for special cases where no truncation is required. - if (length == 0 || string[length] == '\0') { - return 0; - } - - // Search for a non-UTF8-sequence-item so we can truncate the string. + // Count continuation bytes. size_t idx = length; - while(idx > 0 && ((string[idx] & 0xC0) == 0x80)) { + while (idx > 0 && ((string[idx - 1] & 0xC0) == 0x80)) { idx--; } - string[idx] = '\0'; - return length - idx; + int continuation_bytes = length - idx; + + // Adjust length if missing continuation bytes. + if (idx > 0 // Not empty. + // Continuation bytes are not complete. + && (!(continuation_bytes == 0 && (string[idx - 1] & 0x80) == 0x00) + && !(continuation_bytes == 1 && (string[idx - 1] & 0xE0) == 0xC0) + && !(continuation_bytes == 2 && (string[idx - 1] & 0xF0) == 0xE0) + && !(continuation_bytes == 3 && (string[idx - 1] & 0xF8) == 0xF0)) + ) { + length -= (continuation_bytes + 1); + } + + string[length] = '\0'; + return length; } // Returns true when the string is empty or consists of white space characters. @@ -600,10 +610,10 @@ bool export_to_csv(const database_st *db, const char *path) { "task", "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ); - char name[MAX_TASK_NAME]; + char name[TASK_NAME_BYTES]; task_st *limit = db->tasks + db->count; - for (task_st *task = db->tasks; task < limit; task++) { - memcpy(name, task->name, MAX_TASK_NAME); + for (task_st *task = db->tasks; task < limit; task++) { // TODO Simplify for loop. + memcpy(name, task->name, TASK_NAME_BYTES); replace_char(name, ',', ' '); fprintf(file, "%s,%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 "\n", name, task->times[0], task->times[1], task->times[2], task->times[3], task->times[4], task->times[5], task->times[6] @@ -640,8 +650,8 @@ bool import_from_csv(database_st *db, const char *path) { continue; } size_t name_length = (name_delimiter - csv_buffer) + 1; - if (name_length >= MAX_TASK_NAME) { - name_length = MAX_TASK_NAME - 1; + if (name_length > TASK_NAME_LENGTH) { + name_length = TASK_NAME_LENGTH; } // Prepare new task. @@ -695,8 +705,8 @@ bool append_to_csv(task_st *task, const char *path) { fprintf(file, "\n"); } - char name[MAX_TASK_NAME]; - memcpy(name, task->name, MAX_TASK_NAME); + char name[TASK_NAME_BYTES]; + memcpy(name, task->name, TASK_NAME_BYTES); replace_char(name, ',', ' '); fprintf(file, "%s,%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 ",%" PRId64 "\n", name, task->times[0], task->times[1], task->times[2], task->times[3], task->times[4], task->times[5], task->times[6] @@ -1082,6 +1092,22 @@ void exit_gracefully(int signal) { ungetch('q'); } +void static read_input_to_string_buffer_with_space(int row, int column, int length, int space) { + snprintf(string_buffer, string_buffer_size, "%*s", space, ""); + attron(A_UNDERLINE); + mvaddstr(row, column, string_buffer); + echo(); + curs_set(1); + mvgetnstr(row, column, string_buffer, length); + truncate_string_utf8(string_buffer, length); + noecho(); + curs_set(0); + attroff(A_UNDERLINE); +} + +void static inline read_input_to_string_buffer(int row, int column, int length) { + read_input_to_string_buffer_with_space(row, column, length, length); +} int main(int argc, char *argv[]) { @@ -1240,7 +1266,7 @@ int main(int argc, char *argv[]) { clear(); getmaxyx(stdscr, size_y, size_x); is_terminal_too_small = size_x < 60 || size_y < 3; - size_t new_size = 2047 | MAX_TASK_NAME | (size_x + 1); + size_t new_size = 2047 | TASK_NAME_BYTES | (size_x + 1); if (string_buffer_size < new_size) { string_buffer_size = new_size; string_buffer = realloc(string_buffer, string_buffer_size); @@ -1268,12 +1294,11 @@ int main(int argc, char *argv[]) { // Set new task name. time_t now_utc = time(NULL); struct tm *now_local = localtime(&now_utc); - strftime(new_task->name, MAX_TASK_NAME, "%Y-%m-%d %H:%M:%S", now_local); + strftime(new_task->name, TASK_NAME_BYTES, "%Y-%m-%d %H:%M:%S", now_local); // Select new task. TODO Maybe do this on the database? selected_task = new_task; db->selected_task = selected_task - db->tasks; - trigger_autosave(); // Force rename action. @@ -1287,32 +1312,20 @@ int main(int argc, char *argv[]) { break; } - // Prepare row to input new task name. - attron(COLOR_PAIR(selected_task_theme) | A_BOLD | A_UNDERLINE); - snprintf(string_buffer, string_buffer_size, "%*s", size_x - 2, ""); - mvaddstr(selected_task_row, 1, string_buffer); - - // Get new task name. - echo(); - curs_set(1); - memset(string_buffer, 0, string_buffer_size); - mvgetnstr(selected_task_row, 1, string_buffer, MAX_TASK_NAME-1); - noecho(); - curs_set(0); + attron(COLOR_PAIR(selected_task_theme) | A_BOLD); + read_input_to_string_buffer_with_space(selected_task_row, 1, TASK_NAME_LENGTH, size_x - 2); attrset(A_NORMAL); - // Apply new task name. if (is_empty_string(string_buffer) == false) { replace_char(string_buffer, '\t', ' '); replace_char(string_buffer, '\v', ' '); replace_char(string_buffer, '\f', ' '); replace_char(string_buffer, '\r', ' '); - memcpy(selected_task->name, string_buffer, MAX_TASK_NAME); + memcpy(selected_task->name, string_buffer, TASK_NAME_BYTES); trigger_autosave(); } - break; } @@ -1351,11 +1364,8 @@ int main(int argc, char *argv[]) { break; } - int selected_day = key - '1'; - - attron(COLOR_PAIR(selected_task_theme) | A_BOLD); - // Prepare row to input new task name. + int selected_day = key - '1'; int input_width = layout->columns[L_DAYS_IDX + selected_day].width; int input_pos_x = 1 + layout->columns[L_TITLE_IDX].width; for (int col = 0; col < selected_day; col++) { @@ -1363,23 +1373,9 @@ int main(int argc, char *argv[]) { } input_pos_x++; - // Get time delta. - // TODO Maybe use that function referred in 'g' here? - { - attron(A_UNDERLINE); - - snprintf(string_buffer, string_buffer_size, "%*s", input_width, ""); - mvaddstr(selected_task_row, input_pos_x, string_buffer); - - echo(); - curs_set(1); - mvgetnstr(selected_task_row, input_pos_x, string_buffer, input_width); - noecho(); - curs_set(0); - attroff(A_UNDERLINE); - } - + attron(COLOR_PAIR(selected_task_theme) | A_BOLD); + read_input_to_string_buffer(selected_task_row, input_pos_x, input_width); attrset(A_NORMAL); // TODO Check if parsed OK. For that, I need to read the manual to know what strtoX returns. @@ -1443,7 +1439,6 @@ int main(int argc, char *argv[]) { break; } - // Make sure we sync before applying the changes. update_times(db); @@ -1492,18 +1487,11 @@ int main(int argc, char *argv[]) { move(selected_task_row, 1); addch(ACS_CKBOARD); addstr(" Move to: "); - int input_pos_x = getcurx(stdscr); - snprintf(string_buffer, string_buffer_size, "%*s", size_x - input_pos_x - 1, ""); - attron(A_UNDERLINE); - addstr(string_buffer); // Get line number. - echo(); - curs_set(1); - mvgetnstr(selected_task_row, input_pos_x, string_buffer, size_x - input_pos_x - 1); - noecho(); - curs_set(0); - + int input_pos_x = getcurx(stdscr); + int input_width = size_x - input_pos_x - 1; + read_input_to_string_buffer(selected_task_row, input_pos_x, input_width); attrset(A_NORMAL); char *parser; // TODO Rename var. @@ -1538,21 +1526,9 @@ int main(int argc, char *argv[]) { addstr(" Go to: "); // Get line number. - // TODO Maybe convert this code block on a function? - { - int input_pos_x = getcurx(stdscr); - snprintf(string_buffer, string_buffer_size, "%*s", size_x - input_pos_x - 1, ""); - attron(A_UNDERLINE); - addstr(string_buffer); - - echo(); - curs_set(1); - mvgetnstr(selected_task_row, input_pos_x, string_buffer, size_x - input_pos_x - 1); - noecho(); - curs_set(0); - attroff(A_UNDERLINE); - } - + int input_pos_x = getcurx(stdscr); + int input_width = size_x - input_pos_x - 1; + read_input_to_string_buffer(selected_task_row, input_pos_x, input_width); attrset(A_NORMAL); char *parser; |
