чување a21adffccd6fc4e4a1a9569caf8b24c03c927e2d
родитељ 135074b08970e4a87deedc7a418f3c079854feb8
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Fri, 13 Feb 2026 10:55:08 +0100
WIP continue cleaning up code based on sled
Diffstat:
| M | TODO | | | 8 | +++++--- |
| M | TODO.done | | | 3 | +++ |
| M | poe.c | | | 2061 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
| M | poe.h | | | 1 | - |
измењених датотека: 4, додавања: 1030(+), брисања: 1043(-)
diff --git a/TODO b/TODO
@@ -5,9 +5,11 @@ TODO
entries)
[ ] Tidy up basic logic everywhere (based on sled)
-
-[x] More sophisticated "not PO file" detection - avoid opening "binary"
- (relatively speaking) files
+ [ ] Check cleanness against sled, function-by-function
+ Last: erase_search_prev_word
+ [ ] str* -> memccpy
+ [ ] Tidy up return codes and error handling
+ [ ] Introduce position_to (sled)
< > Wrap long lines? Not if it would complicate the code too much.
diff --git a/TODO.done b/TODO.done
@@ -1,6 +1,9 @@
Done todos
==========
+[x] More sophisticated "not PO file" detection - avoid opening "binary"
+ (relatively speaking) files
+
[x] Add pledge(2) and unveil(2)
[x] Switch to strlcpy, strlcat similar to sled; compare util.c and util.h to
diff --git a/poe.c b/poe.c
@@ -31,10 +31,10 @@
#define TB_IMPL
#include "draw.h"
-#include "poe.h"
#include "config.h"
#include "limit.h"
#include "po.h"
+#include "poe.h"
#include "util.h"
#include "version.h"
@@ -47,6 +47,7 @@ extern const char* prompt_overwrite;
static int
cancel_callback(struct DrawState* state, struct tb_event* ev)
{
+ assert((state != NULL) && (ev != NULL));
if (ev->ch == 'y')
save_msgstr(state);
else
@@ -55,6 +56,162 @@ cancel_callback(struct DrawState* state, struct tb_event* ev)
return 0;
}
+static int
+cancel_close(struct DrawState* state)
+{
+ assert(state != NULL);
+ if (!state->error && state->in_prompt)
+ {
+ state->in_prompt = 0;
+ *state->prompt = 0;
+ }
+ else if (state->show_help)
+ state->show_help = 0;
+ else if (state->show_edit)
+ cancel_msgstr(state);
+ else if (state->show_search)
+ cancel_search(state);
+ else
+ state->error = ERR_EXIT_KEY;
+ return 0;
+}
+
+static int
+cancel_msgstr(struct DrawState* state)
+{
+ ssize_t i;
+
+ assert(state != NULL);
+ if (!state->in_prompt && state->dirty)
+ {
+ state->in_prompt = 1;
+ STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE);
+ state->prompt_callback = cancel_callback;
+ return 0;
+ }
+
+ state->show_edit = 0;
+ state->edit_info_focused = 0;
+
+ for (i = 0; i < state->input_rows_count; i++)
+ free_bufferline(state->input_buffer + i);
+ free(state->input_buffer);
+ state->input_buffer = NULL;
+ state->input_rows_count = 0;
+ if (state->info_buffer)
+ for (i = 0; i < state->info_rows_count; i++)
+ free_bufferline(state->info_buffer + i);
+ free(state->info_buffer);
+ state->info_buffer = NULL;
+ state->info_rows_count = 0;
+ state->dirty = state->dirty_before_edit;
+ state->fuzzy_count = state->fuzzy_before_edit;
+ switch (state->input_changed_fuzzy)
+ {
+ case SET:
+ state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY;
+ break;
+ case CLEARED:
+ state->entries[state->msgid_number - 1].flags |= FL_FUZZY;
+ break;
+ default:;
+ }
+ state->input_changed_fuzzy = UNCHANGED;
+ tb_hide_cursor();
+ return 0;
+}
+
+static int
+cancel_search(struct DrawState* state)
+{
+ assert(state != NULL);
+ state->show_search = 0;
+ *state->search = 0;
+ tb_hide_cursor();
+ return 0;
+}
+
+static int
+copy_msgid_to_input(struct DrawState* state)
+{
+ struct PoEntry* current;
+ struct BufferLine* newbuf;
+ struct BufferLine* pbuffer;
+ const uint32_t* pmsgid;
+ const uint32_t* old_pmsgid;
+ uint32_t* newtext;
+ ssize_t msgid_num_lines, lines_to_init, lines_to_free, i;
+ size_t len, newlen;
+
+ assert(state != NULL);
+
+ if (state->edit_info_focused)
+ return 0;
+
+ if (!state->in_prompt && *state->input_buffer->text)
+ {
+ state->in_prompt = 1;
+ STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE);
+ state->prompt_callback = overwrite_copy_msgid_callback;
+ return 0;
+ }
+
+ if (state->msgid_number == 1)
+ {
+ state->error = ERR_ILLEGAL_ON_FIRST;
+ return 0;
+ }
+
+ assert(state->entries != NULL);
+ current = &state->entries[state->msgid_number - 1];
+ msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
+ lines_to_init = state->input_rows_count < msgid_num_lines
+ ? msgid_num_lines - state->input_rows_count
+ : 0;
+ lines_to_free = state->input_rows_count > msgid_num_lines
+ ? state->input_rows_count - msgid_num_lines
+ : 0;
+ for (i = 0; i < lines_to_free; i++)
+ free_bufferline(
+ state->input_buffer + state->input_rows_count - i - 1);
+ newbuf = realloc(state->input_buffer,
+ msgid_num_lines * sizeof(struct BufferLine));
+ pmsgid = current->msgid;
+
+ if (!newbuf)
+ return 0;
+
+ pbuffer = NULL;
+ for (i = 0; i < lines_to_init; i++)
+ {
+ pbuffer = newbuf + msgid_num_lines - 1 - i;
+ init_bufferline(pbuffer);
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ return 0;
+ pbuffer->text = newtext;
+ }
+
+ pbuffer = newbuf;
+ while (pmsgid && *pmsgid)
+ {
+ len = 0;
+ newlen = 0;
+ old_pmsgid = pmsgid;
+ len = u32_next_line(current->msgid, &pmsgid, 0, 0);
+ u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE));
+ newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE);
+ pbuffer->length = newlen;
+ pbuffer++;
+ }
+
+ state->input_rows_count = msgid_num_lines;
+ state->input_buffer = newbuf;
+ state->dirty = 1;
+
+ return 0;
+}
+
#define MAXCOPYBUF 8192
int
@@ -122,41 +279,6 @@ cp_cleanup:
return result;
}
-/*static int
-create_file(struct DrawState* state)
-{
- struct BufferLine* newchunk = NULL;
- uint32_t* newtext = NULL;
-
- assert(state != NULL);
- newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
- if (!newchunk)
- {
- state->running = 0;
- return 1;
- }
- for (ssize_t row = 0; row < ALLOC_DELTA; row++)
- {
- init_bufferline(newchunk + row);
- newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
- if (!newtext)
- {
- state->running = 0;
- return 1;
- }
- newchunk[row].size = LINE_DEFAULT;
- newchunk[row].text = newtext;
- }
- state->buffer = newchunk;
- state->buffer_size = ALLOC_DELTA;
- state->rows_count = 1;
- recalculate_line_numbers(state);
- state->coord.row = 0;
- state->dirty = 1;
-
- return 0;
-}*/
-
static char*
dirname(char* dir, char* path, const ssize_t dir_size)
{
@@ -189,1195 +311,966 @@ dirname(char* dir, char* path, const ssize_t dir_size)
return dir;
}
-static void
-goto_msgid(struct DrawState* state, const ssize_t msgid_number)
+static int
+edit_page_down(struct DrawState* state)
{
- ssize_t last_non_obsolete
- = state->real_msgid_count - state->obsolete_at_end_count;
-
- if (msgid_number > state->real_msgid_count)
- return;
-
- state->msgid_number = msgid_number;
+ assert(state != NULL);
+ if (state->edit_info_focused)
+ {
+ if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2
+ < state->info_rows_count - 1)
+ state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2;
+ return 0;
+ }
+ if (state->dirty)
+ save_msgstr(state);
+ else /* No need to call cancel_msgstr */
+ state->dirty = state->dirty_before_edit;
+ move_list_down(state);
+ state->msgstr_index = 0;
+ return show_edit(state);
+}
- if (state->msgid_number < state->first_shown_msgid)
- state->first_shown_msgid = state->msgid_number;
- else if (state->msgid_number
- > state->first_shown_msgid + state->maxy - 2)
- state->first_shown_msgid
- = state->msgid_number - (state->maxy - 2);
- else if (last_non_obsolete < state->msgid_number + state->maxy - 2)
- state->first_shown_msgid = (last_non_obsolete > state->maxy - 2
- ? last_non_obsolete - (state->maxy - 2)
- : 1);
+static int
+edit_page_up(struct DrawState* state)
+{
+ assert(state != NULL);
+ if (state->edit_info_focused)
+ {
+ if (state->info_first_shown.row > (EDIT_HEIGHT - 3) / 2)
+ state->info_first_shown.row -= (EDIT_HEIGHT - 3) / 2;
+ else
+ move_start(state);
+ return 0;
+ }
+ if (state->dirty)
+ save_msgstr(state);
+ else /* No need to call cancel_msgstr */
+ state->dirty = state->dirty_before_edit;
+ move_list_up(state);
+ state->msgstr_index = 0;
+ return show_edit(state);
}
-static void
-handle_key_event(struct tb_event* ev, struct DrawState* state)
+static int
+erase_backwards(struct DrawState* state)
{
- const struct Key* current = NULL;
+ const struct BufferLine* current_line;
+ struct BufferLine* buffer_line;
- assert((state != NULL) && (ev != NULL));
- state->error = ERR_NONE;
- errno = 0;
+ assert((state != NULL) && (state->input_buffer != NULL));
+ buffer_line = &state->input_buffer[state->coord.row];
+ if (state->edit_info_focused)
+ return 1;
- if (state->in_prompt)
+ if (state->coord.col > 0)
{
- if (ev->ch && strchr("YyNn", ev->ch))
+ ssize_t i = state->coord.col;
+ assert(buffer_line->text != NULL);
+ while (i <= buffer_line->length)
{
- if (state->prompt_callback)
- state->prompt_callback(state, ev);
- state->in_prompt = 0;
- *state->prompt = 0;
+ buffer_line->text[i - 1] = buffer_line->text[i];
+ i++;
}
- else if (ev->key == TB_KEY_ESC)
- cancel_close(state);
- return;
- }
-
- /* Remember to also edit the following condition if you
- * reassign motion keys */
- /* TODO: Condition in sled also contains:
- && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP
- && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F
- && !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch)))
- Check?
- */
- if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
- && state->show_edit)
- state->saved_column = -1;
-
- for (size_t key = 0; key < LEN(top_level_keys); key++)
- {
- current = top_level_keys + key;
- if (((current->ev.mod == ev->mod)
- && (current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch && (current->ev.ch == ev->ch)))
- if (current->callback)
- {
- current->callback(state);
- return;
- }
- }
-
- if (state->show_edit)
- {
- for (size_t key = 0; key < LEN(edit_keys); key++)
- {
- current = edit_keys + key;
- if (((current->ev.mod == ev->mod)
- && (current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback)
- {
- current->callback(state);
- return;
- }
- }
- if (!state->edit_info_focused && ev->ch)
- {
- insert_char(state, ev->ch);
- return;
- }
- }
-
- if (state->show_search)
- {
- for (size_t key = 0; key < LEN(search_keys); key++)
- {
- current = search_keys + key;
- if (((current->ev.mod == ev->mod)
- && (current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback)
- {
- current->callback(state);
- return;
- }
- }
- if (ev->ch)
+ buffer_line->text[i - 1] = 0;
+ buffer_line->length--;
+ /* TODO: position_to */
+ state->coord.col--;
+ if (state->input_first_shown.col > 0)
{
- insert_search_char(state, ev->ch);
- return;
+ current_line = &state->input_buffer[state->coord.row];
+ if (display_length(current_line->text,
+ current_line->length)
+ >= state->edit_width - 4)
+ state->input_first_shown.col--;
}
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row]
+ .text,
+ state->coord.col);
+ if (state->input_display_column < state->input_first_shown.col)
+ state->input_first_shown.col
+ = state->input_display_column;
+ state->dirty = 1;
}
-
- if (state->show_help)
- {
- state->error = ERR_DLG_OPEN;
- return;
- }
-
- for (size_t key = 0; key < LEN(main_keys); key++)
+ else if (state->coord.row > 0)
{
- current = main_keys + key;
- if (((current->ev.mod == ev->mod)
- && (current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch && (current->ev.ch == ev->ch)))
- if (current->callback)
- {
- current->callback(state);
- return;
- }
+ move_up(state);
+ move_end(state);
+ erase_forward(state);
}
-
- if (state->edit_info_focused)
- state->error = ERR_DLG_FOCUS_EDIT;
- else if (state->show_edit || state->show_search)
- state->error = ERR_UNKNOWN_KEY;
- else
- state->error = ERR_UNKNOWN_KEY_HELP;
+ return 0;
}
static int
-insert_char(struct DrawState* state, uint32_t ch)
+erase_forward(struct DrawState* state)
{
struct BufferLine* buffer_line;
- ssize_t len;
- assert(state != NULL);
+ size_t i;
+
+ assert((state != NULL) && (state->input_buffer != NULL));
+ buffer_line = &state->input_buffer[state->coord.row];
if (state->edit_info_focused)
return 1;
- buffer_line = &state->input_buffer[state->coord.row];
- len = buffer_line->length;
- if (state->coord.col < MAXBUFLINE - 1)
+
+ assert(buffer_line->text != NULL);
+ if (buffer_line->text[state->coord.col])
{
- ssize_t i = len + 1;
- int add_null = buffer_line->text[state->coord.col] == 0;
- while (i < MAXBUFLINE - 2 && i > state->coord.col)
+ i = state->coord.col;
+ while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
{
- buffer_line->text[i] = buffer_line->text[i - 1];
- i--;
+ buffer_line->text[i] = buffer_line->text[i + 1];
+ i++;
}
- buffer_line->text[state->coord.col++] = ch;
- buffer_line->length++;
- if (state->coord.col == MAXBUFLINE)
- state->coord.col = MAXBUFLINE - 1;
- state->input_display_column
- = display_length(state->input_buffer[state->coord.row]
- .text,
- state->coord.col);
- if (state->input_display_column - state->input_first_shown.col
- > state->edit_width - 4)
+ buffer_line->text[i] = 0;
+ buffer_line->length--;
+ state->dirty = 1;
+ }
+ else if (state->coord.row < state->input_rows_count - 1)
+ {
+ if (!join_lines(state, state->coord.row, 1))
{
- state->input_first_shown.col
- = state->input_display_column
- - state->edit_width + 4;
- state->input_display_column = display_length(
- state->input_buffer[state->coord.row].text,
- state->coord.col);
+ state->error = ERR_CANT_ALLOC;
+ state->running = 0;
}
- if (add_null)
- buffer_line->text[state->coord.col] = 0;
}
- state->dirty = 1;
return 0;
}
static int
-insert_line(struct DrawState* state)
+erase_prev_word(struct DrawState* state)
{
- struct BufferLine* newbuf = NULL;
+ uint32_t* pline;
+ ssize_t len;
+ int i, j, d;
+ assert(state != NULL);
if (state->edit_info_focused)
return 1;
+ i = state->coord.col;
+ len = state->input_buffer[state->coord.row].length;
+ pline = state->input_buffer[state->coord.row].text;
- state->input_rows_count++;
- newbuf = realloc(state->input_buffer,
- state->input_rows_count * sizeof(struct BufferLine));
- if (!newbuf)
- {
- state->error = ERR_CANT_ALLOC;
- state->running = 0;
- return 0;
- }
+ if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+ while (i > 0 && u32_is_word_boundary(pline[i], 0))
+ i--;
+ while (i > 0 && !u32_is_word_boundary(pline[i], 0)
+ && !u32_is_word_boundary(pline[i - 1], 0))
+ i--;
- state->input_buffer = newbuf;
- init_bufferline(&state->input_buffer[state->input_rows_count - 1]);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- state->error = ERR_CANT_ALLOC;
- state->running = 0;
- return 0;
- }
- state->input_buffer[state->input_rows_count - 1].text = newtext;
- for (ssize_t i = state->input_rows_count - 1; i > state->coord.row + 1;
- i--)
- {
- u32_strncpy(state->input_buffer[i].text,
- state->input_buffer[i - 1].text, MAXBUFLINE);
- state->input_buffer[i].length
- = state->input_buffer[i - 1].length;
- }
- u32_strncpy(state->input_buffer[state->coord.row + 1].text,
- state->input_buffer[state->coord.row].text + state->coord.col,
- MAXBUFLINE);
- state->input_buffer[state->coord.row + 1].length
- = u32_strlen(state->input_buffer[state->coord.row + 1].text);
- state->input_buffer[state->coord.row].text[state->coord.col] = 0;
- state->input_buffer[state->coord.row].length
- = u32_strlen(state->input_buffer[state->coord.row].text);
+ d = state->coord.col - i;
- state->coord.row++;
- if (state->coord.row
- > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2)
- state->input_first_shown.row
- = state->coord.row - (EDIT_HEIGHT - 3) / 2;
- move_start(state);
- state->dirty = 1;
+ for (j = i; j < len - d; j++)
+ pline[j] = pline[j + d];
+ len -= d;
+ state->input_buffer[state->coord.row].length = len;
+ pline[len] = 0;
+ state->coord.col = i;
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row].text,
+ state->coord.col);
+ if (state->input_display_column < state->input_first_shown.col)
+ state->input_first_shown.col = state->input_display_column;
+ state->dirty = 1;
return 0;
}
-static void
-insert_search_char(struct DrawState* state, uint32_t ch)
+static int
+erase_search_backwards(struct DrawState* state)
{
- ssize_t len = u32_strlen(state->search);
- if (state->search_column < MAXSEARCH - 1)
+ size_t i;
+ assert(state != NULL);
+ if (state->search_column > 0)
{
- ssize_t i = len + 1;
- int add_null = state->search[state->search_column] == 0;
- while (i < MAXSEARCH - 2 && i > state->search_column)
+ i = state->search_column;
+ do
{
- state->search[i] = state->search[i - 1];
- i--;
+ state->search[i - 1] = state->search[i];
+ i++;
+ } while (state->search[i]);
+ state->search[i - 1] = 0;
+ state->search_column--;
+ if (state->search_column < state->search_first_shown_column)
+ {
+ if (state->search_column > state->search_width - 4)
+ state->search_first_shown_column
+ = state->search_column
+ - state->search_width + 4;
+ else
+ state->search_first_shown_column = 0;
}
- state->search[state->search_column++] = ch;
- if (state->search_column == MAXSEARCH)
- state->search_column = MAXSEARCH - 1;
state->search_display_column
= display_length(state->search, state->search_column);
- if (state->search_display_column
- - state->search_first_shown_column
- > state->search_width - 4)
- {
- state->search_first_shown_column
- = state->search_display_column
- - state->search_width + 4;
- state->search_display_column
- = display_length(state->search,
- state->search_column);
- }
- if (add_null)
- state->search[state->search_column] = 0;
- next_match(state);
+ return next_match(state);
}
+ return 0;
}
-static struct DrawState*
-join_lines(struct DrawState* state, size_t first_line, int forward)
+static int
+erase_search_forward(struct DrawState* state)
{
- state->coord.col = state->input_buffer[first_line].length;
- state->input_display_column
- = display_length(state->input_buffer[state->coord.row].text,
- state->coord.col);
- u32_strncat(state->input_buffer[first_line].text,
- state->input_buffer[first_line + 1].text, MAXBUFLINE);
- state->input_buffer[first_line].length
- = u32_strlen(state->input_buffer[first_line].text);
- if (!forward)
- {
- state->coord.row--;
- if (state->coord.row < 0)
- state->coord.row = 0;
- }
- for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++)
+ size_t i;
+ assert(state != NULL);
+ if (state->search[state->search_column])
{
- u32_strncpy(state->input_buffer[i].text,
- state->input_buffer[i + 1].text, MAXBUFLINE);
- state->input_buffer[i].length
- = state->input_buffer[i + 1].length;
- }
- free_bufferline(state->input_buffer + state->input_rows_count - 1);
- state->input_rows_count--;
- struct BufferLine* newbuf = realloc(state->input_buffer,
- state->input_rows_count * sizeof(struct BufferLine));
- if (!newbuf)
- return NULL;
- state->input_buffer = newbuf;
- state->dirty = 1;
+ i = state->search_column;
+ while (i < MAXSEARCH - 2 && state->search[i + 1])
+ {
+ state->search[i] = state->search[i + 1];
+ i++;
+ }
+ state->search[i] = 0;
- return state;
+ return next_match(state);
+ }
+ return 0;
}
-static struct BufferLine*
-load_info(struct DrawState* state)
+static int
+erase_search_prev_word(struct DrawState* state)
{
- struct PoEntry* first = &state->entries[0];
- struct PoEntry* current = &state->entries[state->msgid_number - 1];
- struct BufferLine* newbuf = NULL;
- struct BufferLine* pnewbuf = NULL;
- const uint32_t* pumsgid = NULL;
- ssize_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
- ssize_t msgid_plural_num_lines
- = u32_lines_in_string(current->msgid_plural, 0, 0);
- ssize_t num_lines = 0;
- ssize_t newlen = 0;
+ uint32_t* pline;
+ ssize_t len;
+ int i, j, d;
- for (ssize_t i = 0; i < state->info_rows_count; i++)
- free_bufferline(state->info_buffer + i);
- free(state->info_buffer);
+ assert(state != NULL);
+ pline = state->search;
+ len = u32_strlen(state->search);
+ i = state->search_column;
+ if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
+ i--;
+ while (i > 0 && u32_is_word_boundary(pline[i], 0))
+ i--;
+ while (i > 0 && !u32_is_word_boundary(pline[i], 0)
+ && !u32_is_word_boundary(pline[i - 1], 0))
+ i--;
- state->info_rows_maxlen = 0;
+ d = state->search_column - i;
- if (current->msgid)
- num_lines += msgid_num_lines + 1;
- if (current->msgid_plural)
+ for (j = i; j < len - d; j++)
+ pline[j] = pline[j + d];
+
+ len -= state->search_column - i;
+ pline[len] = 0;
+ state->search_column = i;
+ state->search_display_column
+ = display_length(state->search, state->search_column);
+
+ if (state->search_column < state->search_first_shown_column)
{
- num_lines += msgid_plural_num_lines + 1;
- if (first->plural_forms)
- num_lines += 3;
+ if (state->search_column > state->search_width - 4)
+ state->search_first_shown_column = state->search_column
+ - state->search_width + 4;
+ else
+ state->search_first_shown_column = 0;
}
- if (current->comments)
- num_lines += current->comment_lines + 1;
- if (current->extr_comments)
- num_lines += current->extr_comment_lines + 1;
- if (current->ref_comments)
- num_lines += current->ref_comment_lines + 1;
- newbuf = calloc(num_lines, sizeof(struct BufferLine));
- if (!newbuf)
- return NULL;
+ state->search_display_column
+ = display_length(state->search, state->search_column);
- pnewbuf = newbuf;
- for (ssize_t i = 0; i < num_lines; i++)
+ return next_match(state);
+}
+
+static int
+erase_search_to_end(struct DrawState* state)
+{
+ state->search[state->search_column] = 0;
+ move_search_end(state);
+ return next_match(state);
+}
+
+static int
+erase_search_to_start(struct DrawState* state)
+{
+ size_t i = state->search_column;
+ size_t len = u32_strlen(state->search);
+ do
{
- init_bufferline(pnewbuf);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- return NULL;
- pnewbuf->text = newtext;
- pnewbuf++;
- }
+ state->search[i - state->search_column] = state->search[i];
+ i++;
+ } while (state->search[i]);
+ state->search[len - state->search_column] = 0;
+ move_search_start(state);
+ return next_match(state);
+}
- state->info_rows_count = num_lines;
+static int
+erase_to_end(struct DrawState* state)
+{
+ struct BufferLine* buffer_line = NULL;
- pnewbuf = newbuf;
- newlen = 0;
- pumsgid = NULL;
- if (current->msgid)
+ if (state->edit_info_focused)
+ return 1;
+
+ buffer_line = &state->input_buffer[state->coord.row];
+ buffer_line->text[state->coord.col] = 0;
+ buffer_line->length = state->coord.col;
+ move_end(state);
+ state->dirty = 1;
+ return 0;
+}
+
+static int
+erase_to_start(struct DrawState* state)
+{
+ struct BufferLine* buffer_line;
+ ssize_t i;
+ ssize_t len;
+
+ i = state->coord.col;
+ buffer_line = &state->input_buffer[state->coord.row];
+ len = buffer_line->length;
+
+ if (state->edit_info_focused)
+ return 1;
+
+ do
{
- pumsgid = current->msgid;
- newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- while (*pumsgid)
- {
- size_t len = 0;
- const uint32_t* old_pumsgid = pumsgid;
+ buffer_line->text[i - state->coord.col] = buffer_line->text[i];
+ i++;
+ } while (buffer_line->text[i]);
+ buffer_line->text[len - state->coord.col] = 0;
+ buffer_line->length = len - state->coord.col;
+ move_start(state);
+ state->dirty = 1;
+ return 0;
+}
- len = u32_next_line(current->msgid, &pumsgid, 0, 0);
- u32_strncpy(pnewbuf->text, old_pumsgid,
- MIN(len + 1, MAXBUFLINE));
- newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
- }
+static int
+exit_program(struct DrawState* state)
+{
+ if (state->show_edit)
+ {
+ state->error = ERR_DLG_OPEN;
+ return 0;
}
- if (current->msgid_plural)
+ if (state->dirty)
{
- newlen = u32_u8_strncpy(pnewbuf->text,
- "msgid_plural:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- pumsgid = current->msgid_plural;
- while (*pumsgid)
- {
- size_t len = 0;
- const uint32_t* old_pumsgid = pumsgid;
+ state->in_prompt = 1;
+ STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE);
+ state->prompt_callback = quit_callback;
+ }
+ else
+ state->running = 0;
+ //*state->error = 0;
+ return 0;
+}
- len = u32_next_line(current->msgid_plural, &pumsgid, 0,
- 0);
- u32_strncpy(pnewbuf->text, old_pumsgid,
- MIN(len + 1, MAXBUFLINE));
- newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
- }
- if (first->plural_forms)
- {
- newlen = u32_u8_strncpy(pnewbuf->text,
- "Plural forms:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- newlen = u32_strncpy(pnewbuf->text, first->plural_forms,
- MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
- }
- }
- if (current->comments)
+static void
+goto_msgid(struct DrawState* state, const ssize_t msgid_number)
+{
+ ssize_t last_non_obsolete
+ = state->real_msgid_count - state->obsolete_at_end_count;
+
+ if (msgid_number > state->real_msgid_count)
+ return;
+
+ state->msgid_number = msgid_number;
+
+ if (state->msgid_number < state->first_shown_msgid)
+ state->first_shown_msgid = state->msgid_number;
+ else if (state->msgid_number
+ > state->first_shown_msgid + state->maxy - 2)
+ state->first_shown_msgid
+ = state->msgid_number - (state->maxy - 2);
+ else if (last_non_obsolete < state->msgid_number + state->maxy - 2)
+ state->first_shown_msgid = (last_non_obsolete > state->maxy - 2
+ ? last_non_obsolete - (state->maxy - 2)
+ : 1);
+}
+
+static void
+handle_key_event(struct tb_event* ev, struct DrawState* state)
+{
+ const struct Key* current = NULL;
+
+ assert((state != NULL) && (ev != NULL));
+ state->error = ERR_NONE;
+ errno = 0;
+
+ if (state->in_prompt)
{
- newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- for (size_t i = 0; i < current->comment_lines; i++)
+ if (ev->ch && strchr("YyNn", ev->ch))
{
- uint32_t* comment = current->comments[i];
- size_t len = u32_strlen(comment);
- newlen = u32_strncpy(pnewbuf->text, comment,
- MIN(len + 1, MAXBUFLINE));
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
+ if (state->prompt_callback)
+ state->prompt_callback(state, ev);
+ state->in_prompt = 0;
+ *state->prompt = 0;
}
+ else if (ev->key == TB_KEY_ESC)
+ cancel_close(state);
+ return;
}
- if (current->extr_comments)
+
+ /* Remember to also edit the following condition if you
+ * reassign motion keys */
+ /* TODO: Condition in sled also contains:
+ && ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP
+ && ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F
+ && !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch)))
+ Check?
+ */
+ if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
+ && state->show_edit)
+ state->saved_column = -1;
+
+ for (size_t key = 0; key < LEN(top_level_keys); key++)
{
- newlen = u32_u8_strncpy(pnewbuf->text,
- "Translation comments:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- for (size_t i = 0; i < current->extr_comment_lines; i++)
+ current = top_level_keys + key;
+ if (((current->ev.mod == ev->mod)
+ && (current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch && (current->ev.ch == ev->ch)))
+ if (current->callback)
+ {
+ current->callback(state);
+ return;
+ }
+ }
+
+ if (state->show_edit)
+ {
+ for (size_t key = 0; key < LEN(edit_keys); key++)
{
- uint32_t* extr_comment = current->extr_comments[i];
- size_t len = u32_strlen(extr_comment);
- newlen = u32_strncpy(pnewbuf->text, extr_comment,
- MIN(len + 1, MAXBUFLINE));
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
+ current = edit_keys + key;
+ if (((current->ev.mod == ev->mod)
+ && (current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch
+ && (current->ev.ch == ev->ch)))
+ if (current->callback)
+ {
+ current->callback(state);
+ return;
+ }
+ }
+ if (!state->edit_info_focused && ev->ch)
+ {
+ insert_char(state, ev->ch);
+ return;
}
}
- if (current->ref_comments)
+
+ if (state->show_search)
{
- newlen = u32_u8_strncpy(pnewbuf->text,
- "References:", MAXBUFLINE);
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
- pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
- pnewbuf++;
- for (size_t i = 0; i < current->ref_comment_lines; i++)
+ for (size_t key = 0; key < LEN(search_keys); key++)
{
- char* ref_comment = current->ref_comments[i];
- size_t len = strlen(ref_comment);
- newlen = u32_u8_strncpy(pnewbuf->text, ref_comment,
- MIN(len + 1, MAXBUFLINE));
- pnewbuf->length = newlen;
- if (newlen > state->info_rows_maxlen)
- state->info_rows_maxlen = newlen;
- pnewbuf++;
+ current = search_keys + key;
+ if (((current->ev.mod == ev->mod)
+ && (current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch
+ && (current->ev.ch == ev->ch)))
+ if (current->callback)
+ {
+ current->callback(state);
+ return;
+ }
+ }
+ if (ev->ch)
+ {
+ insert_search_char(state, ev->ch);
+ return;
}
}
- state->info_buffer = newbuf;
-
- return state->info_buffer;
-}
-
-static struct BufferLine*
-load_msgstr(struct DrawState* state)
-{
- struct PoEntry* current;
- struct BufferLine* newbuf;
- struct BufferLine* pnewbuf;
- size_t num_lines;
- int msgstr_index;
-
- assert((state != NULL) && (state->entries != NULL));
- current = &state->entries[state->msgid_number - 1];
- newbuf = NULL;
- pnewbuf = NULL;
- msgstr_index = state->msgstr_index;
- num_lines = current->msgstr_count == 0
- ? 1
- : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0);
-
- for (ssize_t i = 0; i < state->input_rows_count; i++)
- free_bufferline(state->input_buffer + i);
- free(state->input_buffer);
-
- newbuf = calloc(num_lines, sizeof(struct BufferLine));
- if (!newbuf)
- return NULL;
-
- pnewbuf = newbuf;
- for (size_t i = 0; i < num_lines; i++)
+ if (state->show_help)
{
- init_bufferline(pnewbuf);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- return NULL;
- pnewbuf->text = newtext;
- pnewbuf++;
+ state->error = ERR_DLG_OPEN;
+ return;
}
- state->input_rows_count = num_lines;
-
- if (current->msgstr_count > 0)
+ for (size_t key = 0; key < LEN(main_keys); key++)
{
- const uint32_t* pumsgstr = *(current->msgstr + msgstr_index);
- if (pumsgstr)
- {
- pnewbuf = newbuf;
- while (*pumsgstr)
+ current = main_keys + key;
+ if (((current->ev.mod == ev->mod)
+ && (current->ev.key == ev->key) && !ev->ch)
+ || (current->ev.ch && (current->ev.ch == ev->ch)))
+ if (current->callback)
{
- size_t len = 0;
- size_t newlen = 0;
- const uint32_t* old_pumsgstr = pumsgstr;
- len = u32_next_line(*(current->msgstr
- + msgstr_index),
- &pumsgstr, 0, 0);
- u32_strncpy(pnewbuf->text, old_pumsgstr,
- MIN(len + 1, MAXBUFLINE));
- newlen = u32_decode_tabs(pnewbuf->text,
- MAXBUFLINE);
- pnewbuf->length = newlen;
- pnewbuf++;
+ current->callback(state);
+ return;
}
- }
- else
- newbuf[0].length = 0;
}
- else
- newbuf[0].length = 0;
- state->input_buffer = newbuf;
- return state->input_buffer;
+ if (state->edit_info_focused)
+ state->error = ERR_DLG_FOCUS_EDIT;
+ else if (state->show_edit || state->show_search)
+ state->error = ERR_UNKNOWN_KEY;
+ else
+ state->error = ERR_UNKNOWN_KEY_HELP;
}
-static size_t
-next_msgid(struct DrawState* state, const int delta)
+static int
+hide_search(struct DrawState* state)
{
- size_t ret = state->msgid_number;
- size_t start = ret;
- size_t last_non_obsolete
- = state->real_msgid_count - state->obsolete_at_end_count;
-
- while (ret < last_non_obsolete + 1)
- {
- ret++;
- if (!state->entries[ret - 1].obsolete && ret >= start + delta)
- break;
- }
-
- return ret == last_non_obsolete + 1 ? last_non_obsolete : ret;
-}
-
-static int
-overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev)
-{
- if (ev->ch == 'y')
- copy_msgid_to_input(state);
- state->prompt_callback = NULL;
+ state->show_search = 0;
+ tb_hide_cursor();
return 0;
}
static int
-overwrite_paste_callback(struct DrawState* state, struct tb_event* ev)
+insert_char(struct DrawState* state, uint32_t ch)
{
- if (ev->ch == 'y')
- paste_paste_buffer_to_input(state);
- state->prompt_callback = NULL;
+ struct BufferLine* buffer_line;
+ ssize_t len;
+ assert(state != NULL);
+ if (state->edit_info_focused)
+ return 1;
+ buffer_line = &state->input_buffer[state->coord.row];
+ len = buffer_line->length;
+ if (state->coord.col < MAXBUFLINE - 1)
+ {
+ ssize_t i = len + 1;
+ int add_null = buffer_line->text[state->coord.col] == 0;
+ while (i < MAXBUFLINE - 2 && i > state->coord.col)
+ {
+ buffer_line->text[i] = buffer_line->text[i - 1];
+ i--;
+ }
+ buffer_line->text[state->coord.col++] = ch;
+ buffer_line->length++;
+ if (state->coord.col == MAXBUFLINE)
+ state->coord.col = MAXBUFLINE - 1;
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row]
+ .text,
+ state->coord.col);
+ if (state->input_display_column - state->input_first_shown.col
+ > state->edit_width - 4)
+ {
+ state->input_first_shown.col
+ = state->input_display_column
+ - state->edit_width + 4;
+ state->input_display_column = display_length(
+ state->input_buffer[state->coord.row].text,
+ state->coord.col);
+ }
+ if (add_null)
+ buffer_line->text[state->coord.col] = 0;
+ }
+ state->dirty = 1;
return 0;
}
-static size_t
-prev_msgid(struct DrawState* state, const int delta)
+static int
+insert_line(struct DrawState* state)
{
- ssize_t ret = state->msgid_number;
- ssize_t start = ret;
+ struct BufferLine* newbuf = NULL;
- if (start < delta)
- return state->real_msgid_count ? 1 : 0;
+ if (state->edit_info_focused)
+ return 1;
- while (ret > 1)
+ state->input_rows_count++;
+ newbuf = realloc(state->input_buffer,
+ state->input_rows_count * sizeof(struct BufferLine));
+ if (!newbuf)
{
- ret--;
- if (!state->entries[ret - 1].obsolete && ret <= start - delta)
- break;
+ state->error = ERR_CANT_ALLOC;
+ state->running = 0;
+ return 0;
}
- return ret;
-}
+ state->input_buffer = newbuf;
+ init_bufferline(&state->input_buffer[state->input_rows_count - 1]);
+ uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ {
+ state->error = ERR_CANT_ALLOC;
+ state->running = 0;
+ return 0;
+ }
+ state->input_buffer[state->input_rows_count - 1].text = newtext;
+ for (ssize_t i = state->input_rows_count - 1; i > state->coord.row + 1;
+ i--)
+ {
+ u32_strncpy(state->input_buffer[i].text,
+ state->input_buffer[i - 1].text, MAXBUFLINE);
+ state->input_buffer[i].length
+ = state->input_buffer[i - 1].length;
+ }
+ u32_strncpy(state->input_buffer[state->coord.row + 1].text,
+ state->input_buffer[state->coord.row].text + state->coord.col,
+ MAXBUFLINE);
+ state->input_buffer[state->coord.row + 1].length
+ = u32_strlen(state->input_buffer[state->coord.row + 1].text);
+ state->input_buffer[state->coord.row].text[state->coord.col] = 0;
+ state->input_buffer[state->coord.row].length
+ = u32_strlen(state->input_buffer[state->coord.row].text);
-int
-print_error(const int code, const char* msg, ...)
-{
- char buf[MAXBUFLINE];
- va_list args;
- va_start(args, msg);
- vsnprintf(buf, sizeof(buf), msg, args);
- va_end(args);
- fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf);
- return code;
-}
+ state->coord.row++;
+ if (state->coord.row
+ > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2)
+ state->input_first_shown.row
+ = state->coord.row - (EDIT_HEIGHT - 3) / 2;
+ move_start(state);
+ state->dirty = 1;
-static int
-quit_callback(struct DrawState* state, struct tb_event* ev)
-{
- if (ev->ch == 'y')
- write_file(state);
- state->prompt_callback = NULL;
- state->running = 0;
return 0;
}
-void
-redraw_status(struct DrawState* state)
+static void
+insert_search_char(struct DrawState* state, uint32_t ch)
{
- assert(state != NULL);
- if (state->recalculate_size)
+ ssize_t len = u32_strlen(state->search);
+ if (state->search_column < MAXSEARCH - 1)
{
- recalculate_size(state);
- state->recalculate_size = 0;
+ ssize_t i = len + 1;
+ int add_null = state->search[state->search_column] == 0;
+ while (i < MAXSEARCH - 2 && i > state->search_column)
+ {
+ state->search[i] = state->search[i - 1];
+ i--;
+ }
+ state->search[state->search_column++] = ch;
+ if (state->search_column == MAXSEARCH)
+ state->search_column = MAXSEARCH - 1;
+ state->search_display_column
+ = display_length(state->search, state->search_column);
+ if (state->search_display_column
+ - state->search_first_shown_column
+ > state->search_width - 4)
+ {
+ state->search_first_shown_column
+ = state->search_display_column
+ - state->search_width + 4;
+ state->search_display_column
+ = display_length(state->search,
+ state->search_column);
+ }
+ if (add_null)
+ state->search[state->search_column] = 0;
+
+ next_match(state);
}
- (void)draw_status(state);
- tb_present();
}
static int
-cancel_close(struct DrawState* state)
+insert_search_space(struct DrawState* state)
{
- assert(state != NULL);
- if (!state->error && state->in_prompt)
- {
- state->in_prompt = 0;
- *state->prompt = 0;
- }
- else if (state->show_help)
- state->show_help = 0;
- else if (state->show_edit)
- cancel_msgstr(state);
- else if (state->show_search)
- cancel_search(state);
- else
- state->error = ERR_EXIT_KEY;
-
+ insert_search_char(state, (uint32_t)L' ');
return 0;
}
static int
-cancel_msgstr(struct DrawState* state)
+insert_search_tab(struct DrawState* state)
{
- if (!state->in_prompt && state->dirty)
- {
- state->in_prompt = 1;
- STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE);
- state->prompt_callback = cancel_callback;
- return 0;
- }
-
- state->show_edit = 0;
- state->edit_info_focused = 0;
-
- for (ssize_t i = 0; i < state->input_rows_count; i++)
- free_bufferline(state->input_buffer + i);
- free(state->input_buffer);
- state->input_buffer = NULL;
- state->input_rows_count = 0;
- if (state->info_buffer)
- for (ssize_t i = 0; i < state->info_rows_count; i++)
- free_bufferline(state->info_buffer + i);
- free(state->info_buffer);
- state->info_buffer = NULL;
- state->info_rows_count = 0;
- state->dirty = state->dirty_before_edit;
- state->fuzzy_count = state->fuzzy_before_edit;
- switch (state->input_changed_fuzzy)
- {
- case SET:
- state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY;
- break;
- case CLEARED:
- state->entries[state->msgid_number - 1].flags |= FL_FUZZY;
- break;
- default:;
- }
- state->input_changed_fuzzy = UNCHANGED;
- tb_hide_cursor();
+ insert_search_char(state, (uint32_t)L'\t');
return 0;
}
static int
-cancel_search(struct DrawState* state)
+insert_space(struct DrawState* state)
{
- state->show_search = 0;
- *state->search = 0;
- tb_hide_cursor();
+ insert_char(state, (uint32_t)L' ');
return 0;
}
static int
-copy_msgid_to_input(struct DrawState* state)
+insert_tab(struct DrawState* state)
{
- struct PoEntry* current;
- struct BufferLine* newbuf;
- struct BufferLine* pbuffer;
- const uint32_t* pmsgid;
- ssize_t msgid_num_lines;
- ssize_t lines_to_init;
- ssize_t lines_to_free;
-
- if (state->edit_info_focused)
- return 0;
+ insert_char(state, (uint32_t)L'\t');
+ return 0;
+}
- if (!state->in_prompt && *state->input_buffer->text)
- {
- state->in_prompt = 1;
- STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE);
- state->prompt_callback = overwrite_copy_msgid_callback;
- return 0;
- }
-
- if (state->msgid_number == 1)
- {
- state->error = ERR_ILLEGAL_ON_FIRST;
- return 0;
- }
-
- current = &state->entries[state->msgid_number - 1];
- msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
- lines_to_init = state->input_rows_count < msgid_num_lines
- ? msgid_num_lines - state->input_rows_count
- : 0;
- lines_to_free = state->input_rows_count > msgid_num_lines
- ? state->input_rows_count - msgid_num_lines
- : 0;
- for (ssize_t i = 0; i < lines_to_free; i++)
- free_bufferline(
- state->input_buffer + state->input_rows_count - i - 1);
- newbuf = realloc(state->input_buffer,
- msgid_num_lines * sizeof(struct BufferLine));
- pmsgid = current->msgid;
-
- if (!newbuf)
- return 0;
-
- pbuffer = NULL;
- for (ssize_t i = 0; i < lines_to_init; i++)
- {
- uint32_t* newtext;
- pbuffer = newbuf + msgid_num_lines - 1 - i;
- init_bufferline(pbuffer);
- newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- return 0;
- pbuffer->text = newtext;
- }
-
- pbuffer = newbuf;
- while (pmsgid && *pmsgid)
+static struct DrawState*
+join_lines(struct DrawState* state, size_t first_line, int forward)
+{
+ state->coord.col = state->input_buffer[first_line].length;
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row].text,
+ state->coord.col);
+ u32_strncat(state->input_buffer[first_line].text,
+ state->input_buffer[first_line + 1].text, MAXBUFLINE);
+ state->input_buffer[first_line].length
+ = u32_strlen(state->input_buffer[first_line].text);
+ if (!forward)
{
- size_t len = 0;
- size_t newlen = 0;
- const uint32_t* old_pmsgid = pmsgid;
- len = u32_next_line(current->msgid, &pmsgid, 0, 0);
- u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE));
- newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE);
- pbuffer->length = newlen;
- pbuffer++;
+ state->coord.row--;
+ if (state->coord.row < 0)
+ state->coord.row = 0;
}
-
- state->input_rows_count = msgid_num_lines;
- state->input_buffer = newbuf;
- state->dirty = 1;
-
- return 0;
-}
-
-static int
-edit_page_down(struct DrawState* state)
-{
- if (state->edit_info_focused)
+ for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++)
{
- if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2
- < state->info_rows_count - 1)
- state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2;
- return 0;
+ u32_strncpy(state->input_buffer[i].text,
+ state->input_buffer[i + 1].text, MAXBUFLINE);
+ state->input_buffer[i].length
+ = state->input_buffer[i + 1].length;
}
+ free_bufferline(state->input_buffer + state->input_rows_count - 1);
+ state->input_rows_count--;
+ struct BufferLine* newbuf = realloc(state->input_buffer,
+ state->input_rows_count * sizeof(struct BufferLine));
+ if (!newbuf)
+ return NULL;
+ state->input_buffer = newbuf;
+ state->dirty = 1;
- if (state->dirty)
- save_msgstr(state);
- else /* No need to call cancel_msgstr */
- state->dirty = state->dirty_before_edit;
- move_list_down(state);
- state->msgstr_index = 0;
- return show_edit(state);
+ return state;
}
-static int
-edit_page_up(struct DrawState* state)
+static struct BufferLine*
+load_info(struct DrawState* state)
{
- if (state->edit_info_focused)
- {
- if (state->info_first_shown.row > (EDIT_HEIGHT - 3) / 2)
- state->info_first_shown.row -= (EDIT_HEIGHT - 3) / 2;
- else
- move_start(state);
- return 0;
- }
+ struct PoEntry* first = &state->entries[0];
+ struct PoEntry* current = &state->entries[state->msgid_number - 1];
+ struct BufferLine* newbuf = NULL;
+ struct BufferLine* pnewbuf = NULL;
+ const uint32_t* pumsgid = NULL;
+ ssize_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
+ ssize_t msgid_plural_num_lines
+ = u32_lines_in_string(current->msgid_plural, 0, 0);
+ ssize_t num_lines = 0;
+ ssize_t newlen = 0;
- if (state->dirty)
- save_msgstr(state);
- else /* No need to call cancel_msgstr */
- state->dirty = state->dirty_before_edit;
- move_list_up(state);
- state->msgstr_index = 0;
- return show_edit(state);
-}
+ for (ssize_t i = 0; i < state->info_rows_count; i++)
+ free_bufferline(state->info_buffer + i);
+ free(state->info_buffer);
-static int
-erase_backwards(struct DrawState* state)
-{
- struct BufferLine* buffer_line;
- assert(state != NULL);
- buffer_line = &state->input_buffer[state->coord.row];
- if (state->edit_info_focused)
- return 1;
+ state->info_rows_maxlen = 0;
- if (state->coord.col > 0)
+ if (current->msgid)
+ num_lines += msgid_num_lines + 1;
+ if (current->msgid_plural)
{
- ssize_t i = state->coord.col;
- do
- {
- buffer_line->text[i - 1] = buffer_line->text[i];
- i++;
- /* TODO: Use size? */
- } while (buffer_line->text[i]);
- buffer_line->text[i - 1] = 0;
- buffer_line->length--;
- state->coord.col--;
- if (state->input_first_shown.col > 0)
- {
- const struct BufferLine* current_line
- = &state->input_buffer[state->coord.row];
- if (display_length(current_line->text,
- current_line->length)
- >= state->edit_width - 4)
- state->input_first_shown.col--;
- }
- state->input_display_column
- = display_length(state->input_buffer[state->coord.row]
- .text,
- state->coord.col);
- if (state->input_display_column < state->input_first_shown.col)
- state->input_first_shown.col
- = state->input_display_column;
- state->dirty = 1;
+ num_lines += msgid_plural_num_lines + 1;
+ if (first->plural_forms)
+ num_lines += 3;
}
- else if (state->coord.row > 0)
+ if (current->comments)
+ num_lines += current->comment_lines + 1;
+ if (current->extr_comments)
+ num_lines += current->extr_comment_lines + 1;
+ if (current->ref_comments)
+ num_lines += current->ref_comment_lines + 1;
+ newbuf = calloc(num_lines, sizeof(struct BufferLine));
+ if (!newbuf)
+ return NULL;
+
+ pnewbuf = newbuf;
+ for (ssize_t i = 0; i < num_lines; i++)
{
- move_up(state);
- move_end(state);
- erase_forward(state);
+ init_bufferline(pnewbuf);
+ uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ return NULL;
+ pnewbuf->text = newtext;
+ pnewbuf++;
}
- return 0;
-}
-static int
-erase_forward(struct DrawState* state)
-{
- struct BufferLine* buffer_line = &state->input_buffer[state->coord.row];
- if (state->edit_info_focused)
- return 1;
+ state->info_rows_count = num_lines;
- if (buffer_line->text[state->coord.col])
+ pnewbuf = newbuf;
+ newlen = 0;
+ pumsgid = NULL;
+ if (current->msgid)
{
- size_t i = state->coord.col;
- while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
+ pumsgid = current->msgid;
+ newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ while (*pumsgid)
{
- buffer_line->text[i] = buffer_line->text[i + 1];
- i++;
+ size_t len = 0;
+ const uint32_t* old_pumsgid = pumsgid;
+
+ len = u32_next_line(current->msgid, &pumsgid, 0, 0);
+ u32_strncpy(pnewbuf->text, old_pumsgid,
+ MIN(len + 1, MAXBUFLINE));
+ newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
}
- buffer_line->text[i] = 0;
- buffer_line->length--;
- state->dirty = 1;
}
- else if (state->coord.row < state->input_rows_count - 1)
+ if (current->msgid_plural)
{
- if (!join_lines(state, state->coord.row, 1))
+ newlen = u32_u8_strncpy(pnewbuf->text,
+ "msgid_plural:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ pumsgid = current->msgid_plural;
+ while (*pumsgid)
{
- state->error = ERR_CANT_ALLOC;
- state->running = 0;
- }
- }
- return 0;
-}
-
-static int
-erase_prev_word(struct DrawState* state)
-{
- uint32_t* pline;
- ssize_t len;
- int i;
- int d;
-
- if (state->edit_info_focused)
- return 1;
-
- i = state->coord.col;
- len = state->input_buffer[state->coord.row].length;
- pline = state->input_buffer[state->coord.row].text;
-
- if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
- i--;
- while (i > 0 && u32_is_word_boundary(pline[i], 0))
- i--;
- while (i > 0 && !u32_is_word_boundary(pline[i], 0)
- && !u32_is_word_boundary(pline[i - 1], 0))
- i--;
-
- d = state->coord.col - i;
-
- for (ssize_t j = i; j < len - d; j++)
- pline[j] = pline[j + d];
-
- len -= d;
- state->input_buffer[state->coord.row].length = len;
- pline[len] = 0;
- state->coord.col = i;
- state->input_display_column
- = display_length(state->input_buffer[state->coord.row].text,
- state->coord.col);
- if (state->input_display_column < state->input_first_shown.col)
- state->input_first_shown.col = state->input_display_column;
- state->dirty = 1;
- return 0;
-}
+ size_t len = 0;
+ const uint32_t* old_pumsgid = pumsgid;
-static int
-erase_search_backwards(struct DrawState* state)
-{
- if (state->search_column > 0)
- {
- size_t i = state->search_column;
- do
- {
- state->search[i - 1] = state->search[i];
- i++;
- } while (state->search[i]);
- state->search[i - 1] = 0;
- state->search_column--;
- if (state->search_column < state->search_first_shown_column)
+ len = u32_next_line(current->msgid_plural, &pumsgid, 0,
+ 0);
+ u32_strncpy(pnewbuf->text, old_pumsgid,
+ MIN(len + 1, MAXBUFLINE));
+ newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
+ }
+ if (first->plural_forms)
{
- if (state->search_column > state->search_width - 4)
- state->search_first_shown_column
- = state->search_column
- - state->search_width + 4;
- else
- state->search_first_shown_column = 0;
+ newlen = u32_u8_strncpy(pnewbuf->text,
+ "Plural forms:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ newlen = u32_strncpy(pnewbuf->text, first->plural_forms,
+ MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
}
- state->search_display_column
- = display_length(state->search, state->search_column);
-
- return next_match(state);
}
- return 0;
-}
-
-static int
-erase_search_forward(struct DrawState* state)
-{
- if (state->search[state->search_column])
+ if (current->comments)
{
- size_t i = state->search_column;
- while (i < MAXSEARCH - 2 && state->search[i + 1])
+ newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ for (size_t i = 0; i < current->comment_lines; i++)
{
- state->search[i] = state->search[i + 1];
- i++;
+ uint32_t* comment = current->comments[i];
+ size_t len = u32_strlen(comment);
+ newlen = u32_strncpy(pnewbuf->text, comment,
+ MIN(len + 1, MAXBUFLINE));
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
}
- state->search[i] = 0;
-
- return next_match(state);
}
- return 0;
-}
-
-static int
-erase_search_prev_word(struct DrawState* state)
-{
- uint32_t* pline;
- ssize_t len;
- int i;
-
- pline = state->search;
- len = u32_strlen(state->search);
- i = state->search_column;
- if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
- i--;
- while (i > 0 && u32_is_word_boundary(pline[i], 0))
- i--;
- while (i > 0 && !u32_is_word_boundary(pline[i], 0)
- && !u32_is_word_boundary(pline[i - 1], 0))
- i--;
-
- int d = state->search_column - i;
-
- for (int j = i; j < len - d; j++)
- pline[j] = pline[j + d];
-
- len -= state->search_column - i;
- pline[len] = 0;
- state->search_column = i;
- state->search_display_column
- = display_length(state->search, state->search_column);
-
- if (state->search_column < state->search_first_shown_column)
- {
- if (state->search_column > state->search_width - 4)
- state->search_first_shown_column = state->search_column
- - state->search_width + 4;
- else
- state->search_first_shown_column = 0;
- }
- state->search_display_column
- = display_length(state->search, state->search_column);
-
- return next_match(state);
-}
-
-static int
-erase_search_to_end(struct DrawState* state)
-{
- state->search[state->search_column] = 0;
- move_search_end(state);
- return next_match(state);
-}
-
-static int
-erase_search_to_start(struct DrawState* state)
-{
- size_t i = state->search_column;
- size_t len = u32_strlen(state->search);
- do
+ if (current->extr_comments)
{
- state->search[i - state->search_column] = state->search[i];
- i++;
- } while (state->search[i]);
- state->search[len - state->search_column] = 0;
- move_search_start(state);
- return next_match(state);
-}
-
-static int
-erase_to_end(struct DrawState* state)
-{
- struct BufferLine* buffer_line = NULL;
+ newlen = u32_u8_strncpy(pnewbuf->text,
+ "Translation comments:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ for (size_t i = 0; i < current->extr_comment_lines; i++)
+ {
+ uint32_t* extr_comment = current->extr_comments[i];
+ size_t len = u32_strlen(extr_comment);
+ newlen = u32_strncpy(pnewbuf->text, extr_comment,
+ MIN(len + 1, MAXBUFLINE));
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
+ }
+ }
+ if (current->ref_comments)
+ {
+ newlen = u32_u8_strncpy(pnewbuf->text,
+ "References:", MAXBUFLINE);
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
+ pnewbuf++;
+ for (size_t i = 0; i < current->ref_comment_lines; i++)
+ {
+ char* ref_comment = current->ref_comments[i];
+ size_t len = strlen(ref_comment);
+ newlen = u32_u8_strncpy(pnewbuf->text, ref_comment,
+ MIN(len + 1, MAXBUFLINE));
+ pnewbuf->length = newlen;
+ if (newlen > state->info_rows_maxlen)
+ state->info_rows_maxlen = newlen;
+ pnewbuf++;
+ }
+ }
- if (state->edit_info_focused)
- return 1;
+ state->info_buffer = newbuf;
- buffer_line = &state->input_buffer[state->coord.row];
- buffer_line->text[state->coord.col] = 0;
- buffer_line->length = state->coord.col;
- move_end(state);
- state->dirty = 1;
- return 0;
+ return state->info_buffer;
}
-static int
-erase_to_start(struct DrawState* state)
+static struct BufferLine*
+load_msgstr(struct DrawState* state)
{
- struct BufferLine* buffer_line;
- ssize_t i;
- ssize_t len;
+ struct PoEntry* current;
+ struct BufferLine* newbuf;
+ struct BufferLine* pnewbuf;
+ size_t num_lines;
+ int msgstr_index;
- i = state->coord.col;
- buffer_line = &state->input_buffer[state->coord.row];
- len = buffer_line->length;
+ assert((state != NULL) && (state->entries != NULL));
+ current = &state->entries[state->msgid_number - 1];
+ newbuf = NULL;
+ pnewbuf = NULL;
+ msgstr_index = state->msgstr_index;
+ num_lines = current->msgstr_count == 0
+ ? 1
+ : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0);
- if (state->edit_info_focused)
- return 1;
+ for (ssize_t i = 0; i < state->input_rows_count; i++)
+ free_bufferline(state->input_buffer + i);
+ free(state->input_buffer);
- do
- {
- buffer_line->text[i - state->coord.col] = buffer_line->text[i];
- i++;
- } while (buffer_line->text[i]);
- buffer_line->text[len - state->coord.col] = 0;
- buffer_line->length = len - state->coord.col;
- move_start(state);
- state->dirty = 1;
- return 0;
-}
+ newbuf = calloc(num_lines, sizeof(struct BufferLine));
+ if (!newbuf)
+ return NULL;
-static int
-exit_program(struct DrawState* state)
-{
- if (state->show_edit)
+ pnewbuf = newbuf;
+ for (size_t i = 0; i < num_lines; i++)
{
- state->error = ERR_DLG_OPEN;
- return 0;
+ init_bufferline(pnewbuf);
+ uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ if (!newtext)
+ return NULL;
+ pnewbuf->text = newtext;
+ pnewbuf++;
}
- if (state->dirty)
+
+ state->input_rows_count = num_lines;
+
+ if (current->msgstr_count > 0)
{
- state->in_prompt = 1;
- STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE);
- state->prompt_callback = quit_callback;
+ const uint32_t* pumsgstr = *(current->msgstr + msgstr_index);
+ if (pumsgstr)
+ {
+ pnewbuf = newbuf;
+ while (*pumsgstr)
+ {
+ size_t len = 0;
+ size_t newlen = 0;
+ const uint32_t* old_pumsgstr = pumsgstr;
+ len = u32_next_line(*(current->msgstr
+ + msgstr_index),
+ &pumsgstr, 0, 0);
+ u32_strncpy(pnewbuf->text, old_pumsgstr,
+ MIN(len + 1, MAXBUFLINE));
+ newlen = u32_decode_tabs(pnewbuf->text,
+ MAXBUFLINE);
+ pnewbuf->length = newlen;
+ pnewbuf++;
+ }
+ }
+ else
+ newbuf[0].length = 0;
}
else
- state->running = 0;
- //*state->error = 0;
- return 0;
-}
-
-static int
-hide_search(struct DrawState* state)
-{
- state->show_search = 0;
- tb_hide_cursor();
- return 0;
-}
-
-static int
-insert_search_space(struct DrawState* state)
-{
- insert_search_char(state, (uint32_t)L' ');
- return 0;
-}
-
-static int
-insert_search_tab(struct DrawState* state)
-{
- insert_search_char(state, (uint32_t)L'\t');
- return 0;
-}
-
-static int
-insert_space(struct DrawState* state)
-{
- insert_char(state, (uint32_t)L' ');
- return 0;
-}
+ newbuf[0].length = 0;
-static int
-insert_tab(struct DrawState* state)
-{
- insert_char(state, (uint32_t)L'\t');
- return 0;
+ state->input_buffer = newbuf;
+ return state->input_buffer;
}
static int
@@ -1906,6 +1799,42 @@ next_untranslated(struct DrawState* state)
return 0;
}
+static size_t
+next_msgid(struct DrawState* state, const int delta)
+{
+ size_t ret = state->msgid_number;
+ size_t start = ret;
+ size_t last_non_obsolete
+ = state->real_msgid_count - state->obsolete_at_end_count;
+
+ while (ret < last_non_obsolete + 1)
+ {
+ ret++;
+ if (!state->entries[ret - 1].obsolete && ret >= start + delta)
+ break;
+ }
+
+ return ret == last_non_obsolete + 1 ? last_non_obsolete : ret;
+}
+
+static int
+overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev)
+{
+ if (ev->ch == 'y')
+ copy_msgid_to_input(state);
+ state->prompt_callback = NULL;
+ return 0;
+}
+
+static int
+overwrite_paste_callback(struct DrawState* state, struct tb_event* ev)
+{
+ if (ev->ch == 'y')
+ paste_paste_buffer_to_input(state);
+ state->prompt_callback = NULL;
+ return 0;
+}
+
static int
paste_paste_buffer_to_input(struct DrawState* state)
{
@@ -2018,6 +1947,60 @@ prev_match(struct DrawState* state)
return 0;
}
+static size_t
+prev_msgid(struct DrawState* state, const int delta)
+{
+ ssize_t ret = state->msgid_number;
+ ssize_t start = ret;
+
+ if (start < delta)
+ return state->real_msgid_count ? 1 : 0;
+
+ while (ret > 1)
+ {
+ ret--;
+ if (!state->entries[ret - 1].obsolete && ret <= start - delta)
+ break;
+ }
+
+ return ret;
+}
+
+int
+print_error(const int code, const char* msg, ...)
+{
+ char buf[MAXBUFLINE];
+ va_list args;
+ va_start(args, msg);
+ vsnprintf(buf, sizeof(buf), msg, args);
+ va_end(args);
+ fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf);
+ return code;
+}
+
+static int
+quit_callback(struct DrawState* state, struct tb_event* ev)
+{
+ if (ev->ch == 'y')
+ write_file(state);
+ state->prompt_callback = NULL;
+ state->running = 0;
+ return 0;
+}
+
+void
+redraw_status(struct DrawState* state)
+{
+ assert(state != NULL);
+ if (state->recalculate_size)
+ {
+ recalculate_size(state);
+ state->recalculate_size = 0;
+ }
+ (void)draw_status(state);
+ tb_present();
+}
+
static int
prev_plural(struct DrawState* state)
{
diff --git a/poe.h b/poe.h
@@ -8,7 +8,6 @@ static int cancel_msgstr(struct DrawState* state);
static int cancel_search(struct DrawState* state);
static int copy_msgid_to_input(struct DrawState* state);
int cp(const char* from, const char* to);
-//static int create_file(struct DrawState* state);
static char* dirname(char* dir, char* path, const ssize_t dir_size);
static int edit_page_down(struct DrawState* state);
static int edit_page_up(struct DrawState* state);