aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.c146
-rw-r--r--readme.md3
2 files changed, 63 insertions, 86 deletions
diff --git a/main.c b/main.c
index 8e5bd9c..4d27c38 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/readme.md b/readme.md
index 973c8d2..b5b955c 100644
--- a/readme.md
+++ b/readme.md
@@ -62,7 +62,7 @@ Task Time Tracker
- [ ] Compress code:
- [x] Re-do sprint_time5_utf8: -12 delta LOC;
- [x] Re-do truncate_string_utf8: 0 delta LOC;
- - [ ] Get input using `get_input(char *input, size_t size, int row, int column)` (what does it returns???):
+ - [ ] Implement `read_input_to_string_buffer`: -24 delta LOC;
- [ ] wrap malloc (and maybe others) in a function with error checking
```c
static inline void *MallocOrDie(size_t MemSize) { void *AllocMem = malloc(MemSize); /* Some implementations return null on a 0 length alloc, * we may as well allow this as it increases compatibility * with very few side effects */ if(!AllocMem && MemSize) { printf("Could not allocate memory!"); exit(-1); } return AllocMem; }
@@ -73,5 +73,6 @@ Task Time Tracker
- `sect_active(database_st *db, task_st *task)`
- [ ] Check if draw_tui may be simplified by drawing entire lines of tasks at once and draw columns separators after;
- [ ] Rename `MAX_TASK_NAME` to `TASK_NAME_BUFFER_SIZE`;
+- [ ] Try to fix flickering of ncurses;
- [ ] Review all code for bugs related to auto-cast on ptrdiff_t/size_t (signed/unsigned);
- [ ] Go over all `TODO` items;