чување 5e2d2a94f06895f95f117fe96e43a5a8b1a14fa2
родитељ 0c3818f8a9db1e237a1b8072b611c760c2c35a61
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Tue, 10 Feb 2026 09:39:19 +0100
WIP tidy up logic based on sled
Diffstat:
| M | Makefile | | | 6 | +++--- |
| M | TODO | | | 2 | +- |
| M | config.Haiku | | | 6 | +++--- |
| M | config.Linux | | | 6 | +++--- |
| M | config.OpenBSD | | | 6 | +++--- |
| M | config.def.h | | | 325 | +++++++++++++++++++++++++++++++++++-------------------------------------------- |
| M | draw.c | | | 1265 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
| M | draw.h | | | 179 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
| M | po.c | | | 250 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
| M | po.h | | | 36 | ++++++++++++++++++++---------------- |
| M | poe.c | | | 1774 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| M | termbox.h | | | 2 | ++ |
| M | util.c | | | 131 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
| M | util.h | | | 108 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
| M | version.h.in | | | 5 | +++-- |
измењених датотека: 15, додавања: 2313(+), брисања: 1788(-)
diff --git a/Makefile b/Makefile
@@ -28,15 +28,15 @@ makedate:
makeversion:
sh lib/makeversion -l
-poe.o: poe.c config.h draw.h po.h termbox.h util.h version.h
+poe.o: poe.c config.h draw.h limit.h po.h termbox.h util.h version.h
$(MANPAGE): $(MANPAGE).in date version
version.h: version.h.in date version
-draw.o: draw.c config.h draw.h po.h termbox.h util.h version.h
+draw.o: draw.c config.h draw.h limit.h po.h termbox.h util.h version.h
-util.o: util.c termbox.h po.h util.h
+util.o: util.c limit.h termbox.h po.h util.h
$(PROG): $(OBJS) $(SRC) $(HEADERS)
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
diff --git a/TODO b/TODO
@@ -4,7 +4,7 @@ TODO
[ ] Support Yudit's nonstandard PO format (no empty lines between individual
entries)
-[~] Tidy up basic logic in poe.c
+[ ] Tidy up basic logic everywhere (based on sled)
< > Wrap long lines? Not if it would complicate the code too much.
diff --git a/config.Haiku b/config.Haiku
@@ -5,9 +5,9 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
DESTDIR = /boot/home/config/non-packaged
INSTALL = install
LIBS =
-SRC = draw.c po.c poe.c strlcat.c strlcpy.c util.c
-HEADERS = config.h draw.h po.h termbox.h util.h version.h
-OBJS = draw.o po.o poe.o strlcat.o strlcpy.o util.o
+SRC = draw.c limit.c po.c poe.c strlcat.c strlcpy.c util.c
+HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
+OBJS = draw.o limit.o po.o poe.o strlcat.o strlcpy.o util.o
PREFIX =
PROG = poe
diff --git a/config.Linux b/config.Linux
@@ -3,9 +3,9 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-D_XOPEN_SOURCE=700 -DNDEBUG
INSTALL = install
LIBS =
-SRC = draw.c po.c poe.c strlcat.c strlcpy.c util.c
-HEADERS = config.h draw.h po.h termbox.h util.h version.h
-OBJS = draw.o po.o poe.o strlcat.o strlcpy.o util.o
+SRC = draw.c limit.c po.c poe.c strlcat.c strlcpy.c util.c
+HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
+OBJS = draw.o limit.o po.o poe.o strlcat.o strlcpy.o util.o
PREFIX = /usr/local
PROG = poe
diff --git a/config.OpenBSD b/config.OpenBSD
@@ -3,9 +3,9 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
-D_XOPEN_SOURCE=700 -D_BSD_SOURCE -DNDEBUG
INSTALL = install
LIBS =
-SRC = draw.c po.c poe.c strlcat.c strlcpy.c util.c
-HEADERS = config.h draw.h po.h termbox.h util.h version.h
-OBJS = draw.o po.o poe.o strlcat.o strlcpy.o util.o
+SRC = draw.c limit.c po.c poe.c strlcat.c strlcpy.c util.c
+HEADERS = config.h draw.h limit.h po.h termbox.h util.h version.h
+OBJS = draw.o limit.o po.o poe.o strlcat.o strlcpy.o util.o
PREFIX = /usr/local
PROG = poe
diff --git a/config.def.h b/config.def.h
@@ -2,147 +2,104 @@
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
-/* Comment out to not create backups */
-#define CREATE_BACKUPS
-
-#ifdef PO_C_GLOBALS
-const char* backup_suffix = "~";
-#endif /* PO_C_GLOBALS */
-
-/* Correct the ending of msgstr to match msgid if ' ', '.' or '\\n' */
-#define MATCH_MSGSTR_ENDING
-
-/* Uncomment to insert this after Plural-Forms: in the first msgstr (only if
- * Plural-Forms isn't already present)
- * The plural string given here is for Serbian; adjust to match your language */
-/*#define PLURAL_STRING "nplurals=3; plural=(n%10==1 && n%100!=11 ?" \
- " 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"*/
-
-#define SAVE_WRAP_WIDTH 70
-#define WRAP_FIRST_MSGSTR 0
-#define WARN_COUNT_DOTS 0
+/* clang-format off */
+static const struct Color colors[] = {
+ [C_CLEAR] = { .fg = TB_DEFAULT, .bg = TB_DEFAULT },
+ [C_DLG] = { .fg = TB_BLACK, .bg = TB_WHITE },
+ [C_EDIT_MSGID] = { .fg = TB_BLACK | TB_BOLD,
+ .bg = TB_WHITE },
+ [C_EDIT_MSGID_FOCUS]
+ = { .fg = TB_BLACK, .bg = TB_WHITE },
+ [C_EDIT_MSGID_HEADING]
+ = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_WHITE },
+ [C_EDIT_MSGSTR] = { .fg = TB_BLACK | TB_BOLD,
+ .bg = TB_BLACK },
+ [C_EDIT_MSGSTR_FOCUS]
+ = { .fg = TB_YELLOW | TB_BOLD,
+ .bg = TB_BLACK },
+ [C_ERROR] = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_RED },
+ [C_FLAG] = { .fg = TB_YELLOW | TB_BOLD,
+ .bg = TB_DEFAULT },
+ [C_LIST_FLAGS] = { .fg = TB_YELLOW | TB_BOLD,
+ .bg = TB_DEFAULT },
+ [C_LIST_FLAGS_SEL]
+ = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_BLUE },
+ [C_LIST_MSGID] = { .fg = TB_WHITE, .bg = TB_DEFAULT },
+ [C_LIST_MSGID_SEL]
+ = { .fg = TB_WHITE, .bg = TB_BLUE },
+ [C_LIST_MSGID_SPECIAL]
+ = { .fg = TB_YELLOW | TB_BOLD,
+ .bg = TB_DEFAULT },
+ [C_LIST_MSGID_SPECIAL_SEL]
+ = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_BLUE },
+ [C_LIST_MSGSTR] = { .fg = TB_GREEN, .bg = TB_DEFAULT },
+ [C_LIST_MSGSTR_SEL]
+ = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_BLUE },
+ [C_LIST_MSGSTR_WARN]
+ = { .fg = TB_RED | TB_BOLD,
+ .bg = TB_DEFAULT },
+ [C_LIST_MSGSTR_SEL_WARN]
+ = { .fg = TB_RED | TB_BOLD,
+ .bg = TB_BLUE },
+ [C_LIST_SEL] = { .fg = TB_WHITE, .bg = TB_BLUE },
+ [C_MSGID] = { .fg = TB_GREEN, .bg = TB_DEFAULT },
+ [C_MSGSTR] = { .fg = TB_WHITE, .bg = TB_DEFAULT },
+ [C_PROMPT] = { .fg = TB_YELLOW | TB_BOLD,
+ .bg = TB_MAGENTA },
+ [C_SEARCH] = { .fg = TB_BLACK, .bg = TB_WHITE },
+ [C_SEARCH_EDIT] = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_BLACK },
+ [C_SEARCH_HL] = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_GREEN },
+ [C_SHADOW] = { .fg = TB_BLACK, .bg = TB_BLACK | TB_BOLD
+ },
+ [C_STATUS_1] = { .fg = TB_BLACK, .bg = TB_WHITE },
+ [C_STATUS_2] = { .fg = TB_WHITE | TB_BOLD,
+ .bg = TB_BLUE },
+ [C_STATUS_3] = { .fg = TB_BLACK, .bg = TB_WHITE },
+ [C_TITLE] = { .fg = TB_BLACK, .bg = TB_WHITE },
+};
+/* clang-format on */
-#define DLG_FG TB_BLACK
-#define DLG_BG TB_WHITE
-#define EDIT_MSGID_FG TB_BLACK | TB_BOLD
-#define EDIT_MSGID_BG TB_WHITE
-#define EDIT_MSGID_FOCUS_FG TB_BLACK
-#define EDIT_MSGID_FOCUS_BG TB_WHITE
-#define EDIT_MSGID_HEADING_FG TB_WHITE | TB_BOLD
-#define EDIT_MSGID_HEADING_BG TB_WHITE
-#define EDIT_MSGSTR_FG TB_BLACK | TB_BOLD
-#define EDIT_MSGSTR_BG TB_BLACK
-#define EDIT_MSGSTR_FOCUS_FG TB_YELLOW | TB_BOLD
-#define EDIT_MSGSTR_FOCUS_BG TB_BLACK
-#define ERROR_FG TB_WHITE | TB_BOLD
-#define ERROR_BG TB_RED
-#define FLAG_FG TB_YELLOW | TB_BOLD
-#define FLAG_BG TB_DEFAULT
-#define LIST_FLAGS_FG TB_YELLOW | TB_BOLD
-#define LIST_FLAGS_BG TB_DEFAULT
-#define LIST_FLAGS_SEL_FG TB_WHITE | TB_BOLD
-#define LIST_FLAGS_SEL_BG TB_BLUE
-#define LIST_MSGID_FG TB_WHITE
-#define LIST_MSGID_BG TB_DEFAULT
-#define LIST_MSGID_SEL_FG TB_WHITE
-#define LIST_MSGID_SEL_BG TB_BLUE
-#define LIST_MSGID_SPECIAL_FG TB_YELLOW | TB_BOLD
-#define LIST_MSGID_SPECIAL_BG TB_DEFAULT
-#define LIST_MSGID_SPECIAL_SEL_FG TB_WHITE | TB_BOLD
-#define LIST_MSGID_SPECIAL_SEL_BG TB_BLUE
-#define LIST_MSGSTR_FG TB_GREEN
-#define LIST_MSGSTR_BG TB_DEFAULT
-#define LIST_MSGSTR_SEL_FG TB_WHITE | TB_BOLD
-#define LIST_MSGSTR_SEL_BG TB_BLUE
-#define LIST_MSGSTR_WARN_FG TB_RED | TB_BOLD
-#define LIST_MSGSTR_WARN_BG TB_DEFAULT
-#define LIST_MSGSTR_SEL_WARN_FG TB_RED | TB_BOLD
-#define LIST_MSGSTR_SEL_WARN_BG TB_BLUE
-#define LIST_SEL_FG TB_WHITE
-#define LIST_SEL_BG TB_BLUE
-#define MSGID_FG TB_GREEN
-#define MSGID_BG TB_DEFAULT
-#define MSGSTR_FG TB_WHITE
-#define MSGSTR_BG TB_DEFAULT
-#define PROMPT_FG TB_YELLOW | TB_BOLD
-#define PROMPT_BG TB_MAGENTA
-#define SEARCH_FG TB_BLACK
-#define SEARCH_BG TB_WHITE
-#define SEARCH_EDIT_FG TB_WHITE | TB_BOLD
-#define SEARCH_EDIT_BG TB_BLACK
-#define SEARCH_HL_FG TB_WHITE | TB_BOLD
-#define SEARCH_HL_BG TB_GREEN
-#define SHADOW_FG TB_BLACK
-#define SHADOW_BG TB_BLACK | TB_BOLD
-#define TITLE_FG TB_BLACK
-#define TITLE_BG TB_WHITE
+extern int backup_fail;
+extern char* backup_suffix;
+extern int create_backups;
+extern int match_msgstr_ending;
+extern char* plural_string;
+extern int warn_count_dots;
+extern int wrap_first_msgstr;
+extern int wrap_width;
#ifdef POE_C_GLOBALS
-int cancel_close(struct DrawState* state);
-int cancel_msgstr(struct DrawState* state);
-int cancel_search(struct DrawState* state);
-int copy_msgid_to_input(struct DrawState* state);
-int edit_page_down(struct DrawState* state);
-int edit_page_up(struct DrawState* state);
-int erase_backwards(struct DrawState* state);
-int erase_forward(struct DrawState* state);
-int erase_prev_word(struct DrawState* state);
-int erase_search_backwards(struct DrawState* state);
-int erase_search_forward(struct DrawState* state);
-int erase_search_prev_word(struct DrawState* state);
-int erase_search_to_end(struct DrawState* state);
-int erase_search_to_start(struct DrawState* state);
-int erase_to_end(struct DrawState* state);
-int erase_to_start(struct DrawState* state);
-int exit_program(struct DrawState* state);
-int hide_search(struct DrawState* state);
-int insert_line(struct DrawState* state);
-int insert_search_space(struct DrawState* state);
-int insert_search_tab(struct DrawState* state);
-int insert_space(struct DrawState* state);
-int insert_tab(struct DrawState* state);
-int move_end(struct DrawState* state);
-int move_down(struct DrawState* state);
-int move_left(struct DrawState* state);
-int move_list_down(struct DrawState* state);
-int move_list_end(struct DrawState* state);
-int move_list_page_down(struct DrawState* state);
-int move_list_page_up(struct DrawState* state);
-int move_list_start(struct DrawState* state);
-int move_list_up(struct DrawState* state);
-int move_next_word(struct DrawState* state);
-int move_prev_word(struct DrawState* state);
-int move_right(struct DrawState* state);
-int move_search_end(struct DrawState* state);
-int move_search_left(struct DrawState* state);
-int move_search_next_word(struct DrawState* state);
-int move_search_prev_word(struct DrawState* state);
-int move_search_right(struct DrawState* state);
-int move_search_start(struct DrawState* state);
-int move_start(struct DrawState* state);
-int move_up(struct DrawState* state);
-int next_fuzzy(struct DrawState* state);
-int next_match(struct DrawState* state);
-int next_plural(struct DrawState* state);
-int next_untranslated(struct DrawState* state);
-int paste_paste_buffer_to_input(struct DrawState* state);
-int prev_match(struct DrawState* state);
-int prev_plural(struct DrawState* state);
-int save_msgstr(struct DrawState* state);
-int show_edit(struct DrawState* state);
-int show_help(struct DrawState* state);
-int show_search(struct DrawState* state);
-int toggle_fuzzy(struct DrawState* state);
-int toggle_info_focused(struct DrawState* state);
-int yank_input_to_paste_buffer(struct DrawState* state);
-int write_file(struct DrawState* state);
-
/* clang-format off */
+/* Fail with error if creating backups is not possible due to file and
+ * directory permissions? */
+int backup_fail = 1;
+char* backup_suffix = "~";
+int create_backups = 1; /* comment out to not create backups */
+int match_msgstr_ending = 1; /* correct the ending of msgstr to
+ * match msgid if ' ', '.' or '\\n'? */
+/* Uncomment/comment the appropriate definition to insert this after
+ * Plural-Forms: in the first msgstr (only if Plural-Forms isn't already
+ * present) The plural string given here is for Serbian; adjust to match
+ * your language */
+char* plural_string = NULL;
+/*char* plural_string = "nplurals=3; plural=(n%10==1 && n%100!=11 ?"
+ " 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";*/
+int warn_count_dots = 0;
+int wrap_first_msgstr = 0;
+int wrap_width = 70;
+
/* Always processed */
static const struct Key top_level_keys[] = {
{ .ev = { .key = TB_KEY_ESC }, .callback = cancel_close },
- { .ev = { .key = TB_KEY_CTRL_Q },
+ { .ev = { .key = TB_KEY_CTRL_Q, .mod = TB_MOD_CTRL },
.callback = exit_program },
};
@@ -160,31 +117,31 @@ static const struct Key edit_keys[] = {
.callback = erase_backwards },
{ .ev = { .key = TB_KEY_BACKSPACE2 },
.callback = erase_backwards },
- { .ev = { .key = TB_KEY_CTRL_A },
+ { .ev = { .key = TB_KEY_CTRL_A, .mod = TB_MOD_CTRL },
.callback = move_start },
- { .ev = { .key = TB_KEY_CTRL_C },
+ { .ev = { .key = TB_KEY_CTRL_C, .mod = TB_MOD_CTRL },
.callback = yank_input_to_paste_buffer },
- { .ev = { .key = TB_KEY_CTRL_E },
+ { .ev = { .key = TB_KEY_CTRL_E, .mod = TB_MOD_CTRL },
.callback = move_end },
- { .ev = { .key = TB_KEY_CTRL_K },
+ { .ev = { .key = TB_KEY_CTRL_K, .mod = TB_MOD_CTRL },
.callback = erase_to_end },
- { .ev = { .key = TB_KEY_CTRL_N },
+ { .ev = { .key = TB_KEY_CTRL_N, .mod = TB_MOD_CTRL },
.callback = next_plural },
- { .ev = { .key = TB_KEY_CTRL_P },
+ { .ev = { .key = TB_KEY_CTRL_P, .mod = TB_MOD_CTRL },
.callback = prev_plural },
- { .ev = { .key = TB_KEY_CTRL_R },
+ { .ev = { .key = TB_KEY_CTRL_R, .mod = TB_MOD_CTRL },
.callback = move_prev_word },
- { .ev = { .key = TB_KEY_CTRL_S },
+ { .ev = { .key = TB_KEY_CTRL_S, .mod = TB_MOD_CTRL },
.callback = save_msgstr },
- { .ev = { .key = TB_KEY_CTRL_T },
+ { .ev = { .key = TB_KEY_CTRL_T, .mod = TB_MOD_CTRL },
.callback = move_next_word },
- { .ev = { .key = TB_KEY_CTRL_U },
+ { .ev = { .key = TB_KEY_CTRL_U, .mod = TB_MOD_CTRL },
.callback = erase_to_start },
- { .ev = { .key = TB_KEY_CTRL_V },
+ { .ev = { .key = TB_KEY_CTRL_V, .mod = TB_MOD_CTRL },
.callback = paste_paste_buffer_to_input },
- { .ev = { .key = TB_KEY_CTRL_W },
+ { .ev = { .key = TB_KEY_CTRL_W, .mod = TB_MOD_CTRL },
.callback = erase_prev_word },
- { .ev = { .key = TB_KEY_CTRL_Z },
+ { .ev = { .key = TB_KEY_CTRL_Z, .mod = TB_MOD_CTRL },
.callback = toggle_fuzzy },
{ .ev = { .key = TB_KEY_DELETE },
.callback = erase_forward },
@@ -209,37 +166,42 @@ static const struct Key search_keys[] = {
.callback = move_search_left },
{ .ev = { .key = TB_KEY_ARROW_RIGHT },
.callback = move_search_right },
- { .ev = { .key = TB_KEY_BACKSPACE },
+ { .ev = { .key = TB_KEY_BACKSPACE, .mod = TB_MOD_CTRL },
.callback = erase_search_backwards },
- { .ev = { .key = TB_KEY_BACKSPACE2 },
+ { .ev = { .key = TB_KEY_BACKSPACE2, .mod = TB_MOD_CTRL },
.callback = erase_search_backwards },
- { .ev = { .key = TB_KEY_CTRL_A },
+ { .ev = { .key = TB_KEY_CTRL_A, .mod = TB_MOD_CTRL },
.callback = move_search_start },
- { .ev = { .key = TB_KEY_CTRL_E },
+ { .ev = { .key = TB_KEY_CTRL_E, .mod = TB_MOD_CTRL },
.callback = move_search_end },
- { .ev = { .key = TB_KEY_CTRL_K },
+ { .ev = { .key = TB_KEY_CTRL_K, .mod = TB_MOD_CTRL },
.callback = erase_search_to_end },
- { .ev = { .key = TB_KEY_CTRL_R },
+ { .ev = { .key = TB_KEY_CTRL_R, .mod = TB_MOD_CTRL },
.callback = move_search_prev_word },
- { .ev = { .key = TB_KEY_CTRL_S },
+ { .ev = { .key = TB_KEY_CTRL_S, .mod = TB_MOD_CTRL },
.callback = write_file },
- { .ev = { .key = TB_KEY_CTRL_T },
+ { .ev = { .key = TB_KEY_CTRL_T, .mod = TB_MOD_CTRL },
.callback = move_search_next_word },
- { .ev = { .key = TB_KEY_CTRL_U },
+ { .ev = { .key = TB_KEY_CTRL_U, .mod = TB_MOD_CTRL },
.callback = erase_search_to_start },
- { .ev = { .key = TB_KEY_CTRL_W },
+ { .ev = { .key = TB_KEY_CTRL_W, .mod = TB_MOD_CTRL },
.callback = erase_search_prev_word },
{ .ev = { .key = TB_KEY_DELETE },
.callback = erase_search_forward },
{ .ev = { .key = TB_KEY_END }, .callback = move_search_end },
- { .ev = { .key = TB_KEY_ENTER },
+ /* Enter = C-M, hence the TB_MOD_CTRL; tb doesn't make a difference
+ * between C-Enter and Enter... */
+ { .ev = { .key = TB_KEY_ENTER, .mod = TB_MOD_CTRL },
.callback = hide_search },
{ .ev = { .key = TB_KEY_F3 }, .callback = next_match },
{ .ev = { .key = TB_KEY_F4 }, .callback = prev_match },
{ .ev = { .key = TB_KEY_HOME }, .callback = move_search_start },
{ .ev = { .key = TB_KEY_SPACE },
.callback = insert_search_space },
- { .ev = { .key = TB_KEY_TAB }, .callback = insert_search_tab },
+ /* Tab = C-I, hence the TB_MOD_CTRL; tb doesn't make a difference
+ * between C-I and Tab... */
+ { .ev = { .key = TB_KEY_TAB, .mod = TB_MOD_CTRL },
+ .callback = insert_search_tab },
};
/* Main window */
@@ -250,7 +212,9 @@ static const struct Key main_keys[] = {
.callback = move_list_up },
{ .ev = { .key = TB_KEY_END },
.callback = move_list_end },
- { .ev = { .key = TB_KEY_ENTER },
+ /* Enter = C-M, hence the TB_MOD_CTRL; tb doesn't make a difference
+ * between C-Enter and Enter... */
+ { .ev = { .key = TB_KEY_ENTER, .mod = TB_MOD_CTRL },
.callback = show_edit },
{ .ev = { .key = TB_KEY_F1 }, .callback = show_help },
{ .ev = { .key = TB_KEY_F3 }, .callback = next_match },
@@ -259,13 +223,13 @@ static const struct Key main_keys[] = {
{ .ev = { .key = TB_KEY_F8 }, .callback = next_untranslated },
{ .ev = { .key = TB_KEY_F9 }, .callback = next_fuzzy },
{ .ev = { .key = TB_KEY_HOME }, .callback = move_list_start },
- { .ev = { .key = TB_KEY_CTRL_B },
+ { .ev = { .key = TB_KEY_CTRL_B, .mod = TB_MOD_CTRL },
.callback = move_list_page_up },
- { .ev = { .key = TB_KEY_CTRL_F },
+ { .ev = { .key = TB_KEY_CTRL_F, .mod = TB_MOD_CTRL },
.callback = move_list_page_down },
- { .ev = { .key = TB_KEY_CTRL_S },
+ { .ev = { .key = TB_KEY_CTRL_S, .mod = TB_MOD_CTRL },
.callback = write_file },
- { .ev = { .key = TB_KEY_CTRL_Z },
+ { .ev = { .key = TB_KEY_CTRL_Z, .mod = TB_MOD_CTRL },
.callback = toggle_fuzzy },
{ .ev = { .key = TB_KEY_PGDN },
.callback = move_list_page_down },
@@ -285,27 +249,22 @@ static const struct Key main_keys[] = {
{ .ev = { .ch = 'z' }, .callback = toggle_fuzzy },
};
+/* clang-format on */
#endif /* POE_C_GLOBALS */
-void format_filename(char* result, size_t result_size, const char* format,
- const struct DrawState* state);
-void format_msgs(char* result, size_t result_size, const char* format,
- const struct DrawState* state);
-
+/* clang-format off */
static const struct StatusSegment status_segments[] = {
- {.format = "%s%s",
- .callback = format_filename,
- .fg = TB_BLACK,
- .bg = TB_WHITE,
- .alignment = LEFT},
- {.format = "%ld/%ld (%ldu/%ldf/%ldo)",
- .callback = format_msgs,
- .fg = TB_WHITE | TB_BOLD,
- .bg = TB_BLUE,
- .alignment = CENTER},
- {.format = "H=HELP, q=QUIT",
- .callback = NULL,
- .fg = TB_BLACK,
- .bg = TB_WHITE,
- .alignment = RIGHT},
+ {.format = "%s%s",
+ .callback = format_filename,
+ .color_index = C_STATUS_1,
+ .alignment = LEFT},
+ {.format = "%ld/%ld (%ldu/%ldf/%ldo)",
+ .callback = format_msgs,
+ .color_index = C_STATUS_2,
+ .alignment = CENTER},
+ {.format = "H=HELP, q=QUIT",
+ .callback = NULL,
+ .color_index = C_STATUS_3,
+ .alignment = RIGHT},
};
+/* clang-format on */
diff --git a/draw.c b/draw.c
@@ -2,16 +2,18 @@
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
+#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "version.h"
-#include "util.h"
-#include "po.h"
#include "draw.h"
#include "config.h"
+#include "limit.h"
+#include "po.h"
+#include "util.h"
+#include "version.h"
/* clang-format off */
static const char* help_title = "Main view help";
@@ -37,18 +39,28 @@ static const char* help[] = {
"See the manual (man poe) for full help."};
static const char* editbox_title = "Edit translation";
const char* errors[] = {
- [ERR_UNKNOWN_KEY] = "Unknown key",
+ [ERR_NO_CL_ARG] = "No filename given",
+ [ERR_NOTPO_FILE] = "Not a PO file",
+ [ERR_NOTREG_FILE] = "'%s' is not a regular file",
+ [ERR_TB_ERROR] = "termbox error",
+ [ERR_UNKNOWN_KEY] = "Unknown key (press F1 for help)",
[ERR_UNKNOWN_KEY_HELP] = "Unknown key (press H or F1 for help)",
[ERR_EXIT_KEY] = "Press q or C-Q to quit",
- [ERR_DLG_OPEN_QUIT] = "Press Esc to close dialog or q/C-Q to quit",
- [ERR_DLG_OPEN] = "Press Esc to close dialog first",
+ [ERR_DLG_OPEN] = "Press Esc to close dialog or q/C-Q to quit",
+ [ERR_DLG_OPEN_QUIT] = "Press Esc to close dialog first",
[ERR_DLG_FOCUS_EDIT] = "Switch to edit box first with F6",
[ERR_EMPTY_FILE] = "No msgids to edit",
[ERR_CANT_ALLOC] = "Memory allocation failed",
[ERR_CANT_SAVE] = "File is not writeable",
[ERR_CANT_MOVE] = "Cannot move temporary file",
[ERR_ILLEGAL_ON_FIRST] = "Not allowed on first entry",
- [ERR_NO_PLURAL_FORMS] = "No Plural-Forms (add manually)"};
+ [ERR_SYNTAX] = "%s:%ld:%ld: Syntax error",
+ [ERR_NO_PLURAL_FORMS] = "No Plural-Forms (add manually)",
+ [ERR_ERRNO_SET] = "System call/library function error"};
+const char* actions[] = {
+ [ACT_WRITING] = "Writing...",
+ [ACT_READING] = "Reading..."
+};
const char* flag_strings[] = {
[FL_C_FORMAT] = "c-format",
[FL_NO_C_FORMAT] = "no-c-format",
@@ -59,13 +71,556 @@ const char* prompt_cancel_dirty = "Entry changed, save [y/n/ESC]?";
const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?";
/* clang-format on */
-int print_error(const int code, const char* msg, ...);
+extern int print_error(const int code, const char* msg, ...);
void
-init_bufferline(struct BufferLine* bl)
+assign_coord(struct Coord* dest, const struct Coord src)
+{
+ init_coord(dest, src.col, src.row);
+}
+
+int
+calculate_edit_width(const struct DrawState* state)
+{
+ assert(state != NULL);
+ return state->maxx > EDIT_WIDTH + 3 ? EDIT_WIDTH : state->maxx - 3;
+}
+
+int
+calculate_help_width(const struct DrawState* state)
+{
+ assert(state != NULL);
+ return state->maxx > HELP_WIDTH + 2 ? HELP_WIDTH : state->maxx - 2;
+}
+
+int
+calculate_search_width(const struct DrawState* state)
+{
+ assert(state != NULL);
+ return state->maxx > SEARCH_WIDTH + 2 ? SEARCH_WIDTH : state->maxx - 2;
+}
+
+/* Calculates display length of an input buffer line.
+ * max_index = the input index up to which the length is calculated
+ */
+ssize_t
+display_length(uint32_t* buffer, const ssize_t max_index)
+{
+ uint32_t* pbuf = buffer;
+ ssize_t len = 0;
+ int w;
+
+ assert(buffer != NULL);
+ for (ssize_t i = 0; *pbuf && i < max_index; i++)
+ {
+ pbuf = &buffer[i];
+ if (*pbuf == '\t')
+ len += TAB_SIZE - (len % TAB_SIZE);
+ else if (!*pbuf)
+ len++;
+ else
+ {
+ w = wcwidth((wchar_t)*pbuf);
+ if (w > 0)
+ len += w;
+ }
+ }
+
+ return len;
+}
+
+int
+draw(const struct DrawState* state)
+{
+ assert(state != NULL);
+ if (draw_list_visible(state))
+ return 1;
+ if (state->show_help)
+ if (draw_help(state))
+ return 1;
+ if (state->show_edit)
+ if (draw_editbox(state))
+ return 1;
+ if (state->show_search)
+ if (draw_search(state))
+ return 1;
+ if (draw_status(state))
+ return 1;
+ return 0;
+}
+
+void
+draw_box(const int startx, const int endx, const int starty, const int endy,
+ const uint16_t fg, const uint16_t bg)
+{
+ int x, y;
+
+ tb_set_cell(startx, starty, BORDER_NW, fg, bg);
+ for (x = startx + 1; x < endx - 1; x++)
+ tb_set_cell(x, starty, BORDER_HOR, fg, bg);
+ tb_set_cell(endx - 1, starty, BORDER_NE, fg, bg);
+
+ for (y = starty + 1; y < endy - 2; y++)
+ {
+ tb_set_cell(startx, y, BORDER_VER, fg, bg);
+ for (x = startx + 1; x < endx - 1; x++)
+ tb_set_cell(x, y, ' ', fg, bg);
+ tb_set_cell(endx - 1, y, BORDER_VER, fg, bg);
+ tb_set_cell(endx, y, ' ', colors[C_SHADOW].fg,
+ colors[C_SHADOW].bg);
+ }
+
+ tb_set_cell(startx, endy - 2, BORDER_SW, fg, bg);
+ for (x = startx + 1; x < endx - 1; x++)
+ tb_set_cell(x, endy - 2, BORDER_HOR, fg, bg);
+ tb_set_cell(endx - 1, endy - 2, BORDER_SE, fg, bg);
+ tb_set_cell(endx, endy - 2, ' ', colors[C_SHADOW].fg,
+ colors[C_SHADOW].bg);
+
+ for (x = startx + 1; x < endx + 1; x++)
+ tb_set_cell(x, endy - 1, ' ', colors[C_SHADOW].fg,
+ colors[C_SHADOW].bg);
+}
+
+int
+draw_help(const struct DrawState* state)
+{
+ int startx, endx, starty, endy;
+ int w, h;
+ int row = 0;
+ int maxx, maxy;
+
+ assert(state != NULL);
+ w = state->help_width;
+ h = HELP_HEIGHT;
+ maxx = state->maxx;
+ maxy = state->maxy;
+
+ startx = w > maxx ? 0 : (maxx - w) / 2;
+ endx = w > maxx ? maxx - 1 : startx + w;
+
+ starty = h > maxy - 1 ? 0 : (maxy - h) / 2;
+ endy = h > maxy - 1 ? maxy - 2 : starty + h;
+
+ draw_box(startx, endx, starty, endy, colors[C_DLG].fg, colors[C_DLG].bg);
+ if (draw_string(startx, starty, colors[C_TITLE].fg, colors[C_TITLE].bg,
+ help_title, strlen(help_title) + 2, w, 1, 1, CENTER, 0, 0,
+ NULL, 0, 0))
+ return 1;
+ while ((size_t)row < LEN(help))
+ {
+ if (draw_string(startx + 1, starty + 1 + row, colors[C_DLG].fg,
+ colors[C_DLG].bg, help[row], w - 2, w - 2, 1, 1,
+ LEFT, 0, 1, NULL, 0, 0))
+ return 1;
+ row++;
+ }
+ tb_hide_cursor();
+ return 0;
+}
+
+int
+draw_editbox(const struct DrawState* state)
+{
+ struct PoEntry* entry;
+ char flags_buf[MAXFLAGSBUF];
+ char sbuf[MAXBUFLINE];
+ ssize_t dlen;
+ uint16_t fg, bg;
+ int startx, endx, starty, endy;
+ int w, h;
+ int maxx, maxy;
+
+ assert(state != NULL);
+ w = state->edit_width;
+ h = EDIT_HEIGHT;
+ maxx = state->maxx;
+ maxy = state->maxy;
+ entry = state->entries + state->msgid_number - 1;
+ dlen = 0;
+
+ format_flags(flags_buf, MAXFLAGSBUF, entry, 1);
+
+ startx = w > maxx ? 0 : (maxx - w) / 2;
+ endx = w > maxx ? maxx - 1 : startx + w;
+
+ starty = h > maxy - 1 ? 0 : (maxy - h) / 2;
+ endy = h > maxy - 1 ? maxy - 2 : starty + h;
+
+ draw_box(startx, endx, starty, endy, colors[C_DLG].fg, colors[C_DLG].bg);
+ if (draw_string(startx + 1, starty, colors[C_TITLE].fg,
+ colors[C_TITLE].bg, editbox_title, strlen(editbox_title) + 2,
+ w - 2, 1, 1, CENTER, 0, 0, NULL, 0, 0))
+ return 1;
+
+ fg = state->edit_info_focused ? colors[C_EDIT_MSGID_FOCUS].fg
+ : colors[C_EDIT_MSGID].fg;
+ bg = state->edit_info_focused ? colors[C_EDIT_MSGID_FOCUS].bg
+ : colors[C_EDIT_MSGID].bg;
+
+ draw_rect(startx + 1, endx - 1, starty + 1, endy - h / 2 - 1, fg, bg);
+ for (ssize_t i = 0; i < (h - 3) / 2 + 1
+ && (ssize_t)state->info_first_shown.row + i
+ < state->info_rows_count;
+ i++)
+ {
+ struct BufferLine* curline
+ = &state->info_buffer[state->info_first_shown.row + i];
+ if (!curline || !curline->text)
+ return 1;
+ dlen = display_length(curline->text, curline->length);
+ if (dlen > state->info_first_shown.col)
+ u32_draw_string(startx + 1, starty + 1 + i,
+ curline->fg == 0 ? fg : curline->fg,
+ curline->bg == 0 ? bg : curline->bg,
+ curline->text, w - 2, w - 2, 1, 1, LEFT,
+ state->info_first_shown.col, 1, NULL, 0, 0);
+ }
+
+ if (state->info_first_shown.row > 0)
+ tb_set_cell(endx - 2, starty + 1, ARROW_UP, fg, bg);
+ if (state->info_first_shown.row + (h - 3) / 2 < state->info_rows_count)
+ tb_set_cell(endx - 2, starty + (h - 3) / 2, ARROW_DOWN, fg, bg);
+
+ fg = state->edit_info_focused ? colors[C_EDIT_MSGSTR].fg
+ : colors[C_EDIT_MSGSTR_FOCUS].fg;
+ bg = state->edit_info_focused ? colors[C_EDIT_MSGSTR].bg
+ : colors[C_EDIT_MSGSTR_FOCUS].bg;
+
+ draw_rect(startx + 1, endx - 1, endy - h / 2 - 1, endy - 2, fg, bg);
+ for (ssize_t i = 0; i < (h - 3) / 2 + 1
+ && (ssize_t)state->input_first_shown.row + i
+ < state->input_rows_count;
+ i++)
+ {
+ struct BufferLine* curline
+ = &state->input_buffer[state->input_first_shown.row + i];
+ if (!curline || !curline->text)
+ return 1;
+ dlen = display_length(curline->text, curline->length);
+ if (dlen > state->input_first_shown.col)
+ u32_draw_string(startx + 1, endy - h / 2 - 1 + i, fg,
+ bg, curline->text, w - 2, w - 2, 1, 1, LEFT,
+ state->input_first_shown.col, 1, NULL, 0, 0);
+ }
+
+ if (state->edit_info_focused)
+ tb_hide_cursor();
+ else
+ tb_set_cursor(startx + 2 + state->input_display_column
+ - state->input_first_shown.col,
+ endy - h / 2 - 1 + state->coord.row
+ - state->input_first_shown.row);
+
+ if (entry->msgstr_count > 1)
+ snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%ld (plural: %d/%d)",
+ state->input_display_column,
+ state->input_first_shown.col, state->coord.row,
+ state->input_first_shown.row, state->input_rows_count,
+ state->msgstr_index + 1, entry->msgstr_count);
+ else
+ snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%ld",
+ state->input_display_column,
+ state->input_first_shown.col, state->coord.row,
+ state->input_first_shown.row, state->input_rows_count);
+ draw_string(startx + 1, endy - 2, colors[C_DLG].fg, colors[C_DLG].bg,
+ sbuf, strlen(sbuf) + 2, w - 2, 1, 1, LEFT, 0, 0, NULL, 0, 0);
+ if (entry->flags != FL_NONE)
+ if (draw_string(endx - MAXFLAGSBUF - 4 - 5, endy - 2,
+ colors[C_DLG].fg, colors[C_DLG].bg, flags_buf,
+ MAXFLAGSBUF + 4 - 1, MAXFLAGSBUF + 4 - 1, 0, 0,
+ LEFT, 0, 0, NULL, 0, 0))
+ return 1;
+ return 0;
+}
+
+int
+draw_list_visible(const struct DrawState* state)
+{
+ const struct PoEntry* current = NULL;
+ ssize_t i = 0;
+ int flags_len;
+ int max_screen_entries = 0;
+ int cy = 0;
+
+ assert(state != NULL);
+
+ if (state->msgid_count == 0)
+ return 0;
+
+ max_screen_entries = state->maxy - 1;
+ i = state->first_shown_msgid;
+
+ while (i <= state->real_msgid_count && cy < max_screen_entries)
+ {
+ char flags_buf[MAXFLAGSBUF];
+ int sel;
+
+ current = state->entries + i - 1;
+ if (current->obsolete)
+ {
+ i++;
+ continue;
+ }
+
+ sel = i == state->msgid_number;
+
+ if (sel)
+ draw_rect(0, state->maxx - 1, cy, cy + 1,
+ colors[C_LIST_SEL].fg, colors[C_LIST_SEL].bg);
+
+ format_flags(flags_buf, MAXFLAGSBUF, current, 0);
+ flags_len = strlen(flags_buf) + 1;
+
+ if (draw_string(0, cy,
+ sel ? colors[C_LIST_FLAGS_SEL].fg
+ : colors[C_LIST_FLAGS].fg,
+ sel ? colors[C_LIST_FLAGS_SEL].bg
+ : colors[C_LIST_FLAGS].bg,
+ flags_buf, flags_len, flags_len, 2, 0, LEFT, 0, 1,
+ NULL, 0, 0))
+ return 1;
+ if (current->msgid)
+ u32_draw_string(flags_len, cy,
+ sel ? colors[C_LIST_MSGID_SEL].fg
+ : colors[C_LIST_MSGID].fg,
+ sel ? colors[C_LIST_MSGID_SEL].bg
+ : colors[C_LIST_MSGID].bg,
+ current->msgid, state->maxx / 2 - flags_len,
+ state->maxx / 2 - flags_len, 2, 0, LEFT, 0, 1,
+ state->search, colors[C_SEARCH_HL].fg,
+ colors[C_SEARCH_HL].bg);
+ else if (current == state->entries) /* special */
+ draw_string(flags_len, cy,
+ sel ? colors[C_LIST_MSGID_SPECIAL_SEL].fg
+ : colors[C_LIST_MSGID_SPECIAL].fg,
+ sel ? colors[C_LIST_MSGID_SPECIAL_SEL].bg
+ : colors[C_LIST_MSGID_SPECIAL].bg,
+ first_entry_msgid, state->maxx / 2 - flags_len,
+ state->maxx / 2 - flags_len, 2, 0, LEFT, 0, 1,
+ NULL, 0, 0);
+
+ /* don't use msgstr_index as then the entire list will shift
+ * when changing currently displayed plural form msgstr in an
+ * edit box */
+ if (current->msgstr && current->msgstr_count > 0)
+ u32_draw_string(state->maxx / 2, cy,
+ current->warning
+ ? (sel ? colors[C_LIST_MSGSTR_SEL_WARN].fg
+ : colors[C_LIST_MSGSTR_WARN].fg)
+ : (sel ? colors[C_LIST_MSGSTR_SEL].fg
+ : colors[C_LIST_MSGSTR].fg),
+ current->warning
+ ? (sel ? colors[C_LIST_MSGSTR_SEL_WARN].bg
+ : colors[C_LIST_MSGSTR_WARN].bg)
+ : (sel ? colors[C_LIST_MSGSTR_SEL].bg
+ : colors[C_LIST_MSGSTR].bg),
+ *(current->msgstr), state->maxx / 2,
+ state->maxx / 2, 2, 2, LEFT, 0, 1,
+ state->search, colors[C_SEARCH_HL].fg,
+ colors[C_SEARCH_HL].bg);
+ i++;
+ cy++;
+ }
+ return 0;
+}
+
+void
+draw_rect(const int startx, const int endx, const int starty, const int endy,
+ const uint16_t fg, const uint16_t bg)
+{
+ for (int y = starty; y < endy; y++)
+ for (int x = startx; x < endx; x++)
+ tb_set_cell(x, y, ' ', fg, bg);
+}
+
+int
+draw_search(const struct DrawState* state)
+{
+ ssize_t len;
+ ssize_t dlen = 0;
+ int startx, endx, starty, endy;
+ int w, h;
+ int maxx, maxy;
+
+ assert(state != NULL);
+ w = state->search_width;
+ h = SEARCH_HEIGHT;
+ maxx = state->maxx;
+ maxy = state->maxy;
+ len = u32_strlen(state->search);
+
+ startx = w > maxx ? 0 : (maxx - w) / 2;
+ endx = w > maxx ? maxx - 1 : startx + w;
+
+ starty = maxy - 1 - h;
+ endy = maxy - 1;
+
+ draw_box(startx, endx, starty, endy, colors[C_SEARCH].fg,
+ colors[C_SEARCH].bg);
+ draw_rect(startx + 1, endx - 1, starty + 1, endy - 2,
+ colors[C_SEARCH_EDIT].fg, colors[C_SEARCH_EDIT].bg);
+ dlen = display_length(state->search, len);
+ if (dlen > state->search_first_shown_column)
+ u32_draw_string(startx + 1, starty + 1,
+ colors[C_SEARCH_EDIT].fg, colors[C_SEARCH_EDIT].bg,
+ state->search, w - 2, w - 2, 1, 1, LEFT,
+ state->search_first_shown_column, 1, NULL, 0, 0);
+ tb_set_cursor(startx + 2 + state->search_display_column
+ - state->search_first_shown_column,
+ starty + 1);
+ return 0;
+}
+
+int
+draw_status(const struct DrawState* state)
+{
+ const struct StatusSegment* pseg;
+ char* buf = NULL;
+ ssize_t seg_size;
+ int current_start;
+ int maxx;
+
+ assert(state != NULL);
+ maxx = state->maxx;
+ seg_size = maxx / LEN(status_segments);
+ current_start = 0;
+ pseg = status_segments;
+
+ if (state->in_prompt && *state->prompt)
+ {
+ if (draw_string(0, state->maxy - 1, colors[C_PROMPT].fg,
+ colors[C_PROMPT].bg, state->prompt,
+ strlen(state->prompt) + 2, maxx, 1, 1, CENTER, 0, 1,
+ NULL, 0, 0))
+ return 1;
+ }
+ else if (state->error)
+ {
+ if (draw_string(0, state->maxy - 1, colors[C_ERROR].fg,
+ colors[C_ERROR].bg, errors[state->error],
+ strlen(errors[state->error]) + 2, maxx, 1, 1,
+ CENTER, 0, 1, NULL, 0, 0))
+ return 1;
+ }
+ else
+ while (pseg < status_segments + LEN(status_segments))
+ {
+ int last = pseg - status_segments
+ == LEN(status_segments) - 1;
+ buf = calloc(limits[L_LINE_MAX], 1);
+ if (!buf)
+ return 1;
+
+ *buf = 0;
+ if (pseg->callback)
+ pseg->callback(buf, limits[L_LINE_MAX],
+ pseg->format, seg_size, state);
+ else
+ memccpy(buf, pseg->format, 0,
+ limits[L_LINE_MAX]);
+ if (pseg == status_segments + LEN(status_segments) - 1)
+ seg_size = maxx - 1 - current_start;
+ if (draw_string(current_start, state->maxy - 1,
+ colors[pseg->color_index].fg,
+ colors[pseg->color_index].bg, buf,
+ MIN(seg_size, (ssize_t)(strlen(buf) + 2)),
+ last ? seg_size + 1 : seg_size, 1, 1,
+ pseg->alignment, 0, 1, NULL, 0, 0))
+ {
+ free(buf);
+ return 1;
+ }
+ free(buf);
+ pseg++;
+ current_start += seg_size;
+ }
+ return 0;
+}
+
+/* mc - (max cols) pack text and padding into this many columns
+ * fc - (fill cols) this many columns from the left/right/center will be
+ * filled
+ * p_? - (padding) this many cells will be skipped from ? side
+ * ds - (display start) string starts being displayed from this display column
+ * counted from the start of the string; an "entry point" joining string
+ * index with screen
+ * fill - boolean; 1 = fill fc, 0 = don't fill fc
+ * srch - search string; if empty or NULL no highlighting happens
+ * sfg - fg color of search highlght
+ * sbg - bg color of search highlight
+ * */
+int
+draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
+ const char* s, const int mc, const int fc, const int p_l, const int p_r,
+ const Alignment align, const int ds, const int fill,
+ const uint32_t* srch, const uint16_t sfg, const uint16_t sbg)
+{
+ uint32_t* us = NULL;
+ assert(s != NULL);
+ us = calloc(strlen(s) + 1, sizeof(uint32_t));
+ if (!us)
+ return 1;
+ u8_string_to_unicode(us, s, mc);
+ u32_draw_string(x, y, fg, bg, us, mc, fc, p_l, p_r, align, ds, fill,
+ srch, sfg, sbg);
+ free(us);
+ return 0;
+}
+
+void
+format_filename(char* result, ssize_t result_size, const char* format,
+ const ssize_t seg_size, const struct DrawState* state)
+{
+ char* basename = NULL;
+ const char* pbase = NULL;
+ int dirty;
+
+ assert((state != NULL) && (format != NULL));
+ if (!result || seg_size < 4)
+ return;
+
+ pbase = u8_basename(state->filename);
+ basename = strndup(pbase, strlen(pbase) + 1);
+ dirty = (state->show_edit && state->dirty_before_edit) || state->dirty;
+ if ((ssize_t)(u8_strlen(basename) + 2) >= seg_size)
+ {
+ if (dirty)
+ basename[seg_size - 4] = 0;
+ else
+ basename[seg_size - 3] = 0;
+ snprintf(result, result_size, format, basename,
+ dirty ? "…*" : "…");
+ }
+ else
+ snprintf(result, result_size, format, basename,
+ dirty ? "*" : "");
+ free(basename);
+}
+
+void
+format_msgs(char* result, ssize_t result_size, const char* format,
+ const ssize_t seg_size, const struct DrawState* state)
+{
+ assert((state != NULL) && (format != NULL));
+ UNUSED(seg_size);
+ if (!result)
+ return;
+
+ snprintf(result, result_size, format,
+ state ? real_msgid_to_display_msgid(state) : 0,
+ state ? state->msgid_count : 0,
+ state ? state->untranslated_count : 0,
+ state ? state->fuzzy_count : 0,
+ state ? state->obsolete_count : 0);
+}
+
+void
+free_bufferline(struct BufferLine* bl)
{
if (!bl)
return;
+ free(bl->text);
bl->text = NULL;
bl->length = 0;
bl->fg = 0;
@@ -73,11 +628,27 @@ init_bufferline(struct BufferLine* bl)
}
void
-free_bufferline(struct BufferLine* bl)
+free_drawstate(struct DrawState* state)
+{
+ ssize_t row = 0;
+ assert(state != NULL);
+ for (row = 0; row < state->input_rows_count; row++)
+ free_bufferline(state->input_buffer + row);
+ free(state->input_buffer);
+ for (row = 0; row < state->info_rows_count; row++)
+ free_bufferline(state->info_buffer + row);
+ free(state->info_buffer);
+ for (row = 0; row < state->real_msgid_count; row++)
+ free_po_entry(state->entries + row);
+ free(state->search);
+ free(state->entries);
+}
+
+void
+init_bufferline(struct BufferLine* bl)
{
if (!bl)
return;
- free(bl->text);
bl->text = NULL;
bl->length = 0;
bl->fg = 0;
@@ -85,38 +656,56 @@ free_bufferline(struct BufferLine* bl)
}
void
-init_drawstate(struct DrawState* state, char* error, char* filename,
- char* prompt)
+init_coord(struct Coord* c, const int col, const int row)
{
- state->error = error;
- state->filename = filename;
- state->real_filename = NULL;
- state->backup_filename = NULL;
- state->rfn_len = 0;
- state->search = NULL;
- state->input_buffer = NULL;
- state->input_changed_fuzzy = UNCHANGED;
- state->input_column = 0;
- state->input_display_column = 0;
- state->input_first_shown_column = 0;
- state->input_first_shown_row = 0;
- state->input_row = 0;
- state->input_rows_count = 0;
- state->saved_column = -1;
- state->info_buffer = NULL;
- state->info_first_shown_column = 0;
- state->info_first_shown_row = 0;
+ assert(c != NULL);
+ c->col = col;
+ c->row = row;
+}
+
+void
+init_drawstate(struct DrawState* state, char* filename, char* prompt)
+{
+ assert(state != NULL);
+ state->error = ERR_NONE;
+ state->action = ACT_IDLE;
+ state->prompt = prompt;
+ state->filename = filename;
+ state->orig_real_filename = NULL;
+ state->real_filename = NULL;
+ state->backup_filename = NULL;
+ state->rfn_len = 0;
+ state->search = NULL;
+ state->input_buffer = NULL;
+ state->buffer_size = 0;
+ init_coord(&state->coord, 0, 0);
+ init_coord(&state->first_shown, 0, 0);
+ init_coord(&state->input_first_shown, 0, 0);
+ // state->input_column = 0;
+ // state->input_row = 0;
+ /*state->input_first_shown_column = 0;
+ state->input_first_shown_row = 0;*/
+ state->input_rows_count = 0;
+ state->input_display_column = 0;
+ state->input_changed_fuzzy = UNCHANGED;
+ state->saved_column = -1;
+ state->info_buffer = NULL;
+ init_coord(&state->info_first_shown, 0, 0);
+ /*state->info_first_shown_column = 0;
+ state->info_first_shown_row = 0;*/
state->info_rows_count = 0;
+ state->info_rows_maxlen = 0;
state->help_width = calculate_help_width(state);
state->edit_width = calculate_edit_width(state);
state->search_width = calculate_search_width(state);
- state->paste_buffer = NULL;
- state->paste_rows_count = 0;
+ state->paste = NULL;
+ state->paste_count = 0;
+ state->paste_size = 0;
state->search_column = 0;
state->search_first_shown_column = 0;
state->search_display_column = 0;
+ state->entries = NULL;
state->nplurals = 2;
- state->prompt = prompt;
state->prompt_callback = NULL;
state->running = 1;
state->dirty = 0;
@@ -127,140 +716,31 @@ init_drawstate(struct DrawState* state, char* error, char* filename,
state->show_edit = 0;
state->edit_info_focused = 0;
state->show_search = 0;
+ state->recalculate_size = 1;
+ state->maxx = 0;
+ state->maxy = 0;
state->msgid_number = state->msgid_count > 0 ? 1 : 0;
+ state->msgid_count = 0;
+ state->msgid_size = 0;
state->fuzzy_count = 0;
state->untranslated_count = 0;
state->obsolete_count = 0;
state->obsolete_at_end_count = 0;
+ state->real_msgid_count = 0;
state->msgstr_index = 0;
state->first_shown_msgid = state->msgid_number;
state->backup_possible = 1;
}
-void
-free_drawstate(struct DrawState* state)
-{
- size_t i = 0;
-
- for (i = 0; i < state->input_rows_count; i++)
- free_bufferline(state->input_buffer + i);
- free(state->input_buffer);
- for (i = 0; i < state->paste_rows_count; i++)
- free_bufferline(state->paste_buffer + i);
- free(state->paste_buffer);
- for (i = 0; i < state->info_rows_count; i++)
- free_bufferline(state->info_buffer + i);
- free(state->info_buffer);
- for (i = 0; i < state->real_msgid_count; i++)
- free_po_entry(state->entries + i);
- free(state->search);
- free(state->entries);
-}
-
-void
-format_filename(char* result, size_t result_size, const char* format,
- const struct DrawState* state)
-{
- if (!result)
- return;
- snprintf(result, result_size, format, u8_basename(state->filename),
- state->show_edit ? (
- (state->dirty_before_edit || state->dirty) ? "*" : "")
- : (state->dirty ? "*" : ""));
-}
-
-size_t real_msgid_to_display_msgid(const struct DrawState* state);
-
-void
-format_msgs(char* result, size_t result_size, const char* format,
- const struct DrawState* state)
-{
- if (!result)
- return;
-
- snprintf(result, result_size, format,
- state ? real_msgid_to_display_msgid(state) : 0,
- state ? state->msgid_count : 0,
- state ? state->untranslated_count : 0,
- state ? state->fuzzy_count : 0,
- state ? state->obsolete_count : 0);
-}
-
-struct DrawState*
-set_nplurals(struct DrawState* state, struct PoEntry* entry)
-{
- if (!entry || !entry->plural_forms)
- return NULL;
-
- int read_number = 0;
- uint32_t* ppf = entry->plural_forms;
- char token[MAXBUFLINE];
- char* ptoken = token;
-
- *ptoken = 0;
- while (*ppf)
- {
- if (u32_starts_with(ppf, (uint32_t*)L"nplurals="))
- {
- ppf += strlen("nplurals=");
- read_number = 1;
- }
- else if (read_number && *ppf >= '0' && *ppf <= '9')
- {
- *ptoken++ = *ppf++;
- *ptoken = 0;
- }
- else if (read_number && *ppf == ';')
- {
- *ptoken = 0;
- ptoken = token;
- break;
- }
- else
- ppf++;
- }
- if (*ptoken)
- {
- state->nplurals = strtol(ptoken, NULL, 0);
- if (errno)
- return NULL;
- }
-
- return state;
-}
-
-/* Calculates display length of an input buffer line.
- * max_index = the input index up to which the length is calculated
- */
-size_t
-display_length(const struct DrawState* state, uint32_t* buffer,
- const size_t max_index)
-{
- size_t len = 0;
- uint32_t* pbuf = buffer;
-
- for (size_t i = 0; *pbuf && i < max_index; i++)
- {
- pbuf = buffer + i;
- if (*pbuf == '\t')
- len += TAB_SIZE - (len % TAB_SIZE);
- else
- len++;
- }
-
- return len;
-}
-
/* Calculates input length from a display length.
* max_index = the display index up to which the length is calculated
*/
-size_t
-input_length(const struct DrawState* state, uint32_t* buffer,
- const size_t max_index)
+ssize_t
+input_length(uint32_t* buffer, const ssize_t max_index)
{
- size_t len = 0;
uint32_t* pbuf = buffer;
- size_t i = 0;
+ ssize_t len = 0;
+ ssize_t i = 0;
for (i = 0; *pbuf && len < max_index; i++)
{
@@ -274,89 +754,78 @@ input_length(const struct DrawState* state, uint32_t* buffer,
return i;
}
-int
-calculate_help_width(const struct DrawState* state)
+size_t
+real_msgid_to_display_msgid(const struct DrawState* state)
{
- return state->maxx > HELP_WIDTH + 2 ? HELP_WIDTH : state->maxx - 2;
-}
+ struct PoEntry* current = state->entries;
+ size_t ret = 1;
-int
-calculate_edit_width(const struct DrawState* state)
-{
- return state->maxx > EDIT_WIDTH + 3 ? EDIT_WIDTH : state->maxx - 3;
-}
+ if (!state->msgid_count)
+ return 0;
-int
-calculate_search_width(const struct DrawState* state)
-{
- return state->maxx > SEARCH_WIDTH + 2 ? SEARCH_WIDTH : state->maxx - 2;
+ while (current != state->entries + state->msgid_number - 1)
+ {
+ if (current != state->entries + state->msgid_number - 1
+ && !current->obsolete)
+ ret++;
+ current++;
+ }
+
+ return ret;
}
void
-draw_rect(const int startx, const int endx, const int starty, const int endy,
- const uint16_t fg, const uint16_t bg)
+recalculate_size(struct DrawState* state)
{
- for (int y = starty; y < endy; y++)
- for (int x = startx; x < endx; x++)
- tb_change_cell(x, y, ' ', fg, bg);
+ assert(state != NULL);
+ state->maxx = tb_width();
+ state->maxy = tb_height();
+ state->help_width = calculate_help_width(state);
+ state->edit_width = calculate_edit_width(state);
+ state->search_width = calculate_search_width(state);
}
-void
-draw_box(const int startx, const int endx, const int starty, const int endy,
- const uint16_t fg, const uint16_t bg)
+struct DrawState*
+set_nplurals(struct DrawState* state, struct PoEntry* entry)
{
- int x, y;
+ if (!entry || !entry->plural_forms)
+ return NULL;
- tb_change_cell(startx, starty, BORDER_NW, fg, bg);
- for (x = startx + 1; x < endx - 1; x++)
- tb_change_cell(x, starty, BORDER_HOR, fg, bg);
- tb_change_cell(endx - 1, starty, BORDER_NE, fg, bg);
+ int read_number = 0;
+ uint32_t* ppf = entry->plural_forms;
+ char token[MAXBUFLINE];
+ char* ptoken = token;
- for (y = starty + 1; y < endy - 2; y++)
+ *ptoken = 0;
+ while (*ppf)
{
- tb_change_cell(startx, y, BORDER_VER, fg, bg);
- for (x = startx + 1; x < endx - 1; x++)
- tb_change_cell(x, y, ' ', fg, bg);
- tb_change_cell(endx - 1, y, BORDER_VER, fg, bg);
- tb_change_cell(endx, y, ' ', SHADOW_FG, SHADOW_BG);
+ if (u32_starts_with(ppf, (uint32_t*)L"nplurals="))
+ {
+ ppf += strlen("nplurals=");
+ read_number = 1;
+ }
+ else if (read_number && *ppf >= '0' && *ppf <= '9')
+ {
+ *ptoken++ = *ppf++;
+ *ptoken = 0;
+ }
+ else if (read_number && *ppf == ';')
+ {
+ *ptoken = 0;
+ ptoken = token;
+ break;
+ }
+ else
+ ppf++;
+ }
+ if (*ptoken)
+ {
+ state->nplurals = strtol(ptoken, NULL, 0);
+ if (errno)
+ return NULL;
}
- tb_change_cell(startx, endy - 2, BORDER_SW, fg, bg);
- for (x = startx + 1; x < endx - 1; x++)
- tb_change_cell(x, endy - 2, BORDER_HOR, fg, bg);
- tb_change_cell(endx - 1, endy - 2, BORDER_SE, fg, bg);
- tb_change_cell(endx, endy - 2, ' ', SHADOW_FG, SHADOW_BG);
-
- for (x = startx + 1; x < endx + 1; x++)
- tb_change_cell(x, endy - 1, ' ', SHADOW_FG, SHADOW_BG);
-}
-
-/* mc - (max cols) pack text and padding into this many columns
- * fc - (fill cols) this many columns from the left/right/center will be
- * filled
- * p_? - (padding) this many cells will be skipped from ? side
- * ds - (display start) string starts being displayed from this display column
- * counted from the start of the string; an "entry point" joining string
- * index with screen
- * fill - boolean; 1 = fill fc, 0 = don't fill fc
- * srch - search string; if empty or NULL no highlighting happens
- * sfg - fg color of search highlght
- * sbg - bg color of search highlight
- * */
-const char*
-draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
- const char* s, const int mc, const int fc, const int p_l, const int p_r,
- const Alignment align, const int ds, const int fill,
- const uint32_t* srch, const uint16_t sfg, const uint16_t sbg)
-{
- uint32_t* us = calloc(strlen(s) + 1, sizeof(uint32_t));
- if (!us)
- return NULL;
- u8_string_to_unicode(us, s, mc);
- u32_draw_string(x, y, fg, bg, us, mc, fc, p_l, p_r, align, ds, fill,
- srch, sfg, sbg);
- free(us);
- return s;
+ return state;
}
/* mc - (max cols) pack text and padding into this many columns
@@ -432,11 +901,11 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
/* fill */
if (fill)
for (int j = 0; j < fc; j++)
- tb_change_cell(x + j, y, ' ', fg, bg);
+ tb_set_cell(x + j, y, ' ', fg, bg);
/* left padding */
for (int j = 0; j < p_l; j++)
- tb_change_cell(x + d + j, y, ' ', fg, bg);
+ tb_set_cell(x + d + j, y, ' ', fg, bg);
sc = 0;
@@ -478,8 +947,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
for (int j = 0;
j < ssc - (ssc % ts) + ts - ds;
j++)
- tb_change_cell(x + d + p_l + sc
- + j,
+ tb_set_cell(x + d + p_l + sc + j,
y, ' ', cfg, cbg);
sc += ssc + (ts - (ssc % ts)) - ds;
hctl -= ssc + (ts - (ssc % ts)) - ds;
@@ -510,8 +978,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
if (ssc + (ts - (ssc % ts)) >= ds + c)
{
for (int j = 0; j < ds + c - ssc; j++)
- tb_change_cell(x + d + p_l + sc
- + j,
+ tb_set_cell(x + d + p_l + sc + j,
y, ' ', cfg, cbg);
sc = c;
ssc = ds + c;
@@ -531,8 +998,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
* ds
* */
for (int j = 0; j < ts - (ssc % ts); j++)
- tb_change_cell(x + d + p_l + sc
- + j,
+ tb_set_cell(x + d + p_l + sc + j,
y, ' ', cfg, cbg);
sc += ts - (ssc % ts);
hctl -= ts - (ssc % ts);
@@ -547,7 +1013,7 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
if (ssc >= ds)
{
if (sc < c)
- tb_change_cell(x + d + p_l + sc, y, *ps,
+ tb_set_cell(x + d + p_l + sc, y, *ps,
cfg, cbg);
sc++;
hctl--;
@@ -566,342 +1032,5 @@ u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
/* right padding */
for (int j = 0; j < p_r; j++)
- tb_change_cell(x + d + p_l + c + j, y, ' ', fg, bg);
-}
-
-const struct DrawState*
-draw_help(const struct DrawState* state)
-{
- if (!state)
- return NULL;
-
- int startx, endx, starty, endy;
- int w = state->help_width;
- int h = HELP_HEIGHT;
- int row = 0;
- int maxx = state->maxx;
- int maxy = state->maxy;
-
- startx = w > maxx ? 0 : (maxx - w) / 2;
- endx = w > maxx ? maxx - 1 : startx + w;
-
- starty = h > maxy - 1 ? 0 : (maxy - h) / 2;
- endy = h > maxy - 1 ? maxy - 2 : starty + h;
-
- draw_box(startx, endx, starty, endy, DLG_FG, DLG_BG);
- if (!draw_string(startx, starty, TITLE_FG, TITLE_BG, help_title,
- strlen(help_title) + 2, w, 1, 1, CENTER, 0, 0, NULL, 0, 0))
- return NULL;
- while (row < LEN(help))
- {
- if (!draw_string(startx + 1, starty + 1 + row, DLG_FG, DLG_BG,
- help[row], w - 2, w - 2, 1, 1, LEFT, 0, 1, NULL, 0,
- 0))
- return NULL;
- row++;
- }
- return state;
-}
-
-const struct DrawState*
-draw_editbox(const struct DrawState* state)
-{
- if (!state)
- return NULL;
-
- int startx, endx, starty, endy;
- int w = state->edit_width;
- int h = EDIT_HEIGHT;
- int maxx = state->maxx;
- int maxy = state->maxy;
- char flags_buf[MAXFLAGSBUF];
- struct PoEntry* entry = state->entries + state->msgid_number - 1;
- size_t dlen = 0;
-
- format_flags(flags_buf, MAXFLAGSBUF, entry, 1);
-
- startx = w > maxx ? 0 : (maxx - w) / 2;
- endx = w > maxx ? maxx - 1 : startx + w;
-
- starty = h > maxy - 1 ? 0 : (maxy - h) / 2;
- endy = h > maxy - 1 ? maxy - 2 : starty + h;
-
- draw_box(startx, endx, starty, endy, DLG_FG, DLG_BG);
- if (!draw_string(startx + 1, starty, TITLE_FG, TITLE_BG, editbox_title,
- strlen(editbox_title) + 2, w - 2, 1, 1, CENTER, 0, 0, NULL,
- 0, 0))
- return NULL;
-
- uint16_t fg = state->edit_info_focused ? EDIT_MSGID_FOCUS_FG
- : EDIT_MSGID_FG;
- uint16_t bg = state->edit_info_focused ? EDIT_MSGID_FOCUS_BG
- : EDIT_MSGID_BG;
-
- draw_rect(startx + 1, endx - 1, starty + 1, endy - h / 2 - 1, fg, bg);
- for (size_t i = 0; i < (h - 3) / 2 + 1
- && state->info_first_shown_row + i < state->info_rows_count;
- i++)
- {
- struct BufferLine* curline
- = &state->info_buffer[state->info_first_shown_row + i];
- if (!curline || !curline->text)
- return NULL;
- dlen = display_length(state, curline->text, curline->length);
- if (dlen > state->info_first_shown_column)
- u32_draw_string(startx + 1, starty + 1 + i,
- curline->fg == 0 ? fg : curline->fg,
- curline->bg == 0 ? bg : curline->bg,
- curline->text, w - 2, w - 2, 1, 1, LEFT,
- state->info_first_shown_column, 1, NULL, 0, 0);
- }
-
- if (state->info_first_shown_row > 0)
- u32_draw_string(endx - 2, starty + 1, fg, bg,
- (uint32_t*)L"\u2191", 1, 1, 0, 0, LEFT, 0, 0, NULL, 0,
- 0);
- if (state->info_first_shown_row + (h - 3) / 2 < state->info_rows_count)
- u32_draw_string(endx - 2, starty + (h - 3) / 2, fg, bg,
- (uint32_t*)L"\u2193", 1, 1, 0, 0, LEFT, 0, 0, NULL, 0,
- 0);
-
- fg = state->edit_info_focused ? EDIT_MSGSTR_FG : EDIT_MSGSTR_FOCUS_FG;
- bg = state->edit_info_focused ? EDIT_MSGSTR_BG : EDIT_MSGSTR_FOCUS_BG;
-
- draw_rect(startx + 1, endx - 1, endy - h / 2 - 1, endy - 2, fg, bg);
- for (size_t i = 0; i < (h - 3) / 2 + 1
- && state->input_first_shown_row + i < state->input_rows_count;
- i++)
- {
- struct BufferLine* curline
- = &state->input_buffer[state->input_first_shown_row + i];
- if (!curline || !curline->text)
- return NULL;
- dlen = display_length(state, curline->text, curline->length);
- if (dlen > state->input_first_shown_column)
- u32_draw_string(startx + 1, endy - h / 2 - 1 + i, fg,
- bg, curline->text, w - 2, w - 2, 1, 1, LEFT,
- state->input_first_shown_column, 1, NULL, 0, 0);
- }
-
- if (state->edit_info_focused)
- tb_hide_cursor();
- else
- tb_set_cursor(startx + 2 + state->input_display_column
- - state->input_first_shown_column,
- endy - h / 2 - 1 + state->input_row
- - state->input_first_shown_row);
-
- char sbuf[MAXBUFLINE];
- if (entry->msgstr_count > 1)
- snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%d (plural: %d/%d)",
- state->input_display_column,
- state->input_first_shown_column, state->input_row,
- state->input_first_shown_row, state->input_rows_count,
- state->msgstr_index + 1, entry->msgstr_count);
- else
- snprintf(sbuf, MAXBUFLINE, "%d[%d]:%d[%d]/%d",
- state->input_display_column,
- state->input_first_shown_column, state->input_row,
- state->input_first_shown_row, state->input_rows_count);
- draw_string(startx + 1, endy - 2, DLG_FG, DLG_BG, sbuf,
- strlen(sbuf) + 2, w - 2, 1, 1, LEFT, 0, 0, NULL, 0, 0);
- if (entry->flags != FL_NONE)
- if (!draw_string(endx - MAXFLAGSBUF - 4 - 5, endy - 2, DLG_FG,
- DLG_BG, flags_buf, MAXFLAGSBUF + 4 - 1,
- MAXFLAGSBUF + 4 - 1, 0, 0, LEFT, 0, 0, NULL, 0, 0))
- return NULL;
- return state;
-}
-
-const struct DrawState*
-draw_search(const struct DrawState* state)
-{
- int startx, endx, starty, endy;
- int w = state->search_width;
- int h = SEARCH_HEIGHT;
- int maxx = state->maxx;
- int maxy = state->maxy;
- size_t len = u32_strlen(state->search);
- size_t dlen = 0;
-
- startx = w > maxx ? 0 : (maxx - w) / 2;
- endx = w > maxx ? maxx - 1 : startx + w;
-
- starty = maxy - 1 - h;
- endy = maxy - 1;
-
- draw_box(startx, endx, starty, endy, SEARCH_FG, SEARCH_BG);
- draw_rect(startx + 1, endx - 1, starty + 1, endy - 2, SEARCH_EDIT_FG,
- SEARCH_EDIT_BG);
- dlen = display_length(state, state->search, len);
- if (dlen > state->search_first_shown_column)
- u32_draw_string(startx + 1, starty + 1, SEARCH_EDIT_FG,
- SEARCH_EDIT_BG, state->search, w - 2, w - 2, 1, 1, LEFT,
- state->search_first_shown_column, 1, NULL, 0, 0);
- tb_set_cursor(startx + 2 + state->search_display_column
- - state->search_first_shown_column,
- starty + 1);
- return state;
-}
-
-const struct DrawState*
-draw_status(const struct DrawState* state)
-{
- if (!state)
- return NULL;
-
- char buf[MAXBUFLINE];
- int seg_size = state->maxx / 3;
- int current_start = 0;
- const struct StatusSegment* pseg = status_segments;
-
- if (state->in_prompt && state->prompt[0])
- {
- if (!draw_string(0, state->maxy - 1, PROMPT_FG, PROMPT_BG,
- state->prompt, strlen(state->prompt) + 2,
- state->maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0))
- return NULL;
- }
- else if (state->error && state->error[0])
- {
- if (!draw_string(0, state->maxy - 1, ERROR_FG, ERROR_BG,
- state->error, strlen(state->error) + 2, state->maxx,
- 1, 1, CENTER, 0, 1, NULL, 0, 0))
- return NULL;
- }
- else
- while (pseg < status_segments + LEN(status_segments))
- {
- int last = pseg - status_segments
- == LEN(status_segments) - 1;
- buf[0] = 0;
- if (pseg->callback)
- pseg->callback(buf, MAXBUFLINE, pseg->format,
- state);
- else
- strncpy(buf, pseg->format, MAXBUFLINE - 1);
- buf[MAXBUFLINE - 1] = 0;
- if (pseg == status_segments + LEN(status_segments) - 1)
- seg_size = state->maxx - 1 - current_start;
- if (!draw_string(current_start, state->maxy - 1,
- pseg->fg, pseg->bg, buf,
- MIN(seg_size, strlen(buf) + 2),
- last ? seg_size + 1 : seg_size, 1, 1,
- pseg->alignment, 0, 1, NULL, 0, 0))
- return NULL;
- pseg++;
- current_start += seg_size;
- }
- return state;
-}
-
-const struct DrawState*
-draw_list_visible(const struct DrawState* state)
-{
- int flags_len;
- const struct PoEntry* current = NULL;
- int max_screen_entries = 0;
- int cy = 0;
- size_t i = 0;
-
- if (!state)
- return NULL;
-
- if (state->msgid_count == 0)
- return state;
-
- max_screen_entries = state->maxy - 1;
- i = state->first_shown_msgid;
-
- while (i <= state->real_msgid_count && cy < max_screen_entries)
- {
- int sel;
- char flags_buf[MAXFLAGSBUF];
-
- current = state->entries + i - 1;
- if (current->obsolete)
- {
- i++;
- continue;
- }
-
- sel = i == state->msgid_number;
-
- if (sel)
- draw_rect(0, state->maxx - 1, cy, cy + 1, LIST_SEL_FG,
- LIST_SEL_BG);
-
- format_flags(flags_buf, MAXFLAGSBUF, current, 0);
- flags_len = strlen(flags_buf) + 1;
-
- if (!draw_string(0, cy, sel ? LIST_FLAGS_SEL_FG : LIST_FLAGS_FG,
- sel ? LIST_FLAGS_SEL_BG : LIST_FLAGS_BG, flags_buf,
- flags_len, flags_len, 2, 0, LEFT, 0, 1, NULL, 0, 0))
- return NULL;
- if (current->msgid)
- u32_draw_string(flags_len, cy,
- sel ? LIST_MSGID_SEL_FG : LIST_MSGID_FG,
- sel ? LIST_MSGID_SEL_BG : LIST_MSGID_BG,
- current->msgid, state->maxx / 2 - flags_len,
- state->maxx / 2 - flags_len, 2, 0, LEFT, 0, 1,
- state->search, SEARCH_HL_FG, SEARCH_HL_BG);
- else if (current == state->entries) /* special */
- draw_string(flags_len, cy,
- sel ? LIST_MSGID_SPECIAL_SEL_FG
- : LIST_MSGID_SPECIAL_FG,
- sel ? LIST_MSGID_SPECIAL_SEL_BG
- : LIST_MSGID_SPECIAL_BG,
- first_entry_msgid, state->maxx / 2 - flags_len,
- state->maxx / 2 - flags_len, 2, 0, LEFT, 0, 1,
- NULL, 0, 0);
-
- /* don't use msgstr_index as then the entire list will shift
- * when changing currently displayed plural form msgstr in an
- * edit box */
- if (current->msgstr && current->msgstr_count > 0)
- u32_draw_string(state->maxx / 2, cy,
- current->warning
- ? (sel ? LIST_MSGSTR_SEL_WARN_FG
- : LIST_MSGSTR_WARN_FG)
- : (sel ? LIST_MSGSTR_SEL_FG
- : LIST_MSGSTR_FG),
- current->warning
- ? (sel ? LIST_MSGSTR_SEL_WARN_BG
- : LIST_MSGSTR_WARN_BG)
- : (sel ? LIST_MSGSTR_SEL_BG
- : LIST_MSGSTR_BG),
- *(current->msgstr), state->maxx / 2,
- state->maxx / 2, 2, 2, LEFT, 0, 1,
- state->search, SEARCH_HL_FG, SEARCH_HL_BG);
- i++;
- cy++;
- }
- return state;
-}
-
-const struct DrawState*
-draw(const struct DrawState* state)
-{
- if (!state)
- return NULL;
-
- if (!draw_list_visible(state))
- return NULL;
-
- if (state->show_help)
- if (!draw_help(state))
- return NULL;
-
- if (state->show_edit)
- if (!draw_editbox(state))
- return NULL;
-
- if (state->show_search)
- if (!draw_search(state))
- return NULL;
-
- if (!draw_status(state))
- return NULL;
-
- return state;
+ tb_set_cell(x + d + p_l + c + j, y, ' ', fg, bg);
}
diff --git a/draw.h b/draw.h
@@ -3,11 +3,11 @@
* See the file LICENSE for exact copyright and license details. */
#include <stddef.h>
+#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <wchar.h>
-#define TB_OPT_V1_COMPAT
#include "termbox.h"
#define BORDER_NW ((uint32_t)0x250c)
@@ -16,6 +16,8 @@
#define BORDER_SE ((uint32_t)0x2518)
#define BORDER_VER ((uint32_t)0x2502)
#define BORDER_HOR ((uint32_t)0x2500)
+#define ARROW_UP ((uint32_t)0x2191)
+#define ARROW_DOWN ((uint32_t)0x2193)
#define EDIT_WIDTH 70
#define EDIT_HEIGHT 20
@@ -28,70 +30,124 @@
typedef enum { LEFT, CENTER, RIGHT } Alignment;
+typedef enum {
+ C_CLEAR,
+ C_DLG,
+ C_EDIT_MSGID,
+ C_EDIT_MSGID_FOCUS,
+ C_EDIT_MSGID_HEADING,
+ C_EDIT_MSGSTR,
+ C_EDIT_MSGSTR_FOCUS,
+ C_ERROR,
+ C_FLAG,
+ C_LIST_FLAGS,
+ C_LIST_FLAGS_SEL,
+ C_LIST_MSGID,
+ C_LIST_MSGID_SEL,
+ C_LIST_MSGID_SPECIAL,
+ C_LIST_MSGID_SPECIAL_SEL,
+ C_LIST_MSGSTR,
+ C_LIST_MSGSTR_SEL,
+ C_LIST_MSGSTR_WARN,
+ C_LIST_MSGSTR_SEL_WARN,
+ C_LIST_SEL,
+ C_MSGID,
+ C_MSGSTR,
+ C_PROMPT,
+ C_SEARCH,
+ C_SEARCH_EDIT,
+ C_SEARCH_HL,
+ C_SHADOW,
+ C_STATUS_1,
+ C_STATUS_2,
+ C_STATUS_3,
+ C_TITLE
+} ColorIndex;
+
typedef enum { UNCHANGED, SET, CLEARED } FlagChange;
+struct Color {
+ uint16_t fg;
+ uint16_t bg;
+};
+
+struct Coord {
+ int col;
+ int row;
+};
+
struct BufferLine {
uint32_t* text;
- size_t length;
+ ssize_t length; /* both in uint32_t units */
+ ssize_t size;
uint16_t fg;
uint16_t bg;
};
struct DrawState {
- char* error;
char* prompt;
char* filename;
- char* real_filename; /* what symlink points to */
+ char* orig_real_filename; /* what symlink points to */
+ char* real_filename; /* directory holding the symlink prepended to it */
char* backup_filename;
- size_t rfn_len;
+ ssize_t rfn_len;
uint32_t* search;
+ int error;
+ int action;
struct BufferLine* input_buffer; /* edit box */
- int input_column;
- int input_row;
- int input_first_shown_column;
- int input_first_shown_row;
- int input_rows_count;
- int input_display_column;
+ ssize_t buffer_size; /* buffer parameter */
+ struct Coord coord; /* buffer coordinates */
+ struct Coord first_shown; /* display coordinates */
+ struct Coord input_first_shown; /* display coordinates */
+ /*int input_column;
+ int input_row;*/
+ /*int input_first_shown_column;
+ int input_first_shown_row;*/
+ ssize_t input_rows_count; /* buffer parameter */
+ int input_display_column; /* display coordinate */
FlagChange input_changed_fuzzy;
- int saved_column;
+ int saved_column; /* display coordinate */
struct BufferLine* info_buffer; /* information pane above
* edit box */
- int info_first_shown_column;
- int info_first_shown_row;
+ struct Coord info_first_shown; /* display coordinates */
+ /*int info_first_shown_column;
+ int info_first_shown_row;*/
int info_rows_count;
int info_rows_maxlen;
- int help_width;
- int edit_width;
- int search_width;
- struct BufferLine* paste_buffer;
- int paste_rows_count;
+ int help_width; /* display parameter */
+ int edit_width; /* display parameter */
+ int search_width; /* display parameter */
+ struct BufferLine* paste;
+ ssize_t paste_count;
+ ssize_t paste_size;
int search_column;
int search_first_shown_column;
int search_display_column;
struct PoEntry* entries;
- long nplurals;
- void (*prompt_callback)(struct DrawState*, struct tb_event*);
+ int nplurals;
+ int (*prompt_callback)(struct DrawState*, struct tb_event*);
int running;
int dirty;
int dirty_before_edit;
- size_t fuzzy_before_edit;
+ ssize_t fuzzy_before_edit;
int in_prompt;
int show_help;
int show_edit;
int edit_info_focused;
int show_search;
+ int recalculate_size;
int maxx;
int maxy;
- size_t msgid_number; /* "real" msgid (inc. obsolete msgids) */
- size_t msgid_count; /* without obsolete msgids */
- size_t msgid_size;
- size_t fuzzy_count;
- size_t untranslated_count;
- size_t obsolete_count;
- size_t obsolete_at_end_count;
- size_t real_msgid_count; /* msgid_count + obsolete_count */
- int msgstr_index; /* with msgstr[] */
- size_t first_shown_msgid;
+ ssize_t msgid_number; /* "real" msgid (inc. obsolete msgids) */
+ ssize_t msgid_count; /* without obsolete msgids */
+ ssize_t msgid_size;
+ ssize_t fuzzy_count;
+ ssize_t untranslated_count;
+ ssize_t obsolete_count;
+ ssize_t obsolete_at_end_count;
+ ssize_t real_msgid_count; /* msgid_count + obsolete_count */
+ int msgstr_index; /* with msgstr[] */
+ ssize_t first_shown_msgid;
int backup_possible; /* is saving backup possible? */
};
@@ -102,42 +158,47 @@ struct Key {
struct StatusSegment {
const char* format;
- void (*callback)(char*, size_t, const char*, const struct DrawState*);
- uint16_t fg;
- uint16_t bg;
+ void (*callback)(char*, ssize_t, const char*, const ssize_t,
+ const struct DrawState*);
+ ColorIndex color_index;
Alignment alignment;
};
-void init_bufferline(struct BufferLine* bl);
-void free_bufferline(struct BufferLine* bl);
-void init_drawstate(struct DrawState* state, char* error, char* filename,
- char* prompt);
-void free_drawstate(struct DrawState* state);
-struct DrawState* set_nplurals(struct DrawState* state, struct PoEntry* entry);
-size_t display_length(const struct DrawState* state, uint32_t* buffer,
- const size_t max_index);
-size_t input_length(const struct DrawState* state, uint32_t* buffer,
- const size_t max_index);
-int calculate_help_width(const struct DrawState* state);
+void assign_coord(struct Coord* dest, const struct Coord src);
int calculate_edit_width(const struct DrawState* state);
+int calculate_help_width(const struct DrawState* state);
int calculate_search_width(const struct DrawState* state);
-void draw_rect(const int startx, const int endx, const int starty,
- const int endy, const uint16_t fg, const uint16_t bg);
+ssize_t display_length(uint32_t* buffer, const ssize_t max_index);
+int draw(const struct DrawState* state);
void draw_box(const int startx, const int endx, const int starty,
const int endy, const uint16_t fg, const uint16_t bg);
-const char* draw_string(const int x, const int y, const uint16_t fg,
- const uint16_t bg, const char* s, const int max_cols,
- const int fill_cols, const int padding_left, const int padding_right,
- const Alignment align, const int ds, const int fill,
- const uint32_t* srch, const uint16_t sfg, const uint16_t sbg);
+int draw_help(const struct DrawState* state);
+int draw_editbox(const struct DrawState* state);
+int draw_list_visible(const struct DrawState* state);
+void draw_rect(const int startx, const int endx, const int starty,
+ const int endy, const uint16_t fg, const uint16_t bg);
+int draw_search(const struct DrawState* state);
+int draw_status(const struct DrawState* state);
+int draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg,
+ const char* s, const int max_cols, const int fill_cols,
+ const int padding_left, const int padding_right, const Alignment align,
+ const int ds, const int fill, const uint32_t* srch, const uint16_t sfg,
+ const uint16_t sbg);
+void format_filename(char* result, ssize_t result_size, const char* format,
+ const ssize_t seg_size, const struct DrawState* state);
+void format_msgs(char* result, ssize_t result_size, const char* format,
+ const ssize_t seg_size, const struct DrawState* state);
+void free_bufferline(struct BufferLine* bl);
+void free_drawstate(struct DrawState* state);
+void init_bufferline(struct BufferLine* bl);
+void init_coord(struct Coord* c, const int col, const int row);
+void init_drawstate(struct DrawState* state, char* filename, char* prompt);
+ssize_t input_length(uint32_t* buffer, const ssize_t max_index);
+size_t real_msgid_to_display_msgid(const struct DrawState* state);
+void recalculate_size(struct DrawState* state);
+struct DrawState* set_nplurals(struct DrawState* state, struct PoEntry* entry);
void u32_draw_string(const int x, const int y, const uint16_t fg,
const uint16_t bg, const uint32_t* s, const int max_cols,
const int fill_cols, const int padding_left, const int padding_right,
const Alignment align, const int ds, const int fill,
const uint32_t* srch, const uint16_t sfg, const uint16_t sbg);
-const struct DrawState* draw_help(const struct DrawState* state);
-const struct DrawState* draw_editbox(const struct DrawState* state);
-const struct DrawState* draw_search(const struct DrawState* state);
-const struct DrawState* draw_status(const struct DrawState* state);
-const struct DrawState* draw_list_visible(const struct DrawState* state);
-const struct DrawState* draw(const struct DrawState* state);
diff --git a/po.c b/po.c
@@ -2,6 +2,7 @@
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
+#include <assert.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
@@ -9,18 +10,19 @@
#include <sys/stat.h>
#include <time.h>
-#define PO_C_GLOBALS
-
-#include "version.h"
-#include "util.h"
-#include "po.h"
#include "draw.h"
#include "config.h"
+#include "limit.h"
+#include "po.h"
+#include "util.h"
+#include "version.h"
extern const char* errors[];
extern const char* flag_strings[];
-int print_error(const int code, const char* msg, ...);
+// TODO: Check ifdef PLURAL_ ...
+
+extern int print_error(const int code, const char* msg, ...);
char*
format_flags(char* buffer, const size_t max, const struct PoEntry* entry,
@@ -119,9 +121,9 @@ set_msgid(struct PoEntry* entry, const char* msgid, int append)
{
uint32_t umsgid[MAXMSGLINE];
size_t msgid_size = 0;
- if (!entry)
- return NULL;
- msgid_size = u8_string_to_unicode(umsgid, msgid, MAXMSGLINE);
+ assert(entry != NULL);
+ if (msgid)
+ msgid_size = u8_string_to_unicode(umsgid, msgid, MAXMSGLINE);
return u32_set_msgid(entry, umsgid, msgid_size, append);
}
@@ -130,10 +132,10 @@ set_msgid_plural(struct PoEntry* entry, const char* msgid_plural, int append)
{
uint32_t umsgid_plural[MAXMSGLINE];
size_t msgid_plural_size = 0;
- if (!entry)
- return NULL;
- msgid_plural_size
- = u8_string_to_unicode(umsgid_plural, msgid_plural, MAXMSGLINE);
+ assert(entry != NULL);
+ if (msgid_plural)
+ msgid_plural_size = u8_string_to_unicode(umsgid_plural,
+ msgid_plural, MAXMSGLINE);
return u32_set_msgid_plural(entry, umsgid_plural, msgid_plural_size,
append);
}
@@ -142,9 +144,9 @@ struct PoEntry*
set_plural_forms(struct PoEntry* entry, const char* plural_forms, int append)
{
uint32_t uplural_forms[MAXMSGLINE];
- if (!entry)
- return NULL;
- u8_string_to_unicode(uplural_forms, plural_forms, MAXMSGLINE);
+ assert(entry != NULL);
+ if (plural_forms)
+ u8_string_to_unicode(uplural_forms, plural_forms, MAXMSGLINE);
return u32_set_plural_forms(entry, uplural_forms, append);
}
@@ -153,9 +155,9 @@ set_msgstr(struct PoEntry* entry, const char* msgstr, int msgstr_index,
int append, struct DrawState* state)
{
uint32_t umsgstr[MAXMSGLINE];
- if (!entry)
- return NULL;
- u8_string_to_unicode(umsgstr, msgstr, MAXMSGLINE);
+ assert(entry != NULL);
+ if (msgstr)
+ u8_string_to_unicode(umsgstr, msgstr, MAXMSGLINE);
return u32_set_msgstr(entry, umsgstr, msgstr_index, append, state);
}
@@ -164,6 +166,8 @@ u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid, size_t msgid_size,
int append)
{
size_t msgid_len = 0;
+ /* TODO: Unused? */
+ UNUSED(msgid_size);
if (!entry)
return NULL;
if (msgid)
@@ -195,6 +199,8 @@ u32_set_msgid_plural(struct PoEntry* entry, const uint32_t* msgid_plural,
size_t msgid_plural_size, int append)
{
size_t msgid_plural_len = 0;
+ /* TODO: Unused? */
+ UNUSED(msgid_plural_size);
if (!entry)
return NULL;
if (msgid_plural)
@@ -414,21 +420,20 @@ add_ref_comment(struct PoEntry* entry, const char* comment)
int
load_file(struct DrawState* dstate, long* lineno, long* col)
{
- struct PoEntry* newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry));
+ struct PoEntry* newchunk = NULL;
struct PoEntry* entry = NULL;
struct PoEntry* current = NULL;
- ParseState pstate = PS_NONE;
-#ifndef PLURAL_STRING
- int has_plural_msgid = 0;
-#endif
- FILE* input = NULL;
+ FILE* input = NULL;
char input_line[MAXBUFLINE];
+ ParseState pstate = PS_NONE;
+ int has_plural_msgid = 0;
+ ssize_t non_obsolete_sofar = 0;
/* lines to tolerate the lack of msgid */
- int msgid_counter = PO_DETECTION_LINES;
- int msgstr_index = -1;
- size_t non_obsolete_sofar = 0;
+ int msgid_counter = PO_DETECTION_LINES;
+ int msgstr_index = -1;
dstate->msgid_size = 0;
+ newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry));
if (!newchunk)
return LOAD_ERR_CANT_ALLOC;
@@ -453,10 +458,8 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
result = parse_po_line(input_line, dstate, &entry, &pstate, col,
&msgstr_index);
-#ifndef PLURAL_STRING
if (entry && entry->msgid_plural)
has_plural_msgid = 1;
-#endif
if (result == PARSE_ERR_NONE)
{
@@ -469,9 +472,10 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
return LOAD_ERR_NOT_PO_FILE;
}
else if (result == PARSE_ERR_SYNTAX)
- snprintf(dstate->error, MAXBUFLINE,
- "%s:%ld:%ld: Syntax error", dstate->filename,
- *lineno, *col);
+ {
+ dstate->error = ERR_SYNTAX;
+ // TODO: Handle this properly
+ }
else
{
free(dstate->entries);
@@ -494,11 +498,8 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
/* obsolete entry */
entry->obsolete = 1;
}
-#ifndef PLURAL_STRING
if (has_plural_msgid && !dstate->entries->plural_forms)
- STRLCPY(errors[ERR_NO_PLURAL_FORMS], dstate->error,
- MAXBUFLINE);
-#endif
+ dstate->error = ERR_NO_PLURAL_FORMS;
}
/* file has < PO_DETECTION_LINES */
@@ -528,36 +529,38 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
}
int
-save_file(const struct PoEntry* entries, const size_t real_msgid_count,
- const char* filename, const int nplurals)
+save_file(struct DrawState* state)
{
- const struct PoEntry* current = entries;
FILE* output = NULL;
-#ifdef CREATE_BACKUPS
- char* backup_filename = NULL;
-#endif
+ const struct PoEntry* current = NULL;
struct stat st;
- int result = SAVE_ERR_NONE;
- char* real_filename = NULL;
- size_t rfn_len = 0;
+ char* real_filename = NULL;
+ char* backup_filename = NULL;
+ ssize_t rfn_len = 0;
Flags flags;
+ int saved_errno;
+ int result = 0;
+ assert(state != NULL);
+ current = state->entries;
if (!(real_filename = calloc(MAXPATH, 1)))
return SAVE_ERR_CANT_ALLOC;
-#ifdef CREATE_BACKUPS
+ if (!create_backups)
+ goto save_skip_backup_copy;
+ if (!backup_fail && !state->backup_possible)
+ goto save_skip_backup_copy;
if (!(backup_filename = calloc(MAXPATH, 1)))
{
free(real_filename);
return SAVE_ERR_CANT_ALLOC;
}
-#endif
-#ifdef CREATE_BACKUPS
- if (lstat(filename, &st) != -1)
+ if (lstat(state->filename, &st) != -1)
{
if (S_ISLNK(st.st_mode))
{
- rfn_len = readlink(filename, real_filename, MAXPATH);
+ rfn_len = readlink(state->filename, real_filename,
+ MAXPATH);
if (rfn_len == -1)
{
result = SAVE_ERR_CANT_READLINK;
@@ -570,9 +573,9 @@ save_file(const struct PoEntry* entries, const size_t real_msgid_count,
goto save_loop_entries_done;
}
}
- STRLCPY(filename, backup_filename, MAXPATH);
+ STRLCPY(state->filename, backup_filename, MAXPATH);
STRLCAT(backup_suffix, backup_filename, MAXPATH);
- if (rename(rfn_len > 0 ? real_filename : filename,
+ if (rename(rfn_len > 0 ? real_filename : state->filename,
backup_filename)
== -1)
{
@@ -585,32 +588,32 @@ save_file(const struct PoEntry* entries, const size_t real_msgid_count,
result = SAVE_ERR_CANT_STAT_FILE;
goto save_loop_entries_done;
}
-#endif
- if (!(output = fopen(rfn_len > 0 ? real_filename : filename, "wt")))
- {
- result = SAVE_ERR_CANT_OPEN_FILE;
- goto save_loop_entries_done;
- }
-
-#ifdef CREATE_BACKUPS
if (rfn_len > 0)
{
- if (unlink(filename) == -1)
+ if (unlink(state->filename) == -1)
{
result = SAVE_ERR_CANT_UNLINK;
goto save_loop_entries_done;
}
- if (symlink(real_filename, filename) == -1)
+ if (symlink(real_filename, state->filename) == -1)
{
result = SAVE_ERR_CANT_SYMLINK;
goto save_loop_entries_done;
}
}
- chmod(rfn_len > 0 ? real_filename : filename, st.st_mode);
-#endif
+ chmod(rfn_len > 0 ? real_filename : state->filename, st.st_mode);
+
+save_skip_backup_copy:
+ if (!(output = fopen(rfn_len > 0 ? real_filename : state->filename,
+ "wt")))
+ {
+ state->error = ERR_ERRNO_SET;
+ result = 1;
+ goto save_file_cleanup;
+ }
save_loop_entries_start:
- if (current >= entries + real_msgid_count)
+ if (current >= state->entries + state->real_msgid_count)
goto save_loop_entries_done;
if (current->obsolete)
@@ -634,9 +637,9 @@ save_comments:
if (!u8_comment)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
- unicode_string_to_u8(u8_comment, size,
+ unicode_string_to_u8(u8_comment,
*(current->comments + c), len);
fprintf(output, "#%s%s\n", current->obsolete ? "~" : "",
u8_comment);
@@ -663,9 +666,9 @@ save_comments:
if (!u8_trans_comment)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
- unicode_string_to_u8(u8_trans_comment, size,
+ unicode_string_to_u8(u8_trans_comment,
*(current->trans_comments + tc), len);
fprintf(output, "#.%s%s\n",
u8_trans_comment[0] == 0 ? "" : " ",
@@ -717,7 +720,7 @@ save_flags:
save_obsolete_check:
if (current->obsolete)
{
- if (current < entries + real_msgid_count - 1)
+ if (current < state->entries + state->real_msgid_count - 1)
fprintf(output, "\n");
current++;
goto save_loop_entries_start;
@@ -725,8 +728,8 @@ save_obsolete_check:
if (current->msgid)
{
- size_t num_lines = u32_lines_in_string(current->msgid, 1,
- SAVE_WRAP_WIDTH);
+ size_t num_lines
+ = u32_lines_in_string(current->msgid, 1, wrap_width);
if ((num_lines > 1)
&& (u32_strlen(current->msgid)
@@ -744,7 +747,7 @@ save_obsolete_check:
const uint32_t* old_pumsgid = pumsgid;
len = u32_next_line(current->msgid, &pumsgid, 1,
- SAVE_WRAP_WIDTH);
+ wrap_width);
if (len == 0)
continue;
@@ -754,13 +757,12 @@ save_obsolete_check:
if (!u8_msgid)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgid, 0, size);
- unicode_string_to_u8(u8_msgid, size,
- old_pumsgid, len);
+ unicode_string_to_u8(u8_msgid, old_pumsgid, len);
fprintf(output, "\"%s\"\n", u8_msgid);
free(u8_msgid);
}
@@ -775,12 +777,12 @@ save_obsolete_check:
if (!u8_msgid)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgid, 0, size);
- unicode_string_to_u8(u8_msgid, size, current->msgid,
+ unicode_string_to_u8(u8_msgid, current->msgid,
current->msgid_size);
fprintf(output, "msgid \"%s\"\n", u8_msgid);
free(u8_msgid);
@@ -792,7 +794,7 @@ save_obsolete_check:
if (current->msgid_plural)
{
size_t num_lines = u32_lines_in_string(current->msgid_plural, 1,
- SAVE_WRAP_WIDTH);
+ wrap_width);
if ((num_lines > 1)
&& (u32_strlen(current->msgid_plural)
@@ -810,7 +812,7 @@ save_obsolete_check:
size_t size;
len = u32_next_line(current->msgid_plural,
- &pumsgid, 1, SAVE_WRAP_WIDTH);
+ &pumsgid, 1, wrap_width);
if (len == 0)
continue;
@@ -819,13 +821,12 @@ save_obsolete_check:
if (!u8_msgid)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgid, 0, size);
- unicode_string_to_u8(u8_msgid, size,
- old_pumsgid, len);
+ unicode_string_to_u8(u8_msgid, old_pumsgid, len);
fprintf(output, "\"%s\"\n", u8_msgid);
free(u8_msgid);
}
@@ -841,14 +842,14 @@ save_obsolete_check:
if (!u8_msgid)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgid, 9, size);
msgid_plural_len = u32_strlen(current->msgid_plural);
- unicode_string_to_u8(u8_msgid, size,
- current->msgid_plural, msgid_plural_len);
+ unicode_string_to_u8(u8_msgid, current->msgid_plural,
+ msgid_plural_len);
fprintf(output, "msgid_plural \"%s\"\n", u8_msgid);
free(u8_msgid);
}
@@ -856,20 +857,19 @@ save_obsolete_check:
if (current->msgstr_count == 1)
{
- size_t num_lines = u32_lines_in_string(*current->msgstr, 1,
- SAVE_WRAP_WIDTH);
- int first_entry = (current == entries) && !current->msgid;
+ ssize_t num_lines
+ = u32_lines_in_string(*current->msgstr, 1, wrap_width);
+ int first_entry
+ = (current == state->entries) && !current->msgid;
if ((num_lines > 1)
&& (first_entry
|| (u32_strlen(*current->msgstr)
- > SAVE_MULTILINE_TRESHOLD)))
+ > (size_t)SAVE_MULTILINE_TRESHOLD)))
{
const uint32_t* pumsgstr = *current->msgstr;
int seen_generator = 0;
-#ifdef PLURAL_STRING
- int seen_plural_forms = 0;
-#endif
+ int seen_plural_forms = 0;
char now[MAXDATEBUF];
time_t t = time(NULL);
struct tm* now_tm = localtime(&t);
@@ -886,9 +886,9 @@ save_obsolete_check:
len = u32_next_line(*current->msgstr, &pumsgstr,
1,
- ((first_entry && WRAP_FIRST_MSGSTR)
+ ((first_entry && wrap_first_msgstr)
|| !first_entry)
- ? SAVE_WRAP_WIDTH
+ ? wrap_width
: 0);
if (len == 0)
@@ -899,13 +899,13 @@ save_obsolete_check:
if (!u8_msgstr)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgstr, 0, size);
- unicode_string_to_u8(u8_msgstr, size,
- old_pumsgstr, len);
+ unicode_string_to_u8(u8_msgstr, old_pumsgstr,
+ len);
if (first_entry
&& starts_with(u8_msgstr,
"PO-Revision-Date: "))
@@ -921,33 +921,29 @@ save_obsolete_check:
"\"X-Generator: poe %s\\n\"\n",
VERSION);
}
-#ifdef PLURAL_STRING
- else if (first_entry
+ else if (first_entry && plural_string
&& starts_with(u8_msgstr,
"Plural-Forms: "))
{
seen_plural_forms = 1;
fprintf(output,
"\"Plural-Forms: %s\\n\"\n",
- PLURAL_STRING);
+ plural_string);
}
-#endif
else
fprintf(output, "\"%s\"\n", u8_msgstr);
free(u8_msgstr);
}
-#ifdef PLURAL_STRING
- if (first_entry && !seen_plural_forms)
+ if (first_entry && plural_string && !seen_plural_forms)
fprintf(output, "\"Plural-Forms: %s\\n\"\n",
- PLURAL_STRING);
-#endif
+ plural_string);
if (first_entry && !seen_generator)
fprintf(output, "\"X-Generator: poe %s\\n\"\n",
VERSION);
}
else if (current->msgid_plural
&& (!*current->msgstr || !**current->msgstr))
- for (int i = 0; i < nplurals; i++)
+ for (int i = 0; i < state->nplurals; i++)
fprintf(output, "msgstr[%d] \"\"\n", i);
else
{
@@ -956,17 +952,18 @@ save_obsolete_check:
size_t msgstr_len;
msgstr_len = u32_strlen(*current->msgstr);
- unicode_string_to_u8(u8_msgstr, size, *current->msgstr,
+ unicode_string_to_u8(u8_msgstr, *current->msgstr,
msgstr_len);
fprintf(output, "msgstr \"%s\"\n", u8_msgstr);
}
}
else if (current->msgstr_count > 0)
{
- for (size_t m = 0; m < current->msgstr_count; m++)
+ for (ssize_t m = 0; m < current->msgstr_count; m++)
{
- size_t num_lines = u32_lines_in_string(*current->msgstr,
- 1, SAVE_WRAP_WIDTH);
+ ssize_t num_lines
+ = u32_lines_in_string(*current->msgstr, 1,
+ wrap_width);
if ((num_lines > 1)
&& (u32_strlen(*(current->msgstr + m))
@@ -986,7 +983,7 @@ save_obsolete_check:
len = u32_next_line(*(current->msgstr
+ m),
- &pumsgstr, 1, SAVE_WRAP_WIDTH);
+ &pumsgstr, 1, wrap_width);
if (len == 0)
continue;
@@ -995,12 +992,12 @@ save_obsolete_check:
if (!u8_msgstr)
{
result = SAVE_ERR_CANT_ALLOC;
- goto save_loop_entries_done;
+ goto save_file_alloc_error;
}
memset(u8_msgstr, 0, size);
- unicode_string_to_u8(u8_msgstr, size,
+ unicode_string_to_u8(u8_msgstr,
old_pumsgstr, len);
fprintf(output, "\"%s\"\n", u8_msgstr);
free(u8_msgstr);
@@ -1012,7 +1009,7 @@ save_obsolete_check:
char u8_msgstr[size];
size_t msgstr_len;
msgstr_len = u32_strlen(*(current->msgstr + m));
- unicode_string_to_u8(u8_msgstr, size,
+ unicode_string_to_u8(u8_msgstr,
*(current->msgstr + m), msgstr_len);
fprintf(output, "msgstr[%ld] \"%s\"\n", m,
u8_msgstr);
@@ -1022,23 +1019,30 @@ save_obsolete_check:
else
{
if (current->msgid_plural)
- for (int i = 0; i < nplurals; i++)
+ for (int i = 0; i < state->nplurals; i++)
fprintf(output, "msgstr[%d] \"\"\n", i);
else
fprintf(output, "msgstr \"\"\n");
}
- if (current < entries + real_msgid_count - 1)
+ if (current < state->entries + state->real_msgid_count - 1)
fprintf(output, "\n");
current++;
goto save_loop_entries_start;
+save_file_alloc_error:
+ if (output)
+ fclose(output);
+ return 1;
+
save_loop_entries_done:
- fclose(output);
-#ifdef CREATE_BACKUPS
+save_file_cleanup:
+ saved_errno = errno;
+ if (output)
+ fclose(output);
free(backup_filename);
-#endif
free(real_filename);
-
+ state->action = ACT_IDLE;
+ errno = saved_errno;
return result;
}
@@ -1422,7 +1426,7 @@ update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning)
return;
if (u32_count_strings(msgid, (const uint32_t*)L"\\n")
!= u32_count_strings(msgstr, (const uint32_t*)L"\\n")
- || (WARN_COUNT_DOTS
+ || (warn_count_dots
&& u32_count_chars(msgid, '.')
!= u32_count_chars(msgstr, '.')))
*warning = 1;
diff --git a/po.h b/po.h
@@ -5,8 +5,8 @@
#include <stdint.h>
#include <stdlib.h>
-#define MSGSTR_MAX 4096
-#define ALLOC_DELTA 10
+#define MSGSTR_MAX 4096
+// #define ALLOC_DELTA 10
#define PO_DETECTION_LINES 24
/* If msgid/msgstr is longer, it will be saved as multiple lines */
#define SAVE_MULTILINE_TRESHOLD 70
@@ -97,36 +97,40 @@ struct PoEntry {
size_t ref_comment_lines;
};
-char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry,
- const int draw_box);
+char* format_flags(char* buffer, const size_t max,
+ const struct PoEntry* entry, const int draw_box);
void init_po_entry(struct PoEntry* entry);
void free_po_entry(struct PoEntry* entry);
struct PoEntry* set_flags(struct PoEntry* entry, const Flags flags);
-struct PoEntry* set_msgid(struct PoEntry* entry, const char* msgid, int append);
+struct PoEntry* set_msgid(struct PoEntry* entry, const char* msgid,
+ int append);
struct PoEntry* set_msgid_plural(struct PoEntry* entry,
const char* msgid_plural, int append);
struct PoEntry* set_plural_forms(struct PoEntry* entry,
const char* plural_forms, int append);
struct PoEntry* set_msgstr(struct PoEntry* entry, const char* msgstr,
int msgstr_index, int append, struct DrawState* state);
-struct PoEntry* u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid,
- size_t msgid_size, int append);
+struct PoEntry* u32_set_msgid(struct PoEntry* entry,
+ const uint32_t* msgid, size_t msgid_size, int append);
struct PoEntry* u32_set_msgid_plural(struct PoEntry* entry,
const uint32_t* msgid_plural, size_t msgid_plural_size, int append);
struct PoEntry* u32_set_plural_forms(struct PoEntry* entry,
const uint32_t* plural_forms, int append);
-struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr,
- int msgstr_index, int append, struct DrawState* state);
-struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment);
+struct PoEntry* u32_set_msgstr(struct PoEntry* entry,
+ const uint32_t* msgstr, int msgstr_index, int append,
+ struct DrawState* state);
+struct PoEntry* u32_add_comment(struct PoEntry* entry,
+ const uint32_t* comment);
struct PoEntry* u32_add_trans_comment(struct PoEntry* entry,
const uint32_t* trans_comment);
-struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment);
+struct PoEntry* add_ref_comment(struct PoEntry* entry,
+ const char* comment);
int load_file(struct DrawState* state, long* lineno, long* col);
-int save_file(const struct PoEntry* entries, const size_t real_msgid_count,
- const char* filename, const int nplurals);
+int save_file(struct DrawState* state);
int parse_po_line(const char* line, struct DrawState* dstate,
struct PoEntry** current_entry, ParseState* pstate, long* col,
int* msgstr_index);
-void update_statistics(const struct PoEntry* entries, struct DrawState* state,
- struct PoEntry* entry);
-void update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning);
+void update_statistics(const struct PoEntry* entries,
+ struct DrawState* state, struct PoEntry* entry);
+void update_warning(const uint32_t* msgid, const uint32_t* msgstr,
+ int* warning);
diff --git a/poe.c b/poe.c
@@ -2,8 +2,10 @@
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
+#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <locale.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -26,38 +28,23 @@
" which is released under the terms of Expat license, see the file" \
" LICENSE.termbox.\n")
#define POE_C_GLOBALS
+#define TB_IMPL
-#include "version.h"
-#include "util.h"
-#include "po.h"
#include "draw.h"
+#include "poe.h"
#include "config.h"
+#include "limit.h"
+#include "po.h"
+#include "util.h"
+#include "version.h"
-extern const char* backup_suffix;
extern const char* errors[];
+extern const char* actions[];
extern const char* prompt_dirty;
extern const char* prompt_cancel_dirty;
extern const char* prompt_overwrite;
-void cancel_callback(struct DrawState* state, struct tb_event* ev);
-char* dirname(char* path);
-void goto_msgid(struct DrawState* state, const size_t msgid_number);
-void handle_key_event(struct tb_event* ev, struct DrawState* state);
-int insert_char(struct DrawState* state, uint32_t ch);
-void insert_search_char(struct DrawState* state, uint32_t ch);
-struct DrawState* join_lines(struct DrawState* state, size_t first_line,
- int forward);
-struct BufferLine* load_info(struct DrawState* state);
-struct BufferLine* load_msgstr(struct DrawState* state);
-size_t next_msgid(struct DrawState* state, const int delta);
-void overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev);
-void overwrite_paste_callback(struct DrawState* state, struct tb_event* ev);
-size_t prev_msgid(struct DrawState* state, const int delta);
-int print_error(const int code, const char* msg, ...);
-void quit_callback(struct DrawState* state, struct tb_event* ev);
-size_t real_msgid_to_display_msgid(const struct DrawState* state);
-
-void
+static int
cancel_callback(struct DrawState* state, struct tb_event* ev)
{
if (ev->ch == 'y')
@@ -65,41 +52,147 @@ cancel_callback(struct DrawState* state, struct tb_event* ev)
else
cancel_msgstr(state);
state->prompt_callback = NULL;
+ return 0;
}
-char*
-dirname(char* path)
+#define MAXCOPYBUF 8192
+
+static int
+cp(const char* from, const char* to)
{
- static char* result = NULL;
- static const char* dot = ".";
- char* p = NULL;
- char* slash = NULL;
+ struct stat st;
+ struct timespec times[2];
+ char buf[MAXCOPYBUF];
+ int fromfd, tofd;
+ int nread = 0;
+ int nwritten = 0;
+ int result;
+ int saved_errno;
- if (!path || !*path)
- return NULL;
+ assert((from != NULL) && (to != NULL));
- free(result);
- result = malloc(MAXPATH);
- memset(result, 0, MAXPATH);
+ tofd = 0;
+ fromfd = open(from, O_RDONLY);
+ if (fromfd < 0)
+ return 1;
+ if (fstat(fromfd, &st) < 0)
+ {
+ result = 1;
+ goto cp_cleanup;
+ }
- STRLCPY(path, result, MAXPATH);
- p = result + strlen(result) - 1;
- if (*p == '/')
- *p = 0;
+ tofd = open(to, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
+ if (tofd < 0)
+ return 1;
+ if (fchmod(tofd, st.st_mode) < 0)
+ {
+ result = 1;
+ goto cp_cleanup;
+ }
+ times[0] = st.st_atim;
+ times[1] = st.st_mtim;
- slash = strrchr(result, '/');
- if (!slash)
- return (char*)dot;
+ result = 0;
+ while ((nread = read(fromfd, buf, MAXCOPYBUF)) > 0)
+ {
+ nwritten = write(tofd, buf, nread);
+ if (nwritten < nread)
+ {
+ result = 1;
+ goto cp_cleanup;
+ }
+ }
- *slash = 0;
+ if (nread < 0)
+ {
+ result = 1;
+ goto cp_cleanup;
+ }
+ errno = 0;
+ if (futimens(tofd, times) < 0)
+ result = 1;
+
+cp_cleanup:
+ saved_errno = errno;
+ if (tofd)
+ close(tofd);
+ close(fromfd);
+ errno = saved_errno;
return result;
}
-void
-goto_msgid(struct DrawState* state, const size_t msgid_number)
+/*static int
+create_file(struct DrawState* state)
{
- size_t last_non_obsolete
+ 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)
+{
+ char* p = NULL;
+
+ assert((dir != NULL) && (path != NULL) && (dir_size > 0));
+ if (!*path)
+ return NULL;
+
+ MEMCCPY(dir, path, dir_size, p);
+
+ if (p > dir + 2 && *(p - 2) == '/')
+ *p = 0;
+ if (p > dir + 1)
+ p--;
+
+ while (p != dir && *p != '/')
+ {
+ p--;
+ if (p != dir && *p == '/')
+ {
+ *p = 0;
+ return dir;
+ }
+ else if (p == dir && *p == '/')
+ return dir;
+ }
+ dir[0] = '.';
+ dir[1] = 0;
+ return dir;
+}
+
+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)
@@ -117,20 +210,20 @@ goto_msgid(struct DrawState* state, const size_t msgid_number)
state->first_shown_msgid = (last_non_obsolete > state->maxy - 2
? last_non_obsolete - (state->maxy - 2)
: 1);
- else
- ;
}
-void
+static void
handle_key_event(struct tb_event* ev, struct DrawState* state)
{
const struct Key* current = NULL;
- *state->error = 0;
+ assert((state != NULL) && (ev != NULL));
+ state->error = ERR_NONE;
+ errno = 0;
if (state->in_prompt)
{
- if (ev->ch == 'y' || ev->ch == 'n')
+ if (ev->ch && strchr("YyNn", ev->ch))
{
if (state->prompt_callback)
state->prompt_callback(state, ev);
@@ -142,37 +235,45 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
return;
}
- /* Remember to also edit the following condition if you reassign motion
- * keys */
+ /* 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 (int key = 0; key < LEN(top_level_keys); key++)
+ for (size_t key = 0; key < LEN(top_level_keys); key++)
{
current = top_level_keys + key;
- if (((current->ev.key == ev->key) && !ev->ch)
+ 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))
+ if (current->callback)
+ {
+ current->callback(state);
return;
+ }
}
- if (state->show_help)
- {
- STRLCPY(errors[ERR_DLG_OPEN_QUIT], state->error, MAXBUFLINE);
- return;
- }
- else if (state->show_edit)
+ if (state->show_edit)
{
- for (int key = 0; key < LEN(edit_keys); key++)
+ for (size_t key = 0; key < LEN(edit_keys); key++)
{
current = edit_keys + key;
- if (((current->ev.key == ev->key) && !ev->ch)
+ 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))
+ if (current->callback)
+ {
+ current->callback(state);
return;
+ }
}
if (!state->edit_info_focused && ev->ch)
{
@@ -180,17 +281,21 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
return;
}
}
- else if (state->show_search)
+
+ if (state->show_search)
{
- for (int key = 0; key < LEN(search_keys); key++)
+ for (size_t key = 0; key < LEN(search_keys); key++)
{
current = search_keys + key;
- if (((current->ev.key == ev->key) && !ev->ch)
+ 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))
+ if (current->callback)
+ {
+ current->callback(state);
return;
+ }
}
if (ev->ch)
{
@@ -198,79 +303,141 @@ handle_key_event(struct tb_event* ev, struct DrawState* state)
return;
}
}
- else
+
+ if (state->show_help)
{
- for (int key = 0; key < LEN(main_keys); key++)
- {
- current = main_keys + key;
- if (((current->ev.key == ev->key) && !ev->ch)
- || (current->ev.ch
- && (current->ev.ch == ev->ch)))
- if (current->callback)
- {
- if (!current->callback(state))
- return;
- else
- break;
- }
- }
+ state->error = ERR_DLG_OPEN;
+ return;
+ }
+
+ for (size_t key = 0; key < LEN(main_keys); key++)
+ {
+ 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;
+ }
}
if (state->edit_info_focused)
- STRLCPY(errors[ERR_DLG_FOCUS_EDIT], state->error, MAXBUFLINE);
+ state->error = ERR_DLG_FOCUS_EDIT;
else if (state->show_edit || state->show_search)
- STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, MAXBUFLINE);
+ state->error = ERR_UNKNOWN_KEY;
else
- STRLCPY(errors[ERR_UNKNOWN_KEY_HELP], state->error, MAXBUFLINE);
+ state->error = ERR_UNKNOWN_KEY_HELP;
}
-int
+static int
insert_char(struct DrawState* state, uint32_t ch)
{
+ struct BufferLine* buffer_line;
+ ssize_t len;
+ assert(state != NULL);
if (state->edit_info_focused)
return 1;
- struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
- size_t len = buffer_line->length;
- if (state->input_column < MAXBUFLINE - 1)
+ buffer_line = &state->input_buffer[state->coord.row];
+ len = buffer_line->length;
+ if (state->coord.col < MAXBUFLINE - 1)
{
- size_t i = len + 1;
- int add_null = buffer_line->text[state->input_column] == 0;
- while (i < MAXBUFLINE - 2 && i > state->input_column)
+ 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->input_column++] = ch;
+ buffer_line->text[state->coord.col++] = ch;
buffer_line->length++;
- if (state->input_column == MAXBUFLINE)
- state->input_column = MAXBUFLINE - 1;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- if (state->input_display_column - state->input_first_shown_column
+ 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_column
+ state->input_first_shown.col
= state->input_display_column
- state->edit_width + 4;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
+ state->input_display_column = display_length(
+ state->input_buffer[state->coord.row].text,
+ state->coord.col);
}
if (add_null)
- buffer_line->text[state->input_column] = 0;
+ buffer_line->text[state->coord.col] = 0;
+ }
+ state->dirty = 1;
+ return 0;
+}
+
+static int
+insert_line(struct DrawState* state)
+{
+ struct BufferLine* newbuf = NULL;
+
+ if (state->edit_info_focused)
+ return 1;
+
+ 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;
+ }
+
+ 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);
+
+ 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;
+
return 0;
}
-void
+static void
insert_search_char(struct DrawState* state, uint32_t ch)
{
- size_t len = u32_strlen(state->search);
+ ssize_t len = u32_strlen(state->search);
if (state->search_column < MAXSEARCH - 1)
{
- size_t i = len + 1;
+ ssize_t i = len + 1;
int add_null = state->search[state->search_column] == 0;
while (i < MAXSEARCH - 2 && i > state->search_column)
{
@@ -280,8 +447,8 @@ insert_search_char(struct DrawState* state, uint32_t ch)
state->search[state->search_column++] = ch;
if (state->search_column == MAXSEARCH)
state->search_column = MAXSEARCH - 1;
- state->search_display_column = display_length(state,
- state->search, state->search_column);
+ 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)
@@ -289,8 +456,9 @@ insert_search_char(struct DrawState* state, uint32_t ch)
state->search_first_shown_column
= state->search_display_column
- state->search_width + 4;
- state->search_display_column = display_length(state,
- state->search, state->search_column);
+ state->search_display_column
+ = display_length(state->search,
+ state->search_column);
}
if (add_null)
state->search[state->search_column] = 0;
@@ -299,24 +467,24 @@ insert_search_char(struct DrawState* state, uint32_t ch)
}
}
-struct DrawState*
+static struct DrawState*
join_lines(struct DrawState* state, size_t first_line, int forward)
{
- state->input_column = state->input_buffer[first_line].length;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
+ 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->input_row--;
- if (state->input_row < 0)
- state->input_row = 0;
+ state->coord.row--;
+ if (state->coord.row < 0)
+ state->coord.row = 0;
}
- for (size_t i = first_line + 1; i < state->input_rows_count - 1; i++)
+ for (ssize_t i = first_line + 1; i < state->input_rows_count - 1; i++)
{
u32_strncpy(state->input_buffer[i].text,
state->input_buffer[i + 1].text, MAXBUFLINE);
@@ -335,19 +503,21 @@ join_lines(struct DrawState* state, size_t first_line, int forward)
return state;
}
-struct BufferLine*
+static struct BufferLine*
load_info(struct DrawState* state)
{
- struct PoEntry* first = &state->entries[0];
- struct PoEntry* current = &state->entries[state->msgid_number - 1];
- size_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
- size_t msgid_plural_num_lines
- = u32_lines_in_string(current->msgid_plural, 0, 0);
- size_t num_lines = 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;
- for (size_t i = 0; i < state->info_rows_count; i++)
+ for (ssize_t i = 0; i < state->info_rows_count; i++)
free_bufferline(state->info_buffer + i);
free(state->info_buffer);
@@ -372,7 +542,7 @@ load_info(struct DrawState* state)
return NULL;
pnewbuf = newbuf;
- for (size_t i = 0; i < num_lines; i++)
+ for (ssize_t i = 0; i < num_lines; i++)
{
init_bufferline(pnewbuf);
uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
@@ -384,9 +554,9 @@ load_info(struct DrawState* state)
state->info_rows_count = num_lines;
- pnewbuf = newbuf;
- size_t newlen = 0;
- const uint32_t* pumsgid = NULL;
+ pnewbuf = newbuf;
+ newlen = 0;
+ pumsgid = NULL;
if (current->msgid)
{
pumsgid = current->msgid;
@@ -394,8 +564,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
pnewbuf++;
while (*pumsgid)
{
@@ -419,8 +589,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
pnewbuf++;
pumsgid = current->msgid_plural;
while (*pumsgid)
@@ -445,8 +615,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ 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);
@@ -462,8 +632,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ 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++)
{
@@ -484,8 +654,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ pnewbuf->fg = colors[C_EDIT_MSGID_HEADING].fg;
+ pnewbuf->bg = colors[C_EDIT_MSGID_HEADING].bg;
pnewbuf++;
for (size_t i = 0; i < current->trans_comment_lines; i++)
{
@@ -506,8 +676,8 @@ load_info(struct DrawState* state)
pnewbuf->length = newlen;
if (newlen > state->info_rows_maxlen)
state->info_rows_maxlen = newlen;
- pnewbuf->fg = EDIT_MSGID_HEADING_FG;
- pnewbuf->bg = EDIT_MSGID_HEADING_BG;
+ 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++)
{
@@ -527,18 +697,25 @@ load_info(struct DrawState* state)
return state->info_buffer;
}
-struct BufferLine*
+static struct BufferLine*
load_msgstr(struct DrawState* state)
{
- int msgstr_index = state->msgstr_index;
- struct PoEntry* current = &state->entries[state->msgid_number - 1];
- size_t num_lines = current->msgstr_count == 0
- ? 1
- : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0);
- struct BufferLine* newbuf = NULL;
- struct BufferLine* pnewbuf = NULL;
-
- for (size_t i = 0; i < state->input_rows_count; i++)
+ 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);
@@ -591,7 +768,7 @@ load_msgstr(struct DrawState* state)
return state->input_buffer;
}
-size_t
+static size_t
next_msgid(struct DrawState* state, const int delta)
{
size_t ret = state->msgid_number;
@@ -609,27 +786,29 @@ next_msgid(struct DrawState* state, const int delta)
return ret == last_non_obsolete + 1 ? last_non_obsolete : ret;
}
-void
+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;
}
-void
+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;
}
-size_t
+static size_t
prev_msgid(struct DrawState* state, const int delta)
{
- size_t ret = state->msgid_number;
- size_t start = ret;
+ ssize_t ret = state->msgid_number;
+ ssize_t start = ret;
if (start < delta)
return state->real_msgid_count ? 1 : 0;
@@ -652,44 +831,25 @@ print_error(const int code, const char* msg, ...)
va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
- fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf);
+ fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf);
return code;
}
-void
+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;
}
-size_t
-real_msgid_to_display_msgid(const struct DrawState* state)
-{
- struct PoEntry* current = state->entries;
- size_t ret = 1;
-
- if (!state->msgid_count)
- return 0;
-
- while (current != state->entries + state->msgid_number - 1)
- {
- if (current != state->entries + state->msgid_number - 1
- && !current->obsolete)
- ret++;
- current++;
- }
-
- return ret;
-}
-
-int
+static int
cancel_close(struct DrawState* state)
{
- *state->error = 0;
- if (state->in_prompt)
+ assert(state != NULL);
+ if (!state->error && state->in_prompt)
{
state->in_prompt = 0;
*state->prompt = 0;
@@ -701,12 +861,12 @@ cancel_close(struct DrawState* state)
else if (state->show_search)
cancel_search(state);
else
- STRLCPY(errors[ERR_EXIT_KEY], state->error, MAXBUFLINE);
+ state->error = ERR_EXIT_KEY;
return 0;
}
-int
+static int
cancel_msgstr(struct DrawState* state)
{
if (!state->in_prompt && state->dirty)
@@ -720,13 +880,13 @@ cancel_msgstr(struct DrawState* state)
state->show_edit = 0;
state->edit_info_focused = 0;
- for (size_t i = 0; i < state->input_rows_count; i++)
+ 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 (size_t i = 0; i < state->info_rows_count; i++)
+ 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;
@@ -748,7 +908,7 @@ cancel_msgstr(struct DrawState* state)
return 0;
}
-int
+static int
cancel_search(struct DrawState* state)
{
state->show_search = 0;
@@ -757,9 +917,17 @@ cancel_search(struct DrawState* state)
return 0;
}
-int
+static int
copy_msgid_to_input(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;
@@ -773,34 +941,35 @@ copy_msgid_to_input(struct DrawState* state)
if (state->msgid_number == 1)
{
- STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
+ state->error = ERR_ILLEGAL_ON_FIRST;
return 0;
}
- struct PoEntry* current = &state->entries[state->msgid_number - 1];
- size_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0);
- size_t lines_to_init = state->input_rows_count < msgid_num_lines
- ? msgid_num_lines - state->input_rows_count
- : 0;
- size_t lines_to_free = state->input_rows_count > msgid_num_lines
- ? state->input_rows_count - msgid_num_lines
- : 0;
- for (size_t i = 0; i < lines_to_free; i++)
+ 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);
- struct BufferLine* newbuf = realloc(state->input_buffer,
+ newbuf = realloc(state->input_buffer,
msgid_num_lines * sizeof(struct BufferLine));
- const uint32_t* pmsgid = current->msgid;
+ pmsgid = current->msgid;
if (!newbuf)
return 0;
- struct BufferLine* pbuffer = NULL;
- for (size_t i = 0; i < lines_to_init; i++)
+ 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);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
if (!newtext)
return 0;
pbuffer->text = newtext;
@@ -826,14 +995,14 @@ copy_msgid_to_input(struct DrawState* state)
return 0;
}
-int
+static int
edit_page_down(struct DrawState* state)
{
if (state->edit_info_focused)
{
- if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2
+ if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2
< state->info_rows_count - 1)
- state->info_first_shown_row += (EDIT_HEIGHT - 3) / 2;
+ state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2;
return 0;
}
@@ -846,13 +1015,13 @@ edit_page_down(struct DrawState* state)
return show_edit(state);
}
-int
+static int
edit_page_up(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;
+ 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;
@@ -867,43 +1036,46 @@ edit_page_up(struct DrawState* state)
return show_edit(state);
}
-int
+static int
erase_backwards(struct DrawState* state)
{
- struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
+ struct BufferLine* buffer_line;
+ assert(state != NULL);
+ buffer_line = &state->input_buffer[state->coord.row];
if (state->edit_info_focused)
return 1;
- if (state->input_column > 0)
+ if (state->coord.col > 0)
{
- size_t i = state->input_column;
+ 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->input_column--;
- if (state->input_first_shown_column > 0)
+ state->coord.col--;
+ if (state->input_first_shown.col > 0)
{
const struct BufferLine* current_line
- = &state->input_buffer[state->input_row];
- if (display_length(state, current_line->text,
+ = &state->input_buffer[state->coord.row];
+ if (display_length(current_line->text,
current_line->length)
>= state->edit_width - 4)
- state->input_first_shown_column--;
+ state->input_first_shown.col--;
}
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- if (state->input_display_column
- < state->input_first_shown_column)
- state->input_first_shown_column
+ 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;
}
- else if (state->input_row > 0)
+ else if (state->coord.row > 0)
{
move_up(state);
move_end(state);
@@ -912,16 +1084,16 @@ erase_backwards(struct DrawState* state)
return 0;
}
-int
+static int
erase_forward(struct DrawState* state)
{
- struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
+ struct BufferLine* buffer_line = &state->input_buffer[state->coord.row];
if (state->edit_info_focused)
return 1;
- if (buffer_line->text[state->input_column])
+ if (buffer_line->text[state->coord.col])
{
- size_t i = state->input_column;
+ size_t i = state->coord.col;
while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
{
buffer_line->text[i] = buffer_line->text[i + 1];
@@ -931,28 +1103,31 @@ erase_forward(struct DrawState* state)
buffer_line->length--;
state->dirty = 1;
}
- else if (state->input_row < state->input_rows_count - 1)
+ else if (state->coord.row < state->input_rows_count - 1)
{
- if (!join_lines(state, state->input_row, 1))
+ if (!join_lines(state, state->coord.row, 1))
{
- STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ state->error = ERR_CANT_ALLOC;
state->running = 0;
}
}
return 0;
}
-int
+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;
- int i = state->input_column;
- size_t len = state->input_buffer[state->input_row].length;
-
- uint32_t* pline = state->input_buffer[state->input_row].text;
+ 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--;
@@ -962,25 +1137,25 @@ erase_prev_word(struct DrawState* state)
&& !u32_is_word_boundary(pline[i - 1], 0))
i--;
- int d = state->input_column - i;
+ d = state->coord.col - i;
- for (int j = i; j < len - d; j++)
+ for (ssize_t j = i; j < len - d; j++)
pline[j] = pline[j + d];
len -= d;
- state->input_buffer[state->input_row].length = len;
+ state->input_buffer[state->coord.row].length = len;
pline[len] = 0;
- state->input_column = i;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- if (state->input_display_column < state->input_first_shown_column)
- state->input_first_shown_column = state->input_display_column;
+ 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;
}
-int
+static int
erase_search_backwards(struct DrawState* state)
{
if (state->search_column > 0)
@@ -1002,15 +1177,15 @@ erase_search_backwards(struct DrawState* state)
else
state->search_first_shown_column = 0;
}
- state->search_display_column = display_length(state,
- state->search, state->search_column);
+ state->search_display_column
+ = display_length(state->search, state->search_column);
return next_match(state);
}
return 0;
}
-int
+static int
erase_search_forward(struct DrawState* state)
{
if (state->search[state->search_column])
@@ -1028,14 +1203,16 @@ erase_search_forward(struct DrawState* state)
return 0;
}
-int
+static int
erase_search_prev_word(struct DrawState* state)
{
- int i = state->search_column;
- size_t len = u32_strlen(state->search);
-
- uint32_t* pline = state->search;
+ 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))
@@ -1053,7 +1230,7 @@ erase_search_prev_word(struct DrawState* state)
pline[len] = 0;
state->search_column = i;
state->search_display_column
- = display_length(state, state->search, state->search_column);
+ = display_length(state->search, state->search_column);
if (state->search_column < state->search_first_shown_column)
{
@@ -1064,12 +1241,12 @@ erase_search_prev_word(struct DrawState* state)
state->search_first_shown_column = 0;
}
state->search_display_column
- = display_length(state, state->search, state->search_column);
+ = display_length(state->search, state->search_column);
return next_match(state);
}
-int
+static int
erase_search_to_end(struct DrawState* state)
{
state->search[state->search_column] = 0;
@@ -1077,7 +1254,7 @@ erase_search_to_end(struct DrawState* state)
return next_match(state);
}
-int
+static int
erase_search_to_start(struct DrawState* state)
{
size_t i = state->search_column;
@@ -1092,7 +1269,7 @@ erase_search_to_start(struct DrawState* state)
return next_match(state);
}
-int
+static int
erase_to_end(struct DrawState* state)
{
struct BufferLine* buffer_line = NULL;
@@ -1100,43 +1277,46 @@ erase_to_end(struct DrawState* state)
if (state->edit_info_focused)
return 1;
- buffer_line = &state->input_buffer[state->input_row];
- buffer_line->text[state->input_column] = 0;
- buffer_line->length = state->input_column;
+ 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;
}
-int
+static int
erase_to_start(struct DrawState* state)
{
- size_t i = state->input_column;
- struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
- size_t len = buffer_line->length;
+ 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
{
- buffer_line->text[i - state->input_column]
- = buffer_line->text[i];
+ buffer_line->text[i - state->coord.col] = buffer_line->text[i];
i++;
} while (buffer_line->text[i]);
- buffer_line->text[len - state->input_column] = 0;
- buffer_line->length = len - state->input_column;
+ buffer_line->text[len - state->coord.col] = 0;
+ buffer_line->length = len - state->coord.col;
move_start(state);
state->dirty = 1;
return 0;
}
-int
+static int
exit_program(struct DrawState* state)
{
if (state->show_edit)
{
- STRLCPY(errors[ERR_DLG_OPEN], state->error, MAXBUFLINE);
+ state->error = ERR_DLG_OPEN;
return 0;
}
if (state->dirty)
@@ -1147,11 +1327,11 @@ exit_program(struct DrawState* state)
}
else
state->running = 0;
- *state->error = 0;
+ //*state->error = 0;
return 0;
}
-int
+static int
hide_search(struct DrawState* state)
{
state->show_search = 0;
@@ -1159,191 +1339,139 @@ hide_search(struct DrawState* state)
return 0;
}
-int
-insert_line(struct DrawState* state)
-{
- struct BufferLine* newbuf = NULL;
-
- if (state->edit_info_focused)
- return 1;
-
- state->input_rows_count++;
- newbuf = realloc(state->input_buffer,
- state->input_rows_count * sizeof(struct BufferLine));
- if (!newbuf)
- {
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
- state->running = 0;
- return 0;
- }
-
- state->input_buffer = newbuf;
- init_bufferline(&state->input_buffer[state->input_rows_count - 1]);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
- if (!newtext)
- {
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
- state->running = 0;
- return 0;
- }
- state->input_buffer[state->input_rows_count - 1].text = newtext;
- for (size_t i = state->input_rows_count - 1; i > state->input_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->input_row + 1].text,
- state->input_buffer[state->input_row].text + state->input_column,
- MAXBUFLINE);
- state->input_buffer[state->input_row + 1].length
- = u32_strlen(state->input_buffer[state->input_row + 1].text);
- state->input_buffer[state->input_row].text[state->input_column] = 0;
- state->input_buffer[state->input_row].length
- = u32_strlen(state->input_buffer[state->input_row].text);
-
- state->input_row++;
- if (state->input_row
- > state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2)
- state->input_first_shown_row
- = state->input_row - (EDIT_HEIGHT - 3) / 2;
- move_start(state);
- state->dirty = 1;
-
- return 0;
-}
-
-int
+static int
insert_search_space(struct DrawState* state)
{
insert_search_char(state, (uint32_t)L' ');
return 0;
}
-int
+static int
insert_search_tab(struct DrawState* state)
{
insert_search_char(state, (uint32_t)L'\t');
return 0;
}
-int
+static int
insert_space(struct DrawState* state)
{
insert_char(state, (uint32_t)L' ');
return 0;
}
-int
+static int
insert_tab(struct DrawState* state)
{
insert_char(state, (uint32_t)L'\t');
return 0;
}
-int
-move_end(struct DrawState* state)
+static int
+move_down(struct DrawState* state)
{
+ ssize_t dlen;
+
if (state->edit_info_focused)
{
- if (state->info_rows_count > (EDIT_HEIGHT - 3) / 2)
- state->info_first_shown_row = state->info_rows_count
- - (EDIT_HEIGHT - 3) / 2;
- state->info_first_shown_column = 0;
+ if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2
+ < state->info_rows_count)
+ state->info_first_shown.row++;
return 0;
}
- size_t len = state->input_buffer[state->input_row].length;
- state->input_column = len;
- size_t dlen = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- state->input_display_column = dlen;
- if (dlen > state->edit_width - 4)
- state->input_first_shown_column = dlen - state->edit_width + 4;
+ if (state->coord.row < state->input_rows_count - 1)
+ state->coord.row++;
+ if (state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2
+ < state->coord.row)
+ state->input_first_shown.row
+ = state->coord.row - (EDIT_HEIGHT - 3) / 2;
+ if (state->saved_column == -1)
+ state->saved_column = state->input_display_column;
+ dlen = display_length(state->input_buffer[state->coord.row].text,
+ state->input_buffer[state->coord.row].length);
+ if (dlen < state->saved_column)
+ state->input_display_column = dlen;
else
- state->input_first_shown_column = 0;
+ state->input_display_column = state->saved_column;
+ state->coord.col
+ = input_length(state->input_buffer[state->coord.row].text,
+ state->input_display_column);
+ if (state->input_first_shown.col > state->input_display_column)
+ state->input_first_shown.col = state->input_display_column;
+ if (state->input_first_shown.col + state->edit_width - 4
+ < state->input_display_column)
+ 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);
return 0;
}
-int
-move_down(struct DrawState* state)
+static int
+move_end(struct DrawState* state)
{
+ ssize_t len;
+ ssize_t dlen;
+
if (state->edit_info_focused)
{
- if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2
- < state->info_rows_count)
- state->info_first_shown_row++;
+ if (state->info_rows_count > (EDIT_HEIGHT - 3) / 2)
+ state->info_first_shown.row = state->info_rows_count
+ - (EDIT_HEIGHT - 3) / 2;
+ state->info_first_shown.col = 0;
return 0;
}
- if (state->input_row < state->input_rows_count - 1)
- state->input_row++;
- if (state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2
- < state->input_row)
- state->input_first_shown_row
- = state->input_row - (EDIT_HEIGHT - 3) / 2;
- if (state->saved_column == -1)
- state->saved_column = state->input_display_column;
- size_t dlen = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_buffer[state->input_row].length);
- if (dlen < state->saved_column)
- state->input_display_column = dlen;
+ len = state->input_buffer[state->coord.row].length;
+ state->coord.col = len;
+ dlen = display_length(state->input_buffer[state->coord.row].text,
+ state->coord.col);
+ state->input_display_column = dlen;
+ if (dlen > state->edit_width - 4)
+ state->input_first_shown.col = dlen - state->edit_width + 4;
else
- state->input_display_column = state->saved_column;
- state->input_column = input_length(state,
- state->input_buffer[state->input_row].text,
- state->input_display_column);
- if (state->input_first_shown_column > state->input_display_column)
- state->input_first_shown_column = state->input_display_column;
- if (state->input_first_shown_column + state->edit_width - 4
- < state->input_display_column)
- state->input_first_shown_column
- = state->input_display_column - state->edit_width + 4;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
+ state->input_first_shown.col = 0;
return 0;
}
-int
+static int
move_left(struct DrawState* state)
{
if (state->edit_info_focused)
{
- if (state->info_first_shown_column > 0)
- state->info_first_shown_column--;
+ if (state->info_first_shown.col > 0)
+ state->info_first_shown.col--;
return 0;
}
- if (state->input_column == 0)
+ if (state->coord.col == 0)
return 0;
- if (state->input_buffer[state->input_row].text[state->input_column - 1]
+ if (state->input_buffer[state->coord.row].text[state->coord.col - 1]
== '\t')
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column - 1);
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row]
+ .text,
+ state->coord.col - 1);
else
state->input_display_column--;
- state->input_column--;
+ state->coord.col--;
- if (state->input_display_column < state->input_first_shown_column)
- state->input_first_shown_column = state->input_display_column;
+ if (state->input_display_column < state->input_first_shown.col)
+ state->input_first_shown.col = state->input_display_column;
return 0;
}
-int
+static int
move_list_down(struct DrawState* state)
{
goto_msgid(state, next_msgid(state, 1));
return 0;
}
-int
+static int
move_list_end(struct DrawState* state)
{
goto_msgid(state,
@@ -1352,69 +1480,76 @@ move_list_end(struct DrawState* state)
return 0;
}
-int
+static int
move_list_page_down(struct DrawState* state)
{
- *state->error = 0;
+ state->error = ERR_NONE;
goto_msgid(state, next_msgid(state, state->maxy - 2));
return 0;
}
-int
+static int
move_list_page_up(struct DrawState* state)
{
- *state->error = 0;
+ state->error = ERR_NONE;
goto_msgid(state, prev_msgid(state, state->maxy - 2));
return 0;
}
-int
+static int
move_list_start(struct DrawState* state)
{
goto_msgid(state, 1);
return 0;
}
-int
+static int
move_list_up(struct DrawState* state)
{
goto_msgid(state, prev_msgid(state, 1));
return 0;
}
-int
+static int
move_next_word(struct DrawState* state)
{
+ int i;
+ uint32_t* pline;
+ ssize_t len;
+
if (state->edit_info_focused)
return 1;
- int i = state->input_column;
- uint32_t* pline = state->input_buffer[state->input_row].text;
- size_t len = state->input_buffer[state->input_row].length;
+ i = state->coord.col;
+ pline = state->input_buffer[state->coord.row].text;
+ len = state->input_buffer[state->coord.row].length;
while (i < len && !u32_is_word_boundary(pline[i], 0))
i++;
while (i < len && u32_is_word_boundary(pline[i], 0))
i++;
- state->input_column = i;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- if (state->input_display_column - state->input_first_shown_column
+ 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->edit_width - 4)
- state->input_first_shown_column
+ state->input_first_shown.col
= state->input_display_column - state->edit_width + 4;
return 0;
}
-int
+static int
move_prev_word(struct DrawState* state)
{
+ int i;
+ uint32_t* pline;
+
if (state->edit_info_focused)
return 1;
- int i = state->input_column;
- uint32_t* pline = state->input_buffer[state->input_row].text;
+ i = state->coord.col;
+ pline = state->input_buffer[state->coord.row].text;
if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
i--;
@@ -1424,53 +1559,54 @@ move_prev_word(struct DrawState* state)
&& !u32_is_word_boundary(pline[i - 1], 0))
i--;
- state->input_column = i;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
- if (state->input_display_column < state->input_first_shown_column)
- state->input_first_shown_column = state->input_display_column;
+ 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;
return 0;
}
-int
+static int
move_right(struct DrawState* state)
{
+ ssize_t len;
+
if (state->edit_info_focused)
{
- if (state->info_first_shown_column + state->edit_width - 3
+ if (state->info_first_shown.col + state->edit_width - 3
< state->info_rows_maxlen + 1)
- state->info_first_shown_column++;
+ state->info_first_shown.col++;
return 0;
}
- size_t len = state->input_buffer[state->input_row].length;
- if (state->input_column == len)
+ len = state->input_buffer[state->coord.row].length;
+ if (state->coord.col == len)
return 0;
- if (state->input_buffer[state->input_row].text[state->input_column]
- == '\t')
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column + 1);
+ if (state->input_buffer[state->coord.row].text[state->coord.col] == '\t')
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row]
+ .text,
+ state->coord.col + 1);
else
state->input_display_column++;
- state->input_column++;
+ state->coord.col++;
- if (state->input_display_column - state->input_first_shown_column
+ if (state->input_display_column - state->input_first_shown.col
> state->edit_width - 4)
- state->input_first_shown_column
+ state->input_first_shown.col
= state->input_display_column - state->edit_width + 4;
return 0;
}
-int
+static int
move_search_end(struct DrawState* state)
{
- size_t len = u32_strlen(state->search);
+ ssize_t len = u32_strlen(state->search);
state->search_column = len;
- size_t dlen
- = display_length(state, state->search, state->search_column);
+ ssize_t dlen = display_length(state->search, state->search_column);
state->search_display_column = dlen;
if (dlen > state->search_width - 4)
state->search_first_shown_column
@@ -1480,15 +1616,15 @@ move_search_end(struct DrawState* state)
return 0;
}
-int
+static int
move_search_left(struct DrawState* state)
{
if (state->search_column == 0)
return 0;
if (state->search[state->search_column - 1] == '\t')
- state->search_display_column = display_length(state,
- state->search, state->search_column - 1);
+ state->search_display_column = display_length(state->search,
+ state->search_column - 1);
else
state->search_display_column--;
state->search_column--;
@@ -1498,12 +1634,12 @@ move_search_left(struct DrawState* state)
return 0;
}
-int
+static int
move_search_next_word(struct DrawState* state)
{
int i = state->search_column;
uint32_t* pline = state->search;
- size_t len = u32_strlen(state->search);
+ ssize_t len = u32_strlen(state->search);
while (i < len && !u32_is_word_boundary(pline[i], 0))
i++;
@@ -1511,20 +1647,20 @@ move_search_next_word(struct DrawState* state)
i++;
state->search_column = i;
state->search_display_column
- = display_length(state, state->search, state->search_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,
- state->search, state->search_column);
+ state->search_display_column
+ = display_length(state->search, state->search_column);
}
return 0;
}
-int
+static int
move_search_prev_word(struct DrawState* state)
{
int i = state->search_column;
@@ -1540,7 +1676,7 @@ move_search_prev_word(struct DrawState* state)
state->search_column = i;
state->search_display_column
- = display_length(state, state->search, state->search_column);
+ = display_length(state->search, state->search_column);
if (state->search_column < state->search_first_shown_column)
{
@@ -1551,20 +1687,20 @@ move_search_prev_word(struct DrawState* state)
state->search_first_shown_column = 0;
}
state->search_display_column
- = display_length(state, state->search, state->search_column);
+ = display_length(state->search, state->search_column);
return 0;
}
-int
+static int
move_search_right(struct DrawState* state)
{
- size_t len = u32_strlen(state->search);
+ ssize_t len = u32_strlen(state->search);
if (state->search_column == len)
return 0;
if (state->search[state->search_column] == '\t')
- state->search_display_column = display_length(state,
- state->search, state->search_column + 1);
+ state->search_display_column = display_length(state->search,
+ state->search_column + 1);
else
state->search_display_column++;
state->search_column++;
@@ -1576,7 +1712,7 @@ move_search_right(struct DrawState* state)
return 0;
}
-int
+static int
move_search_start(struct DrawState* state)
{
state->search_column = 0;
@@ -1585,70 +1721,71 @@ move_search_start(struct DrawState* state)
return 0;
}
-int
+static int
move_start(struct DrawState* state)
{
if (state->edit_info_focused)
{
- state->info_first_shown_row = 0;
- state->info_first_shown_column = 0;
+ state->info_first_shown.row = 0;
+ state->info_first_shown.col = 0;
return 0;
}
- state->input_column = 0;
- state->input_display_column = 0;
- state->input_first_shown_column = 0;
+ state->coord.col = 0;
+ state->input_display_column = 0;
+ state->input_first_shown.col = 0;
return 0;
}
-int
+static int
move_up(struct DrawState* state)
{
+ ssize_t dlen;
+
if (state->edit_info_focused)
{
- if (state->info_first_shown_row > 0)
- state->info_first_shown_row--;
+ if (state->info_first_shown.row > 0)
+ state->info_first_shown.row--;
return 0;
}
- if (state->input_row > 0)
- state->input_row--;
- if (state->input_first_shown_row > state->input_row)
- state->input_first_shown_row = state->input_row;
+ if (state->coord.row > 0)
+ state->coord.row--;
+ if (state->input_first_shown.row > state->coord.row)
+ state->input_first_shown.row = state->coord.row;
if (state->saved_column == -1)
state->saved_column = state->input_display_column;
- size_t dlen = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_buffer[state->input_row].length);
+ dlen = display_length(state->input_buffer[state->coord.row].text,
+ state->input_buffer[state->coord.row].length);
if (dlen < state->saved_column)
state->input_display_column = dlen;
else
state->input_display_column = state->saved_column;
- state->input_column = input_length(state,
- state->input_buffer[state->input_row].text,
- state->input_display_column);
- if (state->input_first_shown_column > state->input_display_column)
- state->input_first_shown_column = state->input_display_column;
- if (state->input_first_shown_column + state->edit_width - 4
+ state->coord.col
+ = input_length(state->input_buffer[state->coord.row].text,
+ state->input_display_column);
+ if (state->input_first_shown.col > state->input_display_column)
+ state->input_first_shown.col = state->input_display_column;
+ if (state->input_first_shown.col + state->edit_width - 4
< state->input_display_column)
- state->input_first_shown_column
+ state->input_first_shown.col
= state->input_display_column - state->edit_width + 4;
- state->input_display_column = display_length(state,
- state->input_buffer[state->input_row].text,
- state->input_column);
+ state->input_display_column
+ = display_length(state->input_buffer[state->coord.row].text,
+ state->coord.col);
return 0;
}
-int
+static int
next_fuzzy(struct DrawState* state)
{
+ struct PoEntry* current = NULL;
+
if (state->msgid_count == 0)
return 0;
- *state->error = 0;
-
- struct PoEntry* current = NULL;
- for (size_t e = state->msgid_number; e < state->msgid_count; e++)
+ state->error = ERR_NONE;
+ for (ssize_t e = state->msgid_number; e < state->msgid_count; e++)
{
current = state->entries + e;
if (current->flags & FL_FUZZY)
@@ -1676,15 +1813,16 @@ next_fuzzy(struct DrawState* state)
return 0;
}
-int
+static int
next_match(struct DrawState* state)
{
+ struct PoEntry* current = NULL;
+
if (state->msgid_count == 0 || !state->search || !*state->search)
return 0;
- *state->error = 0;
- struct PoEntry* current = NULL;
- for (size_t e = state->msgid_number; e < state->real_msgid_count; e++)
+ state->error = ERR_NONE;
+ for (ssize_t e = state->msgid_number; e < state->real_msgid_count; e++)
{
current = state->entries + e;
if (current->obsolete)
@@ -1702,7 +1840,7 @@ next_match(struct DrawState* state)
return 0;
}
-int
+static int
next_plural(struct DrawState* state)
{
struct PoEntry* current = NULL;
@@ -1716,16 +1854,17 @@ next_plural(struct DrawState* state)
return 0;
}
-int
+static int
next_untranslated(struct DrawState* state)
{
+ struct PoEntry* current = NULL;
+
if (state->msgid_count == 0)
return 0;
- *state->error = 0;
+ state->error = ERR_NONE;
- struct PoEntry* current = NULL;
- for (size_t e = state->msgid_number; e < state->msgid_count; e++)
+ for (ssize_t e = state->msgid_number; e < state->msgid_count; e++)
{
current = state->entries + e;
if (!current->msgstr || !*current->msgstr
@@ -1754,9 +1893,15 @@ next_untranslated(struct DrawState* state)
return 0;
}
-int
+static int
paste_paste_buffer_to_input(struct DrawState* state)
{
+ struct BufferLine* pinput = NULL;
+ struct BufferLine* newbuf = NULL;
+ struct BufferLine* ppaste = NULL;
+ ssize_t lines_to_init = 0;
+ ssize_t lines_to_free = 0;
+
if (state->edit_info_focused)
return 1;
@@ -1770,64 +1915,56 @@ paste_paste_buffer_to_input(struct DrawState* state)
if (state->msgid_number == 1)
{
- STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
+ state->error = ERR_ILLEGAL_ON_FIRST;
return 0;
}
- size_t lines_to_init = 0;
- size_t lines_to_free = 0;
- struct BufferLine* pinput = NULL;
- struct BufferLine* newbuf = NULL;
- struct BufferLine* ppaste = NULL;
-
- if (!state->paste_buffer || !(*state->paste_buffer).text
- || !*(*state->paste_buffer).text)
+ if (!state->paste || !(*state->paste).text || !*(*state->paste).text)
{
/* leave one row */
- for (size_t i = 1; i < state->input_rows_count; i++)
+ for (ssize_t i = 1; i < state->input_rows_count; i++)
free_bufferline(state->input_buffer + i);
- state->input_buffer = realloc(state->input_buffer,
- sizeof(struct BufferLine));
- *(state->input_buffer->text) = 0;
- state->input_column = 0;
- state->input_display_column = 0;
- state->input_first_shown_column = 0;
- state->input_first_shown_row = 0;
- state->input_row = 0;
- state->input_rows_count = 1;
- state->dirty = 1;
+ state->input_buffer = realloc(state->input_buffer,
+ sizeof(struct BufferLine));
+ *(state->input_buffer->text) = 0;
+ init_coord(&state->coord, 0, 0);
+ state->input_display_column = 0;
+ init_coord(&state->input_first_shown, 0, 0);
+ state->input_rows_count = 1;
+ state->dirty = 1;
return 0;
}
- lines_to_init = state->input_rows_count < state->paste_rows_count
- ? state->paste_rows_count - state->input_rows_count
+ lines_to_init = state->input_rows_count < state->paste_count
+ ? state->paste_count - state->input_rows_count
: 0;
- lines_to_free = state->input_rows_count > state->paste_rows_count
- ? state->input_rows_count - state->paste_rows_count
+ lines_to_free = state->input_rows_count > state->paste_count
+ ? state->input_rows_count - state->paste_count
: 0;
- for (size_t i = 0; i < lines_to_free; i++)
+ 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,
- state->paste_rows_count * sizeof(struct BufferLine));
- ppaste = state->paste_buffer;
+ state->paste_count * sizeof(struct BufferLine));
+ ppaste = state->paste;
if (!newbuf)
return 0;
- for (size_t i = 0; i < lines_to_init; i++)
+ for (ssize_t i = 0; i < lines_to_init; i++)
{
- pinput = newbuf + state->paste_rows_count - 1 - i;
+ uint32_t* newtext;
+ pinput = newbuf + state->paste_count - 1 - i;
init_bufferline(pinput);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
if (!newtext)
return 0;
pinput->text = newtext;
}
if (ppaste)
- for (size_t i = 0; i < state->paste_rows_count; i++)
+ for (ssize_t i = 0; i < state->paste_count; i++)
{
pinput = newbuf + i;
u32_strncpy(pinput->text, ppaste->text,
@@ -1836,22 +1973,23 @@ paste_paste_buffer_to_input(struct DrawState* state)
ppaste++;
}
- state->input_rows_count = state->paste_rows_count;
+ state->input_rows_count = state->paste_count;
state->input_buffer = newbuf;
state->dirty = 1;
return 0;
}
-int
+static int
prev_match(struct DrawState* state)
{
+ struct PoEntry* current = NULL;
+
if (state->msgid_count == 0 || !state->search || !*state->search)
return 0;
- *state->error = 0;
- struct PoEntry* current = NULL;
- for (size_t e = state->msgid_number - 1; e > 0; e--)
+ state->error = ERR_NONE;
+ for (ssize_t e = state->msgid_number - 1; e > 0; e--)
{
current = state->entries + e - 1;
if ((current->msgid && *current->msgid
@@ -1867,7 +2005,7 @@ prev_match(struct DrawState* state)
return 0;
}
-int
+static int
prev_plural(struct DrawState* state)
{
if (state->edit_info_focused)
@@ -1879,9 +2017,10 @@ prev_plural(struct DrawState* state)
return 0;
}
-int
+static int
save_msgstr(struct DrawState* state)
{
+ struct BufferLine* pinput_buffer;
uint32_t* to_save = NULL;
size_t to_save_size = 0;
struct PoEntry* current = NULL;
@@ -1895,12 +2034,11 @@ save_msgstr(struct DrawState* state)
to_save = calloc(to_save_size, sizeof(uint32_t));
if (!to_save)
{
- STRLCPY(errors[ERR_CANT_ALLOC], state->error,
- MAXBUFLINE);
+ state->error = ERR_CANT_ALLOC;
return 0;
}
- struct BufferLine* pinput_buffer = NULL;
- for (size_t i = 0; i < state->input_rows_count; i++)
+ pinput_buffer = NULL;
+ for (ssize_t i = 0; i < state->input_rows_count; i++)
{
pinput_buffer = state->input_buffer + i;
if (i == 0)
@@ -1919,7 +2057,7 @@ save_msgstr(struct DrawState* state)
free(state->input_buffer);
state->input_buffer = NULL;
state->input_rows_count = 0;
- for (size_t i = 0; i < state->info_rows_count; i++)
+ for (ssize_t i = 0; i < state->info_rows_count; i++)
free_bufferline(state->info_buffer + i);
free(state->info_buffer);
state->dirty = (state->dirty_before_edit || state->dirty);
@@ -1938,7 +2076,7 @@ save_msgstr(struct DrawState* state)
return 0;
}
-int
+static int
show_edit(struct DrawState* state)
{
if (state->msgstr_index + 1
@@ -1947,35 +2085,33 @@ show_edit(struct DrawState* state)
if (!load_msgstr(state) || !load_info(state))
{
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
+ state->error = ERR_CANT_ALLOC;
state->running = 0;
return 0;
}
- state->dirty_before_edit = state->dirty;
- state->dirty = 0;
- state->fuzzy_before_edit = state->fuzzy_count;
- state->input_changed_fuzzy = UNCHANGED;
- state->show_edit = 1;
- state->input_row = 0;
- state->input_first_shown_row = 0;
- state->input_first_shown_column = 0;
- state->info_first_shown_row = 0;
- state->info_first_shown_column = 0;
+ state->dirty_before_edit = state->dirty;
+ state->dirty = 0;
+ state->fuzzy_before_edit = state->fuzzy_count;
+ state->input_changed_fuzzy = UNCHANGED;
+ state->show_edit = 1;
+ state->coord.row = 0;
+ init_coord(&state->input_first_shown, 0, 0);
+ init_coord(&state->info_first_shown, 0, 0);
move_start(state);
- *state->error = 0;
+ state->error = ERR_NONE;
return 0;
}
-int
+static int
show_help(struct DrawState* state)
{
state->show_help = 1;
- *state->error = 0;
+ state->error = ERR_NONE;
return 0;
}
-int
+static int
show_search(struct DrawState* state)
{
state->show_search = 1;
@@ -1990,12 +2126,12 @@ show_search(struct DrawState* state)
return 0;
}
-int
+static int
toggle_fuzzy(struct DrawState* state)
{
if (state->msgid_number == 1)
{
- STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
+ state->error = ERR_ILLEGAL_ON_FIRST;
return 0;
}
if (state->msgid_count > 0)
@@ -2022,75 +2158,75 @@ toggle_fuzzy(struct DrawState* state)
return 0;
}
-int
+static int
toggle_info_focused(struct DrawState* state)
{
state->edit_info_focused = !state->edit_info_focused;
return 0;
}
-int
+static int
yank_input_to_paste_buffer(struct DrawState* state)
{
+ struct BufferLine* ppaste = NULL;
+ struct BufferLine* newbuf = NULL;
+ struct BufferLine* pinput = NULL;
+ ssize_t lines_to_init = 0;
+ ssize_t lines_to_free = 0;
+
if (state->edit_info_focused)
return 1;
if (state->msgid_number == 1)
{
- STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
+ state->error = ERR_ILLEGAL_ON_FIRST;
return 0;
}
- size_t lines_to_init = 0;
- size_t lines_to_free = 0;
- struct BufferLine* ppaste = NULL;
- struct BufferLine* newbuf = NULL;
- struct BufferLine* pinput = NULL;
-
if (!state->input_buffer || !(*state->input_buffer).text
|| !*(*state->input_buffer).text)
{
- for (size_t i = 0; i < state->paste_rows_count; i++)
- free_bufferline(state->paste_buffer + i);
- free(state->paste_buffer);
- state->paste_buffer = NULL;
- state->paste_rows_count = 0;
+ for (ssize_t i = 0; i < state->paste_count; i++)
+ free_bufferline(state->paste + i);
+ free(state->paste);
+ state->paste = NULL;
+ state->paste_count = 0;
return 0;
}
- lines_to_init = state->paste_rows_count < state->input_rows_count
- ? state->input_rows_count - state->paste_rows_count
+ lines_to_init = state->paste_count < state->input_rows_count
+ ? state->input_rows_count - state->paste_count
: 0;
- lines_to_free = state->paste_rows_count > state->input_rows_count
- ? state->paste_rows_count - state->input_rows_count
+ lines_to_free = state->paste_count > state->input_rows_count
+ ? state->paste_count - state->input_rows_count
: 0;
- for (size_t i = 0; i < lines_to_free; i++)
- free_bufferline(
- state->paste_buffer + state->paste_rows_count - i - 1);
- if (!state->paste_buffer)
+ for (ssize_t i = 0; i < lines_to_free; i++)
+ free_bufferline(state->paste + state->paste_count - i - 1);
+ if (!state->paste)
newbuf = calloc(state->input_rows_count,
sizeof(struct BufferLine));
else
- newbuf = realloc(state->paste_buffer,
+ newbuf = realloc(state->paste,
state->input_rows_count * sizeof(struct BufferLine));
pinput = state->input_buffer;
if (!newbuf)
return 0;
- for (size_t i = 0; i < lines_to_init; i++)
+ for (ssize_t i = 0; i < lines_to_init; i++)
{
+ uint32_t* newtext;
ppaste = newbuf + state->input_rows_count - 1 - i;
init_bufferline(ppaste);
- uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
+ newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
if (!newtext)
return 0;
ppaste->text = newtext;
}
if (pinput)
- for (size_t i = 0; i < state->input_rows_count; i++)
+ for (ssize_t i = 0; i < state->input_rows_count; i++)
{
ppaste = newbuf + i;
u32_strncpy(ppaste->text, pinput->text,
@@ -2099,35 +2235,44 @@ yank_input_to_paste_buffer(struct DrawState* state)
pinput++;
}
- state->paste_rows_count = state->input_rows_count;
- state->paste_buffer = newbuf;
+ state->paste_count = state->input_rows_count;
+ state->paste = newbuf;
return 0;
}
-int
+static int
write_file(struct DrawState* state)
{
+ assert(state != NULL);
+ if (save_file(state))
+ {
+ state->running = 0;
+ return 1;
+ }
+ /* TODO: Check
int result = save_file(state->entries, state->real_msgid_count,
state->filename, state->nplurals);
switch (result)
{
case SAVE_ERR_CANT_ALLOC:
- STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
+ state->error = ERR_CANT_ALLOC;
state->running = 0;
break;
case SAVE_ERR_CANT_OPEN_FILE:
- STRLCPY(errors[ERR_CANT_SAVE], state->error, MAXBUFLINE);
+ state->error = ERR_CANT_SAVE;
state->running = 0;
break;
case SAVE_ERR_CANT_MOVE_FILE:
- STRLCPY(strerror(errno), state->error, MAXBUFLINE);
+ state->error = ERR_ERRNO_SET;
state->running = 0;
break;
default:
- *state->error = 0;
+ state->error = ERR_NONE;
state->dirty = 0;
- }
+ }*/
+ state->error = ERR_NONE;
+ state->dirty = 0;
return 0;
}
@@ -2135,290 +2280,389 @@ int
main(int argc, char** argv)
{
struct tb_event ev;
- char error[MAXBUFLINE];
- char prompt[MAXBUFLINE];
struct DrawState state;
- char filename[MAXPATH];
- int recalculate_size = 1;
struct stat st;
+ char prompt[LINE_DEFAULT];
+ char* dir = NULL;
+ char* filename = NULL;
+ char* pdir = NULL;
+ char* temp = NULL;
+ char* rfn = NULL;
+ const char* saved_tb_error = NULL;
+ char* wd = NULL;
+ ssize_t filename_size = 0;
long lineno;
long col;
- int result;
int dir_access;
int dir_access_errno;
+ int optind = 1;
+ int result;
int saved_errno = 0;
- char* dir = NULL;
+ int tbires;
- *error = 0;
- *prompt = 0;
- *filename = 0;
+ *prompt = 0;
#ifdef __OpenBSD__
if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0)
{
- perror(PROGRAMNAME ": pledge");
- exit(1);
+ perror(PROGRAM_NAME ": pledge");
+ exit(errno);
}
#endif
+
+ setlocale(LC_ALL, "");
+
if (argc == 1)
- return print_error(1, "No filename given");
+ return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG,
+ "No filename given");
- if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+ // main_options_loop:
+ if (optind == argc)
+ return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG,
+ "No filename given");
+
+ if (!strcmp(argv[optind], "-h") || !strcmp(argv[optind], "--help"))
{
printf("Usage:\t%s -h | --help | -V | --full-version | -v |"
" --version\n"
"\t%s filename.po\n",
- PROGRAMNAME, PROGRAMNAME);
+ PROGRAM_NAME, PROGRAM_NAME);
return 0;
}
- else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
+ else if (!strcmp(argv[optind], "-v")
+ || !strcmp(argv[optind], "--version"))
{
- printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
+ printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE);
return 0;
}
- else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--full-version"))
+ else if (!strcmp(argv[optind], "-V")
+ || !strcmp(argv[optind], "--full-version"))
{
- printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
+ printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE);
puts(COPYRIGHT);
printf("Features set in config.h:\n");
-#ifdef CREATE_BACKUPS
- printf(" CREATE_BACKUPS:\tset\n");
-#else
- printf(" CREATE_BACKUPS:\tunset\n");
-#endif
-#ifdef MATCH_MSGSTR_ENDING
- printf(" MATCH_MSGSTR_ENDING:\tset\n");
-#else
- printf(" MATCH_MSGSTR_ENDING:\tunset\n");
-#endif
-#ifdef PLURAL_STRING
- printf(" PLURAL_STRING:\t\"%s\"\n", PLURAL_STRING);
-#else
- printf(" PLURAL_STRING:\tunset\n");
-#endif
- printf(" SAVE_WRAP_WIDTH:\t%d\n", SAVE_WRAP_WIDTH);
- printf(" WRAP_FIRST_MSGSTR:\t%d\n", WRAP_FIRST_MSGSTR);
- printf(" WARN_COUNT_DOTS:\t%d\n", WARN_COUNT_DOTS);
+ printf(" backup_fail:\t\t%s\n", backup_fail ? "set" : "unset");
+ if (backup_suffix)
+ printf(" backup_suffix:\t\"%s\"\n", backup_suffix);
+ else
+ printf(" backup_suffix:\tunset\n");
+ printf(" create_backups:\t%s\n",
+ create_backups ? "set" : "unset");
+ printf(" match_msgstr_ending:\t%s\n",
+ match_msgstr_ending ? "set" : "unset");
+ if (plural_string)
+ printf(" plural_string:\t\"%s\"\n", plural_string);
+ else
+ printf(" plural_string:\tunset\n");
+ printf(" warn_count_dots:\t%s\n",
+ warn_count_dots ? "set" : "unset");
+ printf(" wrap_first_msgstr:\t%s\n",
+ wrap_first_msgstr ? "set" : "unset");
+ printf(" wrap_width:\t\t%d\n", wrap_width);
return 0;
}
- else
+
+ /*
+ * Chicken and egg problem: to get fpathconf(dirfd, _PC_PATH_MAX)
+ * we need a dir, which needs to be allocated, and to allocate it we
+ * need to know the value returned by fpathconf(dirfd, _PC_PATH_MAX)...
+ */
+ /* argv[1] is the filename; BTW, "filename" is a misnomer */
+ filename_size = strlen(argv[optind]) + 1;
+ if (!(dir = malloc(filename_size)))
{
- strncpy(filename, argv[1], MAXPATH - 1);
- filename[MAXPATH - 1] = 0;
- if (!*filename)
- print_error(EINVAL, "No filename given");
+ perror(PROGRAM_NAME ": malloc");
+ exit(errno);
}
- init_drawstate(&state, error, filename, prompt);
+ (void)dirname(dir, argv[optind], filename_size);
+ if (init_limits(dir) < 0)
+ {
+ perror(PROGRAM_NAME ": init_limits");
+ free(dir);
+ exit(errno);
+ }
- if (stat(filename, &st) == -1 || access(filename, R_OK | W_OK) == -1)
- goto file_error;
+ filename = calloc(limits[L_PATH_MAX], 1);
+ if (!filename)
+ {
+ perror(PROGRAM_NAME ": calloc");
+ exit(errno);
+ }
- if (!S_ISREG(st.st_mode))
+ *filename = 0;
+ MEMCCPY(filename, argv[optind], limits[L_PATH_MAX], temp);
+ if (!*filename)
{
- result = print_error(EINVAL, "Is not a regular file");
- goto general_error;
+ free(filename);
+ free(dir);
+ return print_error(EINVAL, "No filename given");
}
+ init_drawstate(&state, filename, prompt);
+
#ifdef __OpenBSD__
if (unveil(state.filename, "rwc") < 0)
{
- perror(PROGRAMNAME ": unveil");
- exit(1);
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
+ }
+ if (unveil(dir, "rwc") < 0)
+ {
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
#endif
- dir = dirname(state.filename);
+ if (!create_backups)
+ goto main_skip_backups;
+
+ if (!(wd = malloc(limits[L_PATH_MAX])))
+ {
+ perror(PROGRAM_NAME ": malloc");
+ goto main_done;
+ }
+ if (!getcwd(wd, limits[L_PATH_MAX]))
+ {
+ perror(PROGRAM_NAME ": getcwd");
+ goto main_done;
+ }
#ifdef __OpenBSD__
if (unveil(dir, "rwc") < 0)
{
- perror(PROGRAMNAME ": unveil");
- exit(1);
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
#endif
-#ifdef CREATE_BACKUPS
+
errno = 0;
if (lstat(state.filename, &st) != -1)
{
if (S_ISLNK(st.st_mode))
{
- if (!(state.real_filename = calloc(MAXPATH, 1)))
- return print_error(1,
- "Allocation failed (out of memory?)");
- state.rfn_len = readlink(state.filename,
- state.real_filename, MAXPATH);
+ if (!(rfn = malloc(limits[L_PATH_MAX])))
+ {
+ perror(PROGRAM_NAME ": malloc");
+ goto main_done;
+ }
+
+ if (!(state.real_filename = malloc(limits[L_PATH_MAX])))
+ {
+ perror(PROGRAM_NAME ": malloc");
+ goto main_done;
+ }
+
+ state.rfn_len = readlink(state.filename, rfn,
+ limits[L_PATH_MAX]);
if (state.rfn_len == -1)
- goto file_error;
- state.real_filename[state.rfn_len] = 0;
+ goto main_done;
+
+ rfn[state.rfn_len] = 0;
+
+ if (*rfn == '/') /* rfn is absolute */
+ {
+ MEMCCPY(state.real_filename, rfn,
+ limits[L_PATH_MAX], temp);
+ state.rfn_len = strlen(rfn);
+ }
+ else /* rfn is relative */
+ {
+ size_t rfn_dir_len;
+
+ state.orig_real_filename
+ = strndup(rfn, state.rfn_len);
+ if (!state.orig_real_filename)
+ {
+ perror(PROGRAM_NAME ": strndup");
+ goto main_done;
+ }
+
+ MEMCCPY(state.real_filename, dir,
+ limits[L_PATH_MAX], temp);
+ if (!temp)
+ {
+ errno = ENAMETOOLONG;
+ goto main_done;
+ }
+ *(temp - 1) = '/'; /* overwrite NUL */
+ rfn_dir_len = temp - state.real_filename - 1;
+ MEMCCPY_EXT(temp, state.real_filename, rfn,
+ limits[L_PATH_MAX] - rfn_dir_len, temp);
+ if (temp)
+ state.rfn_len = temp
+ - state.real_filename - 1;
+ }
+ free(rfn);
+ rfn = NULL;
#ifdef __OpenBSD__
if (unveil(state.real_filename, "rwc") < 0)
{
- perror(PROGRAMNAME ": unveil");
- exit(1);
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
#endif
if (stat(state.real_filename, &st) == -1)
- goto file_error;
- }
- if (!(state.backup_filename = calloc(MAXPATH, 1)))
- {
- free(state.real_filename);
- return print_error(1,
- "Allocation failed (out of memory?)");
+ goto main_done;
}
+ }
+ else
+ goto main_done;
- /* Backup filename is the target file + suffix if the file to
- * write is a symlink, otherwise file + suffix */
- STRLCPY(state.rfn_len > 0 ? state.real_filename : state.filename,
- state.backup_filename, MAXPATH);
- STRLCAT(backup_suffix, state.backup_filename, MAXPATH);
-#ifdef __OpenBSD__
- if (unveil(state.backup_filename, "rwc") < 0)
- {
- perror(PROGRAMNAME ": unveil");
- exit(1);
- }
-#endif
+ if (!(state.backup_filename = malloc(limits[L_PATH_MAX])))
+ {
+ perror(PROGRAM_NAME ": malloc");
+ goto main_done;
}
+
+ /* Backup filename is the target file + suffix if the file to
+ * write is a symlink, otherwise file + suffix */
+ MEMCCPY(state.backup_filename,
+ state.rfn_len > 0 ? state.real_filename : state.filename,
+ limits[L_PATH_MAX], temp);
+ if (!temp) /* filename is full, turning off backups */
+ state.backup_possible = 0;
else
- goto file_error;
-#endif /* CREATE_BACKUPS */
+ MEMCCPY_EXT((temp - 1), state.backup_filename, backup_suffix,
+ limits[L_PATH_MAX] + temp - state.backup_filename,
+ temp);
+#ifdef __OpenBSD__
+ if (unveil(state.backup_filename, "rwc") < 0)
+ {
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
+ }
+#endif
+main_skip_backups:
if (state.rfn_len > 0)
{
- dir = dirname(state.real_filename);
+ if (!(pdir = realloc(dir, limits[L_PATH_MAX])))
+ {
+ perror(PROGRAM_NAME ": realloc");
+ goto main_done;
+ }
+ dir = pdir;
+ *dir = 0;
+ (void)dirname(dir, state.real_filename, limits[L_PATH_MAX]);
#ifdef __OpenBSD__
if (unveil(dir, "rwc") < 0)
{
- perror(PROGRAMNAME ": unveil");
- exit(1);
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
#endif
}
- dir_access = access(dir, W_OK | X_OK);
- dir_access_errno = errno;
-#ifdef BACKUP_FAIL_OK
- state.backup_possible = dir_access != -1;
-#else /* !BACKUP_FAIL_OK */
- if (dir_access == -1)
+#ifdef __OpenBSD__
+ if (unveil_terminfo_path() < 0)
{
- errno = dir_access_errno;
- goto file_error;
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
-#endif /* BACKUP_FAIL_OK */
- if (stat(state.filename, &st) == -1)
+ if (unveil(NULL, NULL) < 0)
{
- errno = dir_access_errno;
- goto file_error;
+ perror(PROGRAM_NAME ": unveil");
+ goto main_done;
}
+#endif
- if (!S_ISREG(st.st_mode))
+ dir_access = access(dir, W_OK | X_OK);
+ dir_access_errno = errno;
+
+ if (!backup_fail && state.backup_possible)
+ state.backup_possible = dir_access != -1;
+
+ result = stat(state.filename, &st);
+ if (result == -1)
{
- result = print_error(EINVAL, "Is not a regular file");
- goto general_error;
+ if (dir_access == -1)
+ {
+ errno = dir_access_errno;
+ goto main_done;
+ }
+ goto init_termbox;
}
-#ifdef __OpenBSD__
- if (unveil(NULL, NULL) < 0)
+ errno = 0;
+
+ if (!S_ISREG(st.st_mode))
{
- perror(PROGRAMNAME ": unveil");
- exit(1);
+ state.error = ERR_NOTREG_FILE;
+ goto main_done;
}
-#endif
if (access(state.filename, F_OK | R_OK | W_OK) == -1)
- goto file_error;
+ goto main_done;
if (state.rfn_len > 0
&& access(state.real_filename, F_OK | R_OK | W_OK) == -1)
- goto file_error;
+ goto main_done;
+
+ if (load_file(&state, &lineno, &col))
+ goto main_done;
- lineno = 1;
- col = 1;
- result = load_file(&state, &lineno, &col);
- switch (result)
- {
- case LOAD_ERR_CANT_ALLOC:
- case LOAD_ERR_P_ALLOC:
- (void)print_error(result, "Allocation failed (out of memory?)");
- goto general_error; // TODO: Avoid free()
- case LOAD_ERR_CANT_OPEN_FILE:
- (void)print_error(result, "Cannot open file `%s'", filename);
- goto general_error;
- case LOAD_ERR_NOT_PO_FILE:
- (void)print_error(result, "Not a .po file `%s'", filename);
- goto general_error;
- case LOAD_ERR_P_INVAL:
- (void)print_error(result, "Invalid value");
- goto general_error;
- case LOAD_ERR_P_SYNTAX:
- (void)print_error(result, "%s:%ld:%ld: Syntax error", filename,
- lineno, col);
- goto general_error;
- case LOAD_ERR_NONE:
- if (*error)
- {
- result = print_error(1, error);
- goto general_error;
- }
- break;
- default:
- result = print_error(1, "Unknown error (%d)", result);
- goto general_error;
- }
state.msgid_number = state.msgid_count > 0 ? 1 : 0;
state.first_shown_msgid = state.msgid_number;
- goto init_termbox;
-
-file_error:
- result = errno;
- perror(PROGRAMNAME);
- /* FALLTHROUGH */
-general_error:
- free(state.real_filename);
- free(state.backup_filename);
- return result;
+ goto init_termbox;
init_termbox:
- tb_init();
- *error = 0;
- state.maxx = 0;
+ tbires = 0;
+ if ((tbires = tb_init()))
+ {
+ saved_tb_error = tb_strerror(tbires);
+ errno = ERR_TB_ERROR;
+ goto main_done;
+ }
+
+ errno = 0;
+ state.maxx = 0;
+ state.recalculate_size = 1;
while (state.running)
{
+ tb_set_clear_attrs(colors[C_CLEAR].fg, colors[C_CLEAR].bg);
tb_clear();
- if (recalculate_size)
+ if (state.recalculate_size)
{
- state.maxx = tb_width();
- state.help_width = calculate_help_width(&state);
- state.edit_width = calculate_edit_width(&state);
- state.search_width = calculate_search_width(&state);
- recalculate_size = 0;
+ recalculate_size(&state);
+ state.recalculate_size = 0;
}
- state.maxy = tb_height();
-
- if (!draw(&state))
+ if (draw(&state))
{
- STRLCPY(errors[ERR_CANT_ALLOC], state.error, MAXBUFLINE);
- saved_errno = ENOMEM;
+ state.running = 0;
goto main_done;
}
tb_present();
result = tb_poll_event(&ev);
if (result == TB_OK && ev.type == TB_EVENT_RESIZE)
- recalculate_size = 1;
+ state.recalculate_size = 1;
else if (result == TB_OK && ev.type == TB_EVENT_KEY)
handle_key_event(&ev, &state);
}
main_done:
+ saved_errno = errno;
+
+ if (saved_tb_error)
+ {
+ tb_shutdown(1);
+ print_error(1, saved_tb_error);
+ exit(CUSTOM_ERROR_START + saved_errno);
+ }
+
tb_shutdown(saved_errno != ENOMEM);
- if (state.error && *state.error)
- return print_error(1, state.error);
- /*free_drawstate(&state);*/
- return 0;
+ if (saved_errno)
+ perror(PROGRAM_NAME);
+
+ if (state.error == ERR_NOTREG_FILE)
+ print_error(1, errors[state.error], state.filename);
+ else if (state.error)
+ print_error(1, errors[state.error]);
+
+ return saved_errno
+ ? saved_errno
+ : (state.error ? CUSTOM_ERROR_START + state.error : 0);
}
diff --git a/termbox.h b/termbox.h
@@ -2302,7 +2302,9 @@ static int init_cellbuf(void);
static int tb_deinit(int deallocate);
static int load_terminfo(void);
static int load_terminfo_from_path(const char *path, const char *term);
+#ifdef __OpenBSD__
static int unveil_terminfo_path(void);
+#endif
static int read_terminfo_path(const char *path);
static int parse_terminfo_caps(void);
static int load_builtin_caps(void);
diff --git a/util.c b/util.c
@@ -1,7 +1,7 @@
/* This program is licensed under the terms of GNU GPL v3 or (at your option)
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
-
+#include <assert.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
@@ -12,6 +12,11 @@
#include "po.h"
#include "util.h"
+/* Whitespace definition for determining word boundaries */
+#define B_WS " \v\t"
+/* Full word boundary delimiter definition */
+#define B_FULL " \v\t()[]{},.;:/\\"
+
enum { UTLERRNONE = 0, UTLERROVFL = 1 };
int utlerrno = UTLERRNONE;
@@ -26,14 +31,15 @@ u8_string_to_unicode(uint32_t* us, const char* s, const size_t max)
int u8_len = 0;
size_t added = 0;
- while (s && *s && u8_len != TB_ERR)
+ assert(s != NULL);
+ while (*s)
{
u8_len = tb_utf8_char_to_unicode(&uch, s);
if (added + 1 > max)
break;
- *pus++ = uch;
- if (u8_len != TB_ERR)
+ if (u8_len > 0)
{
+ *pus++ = uch;
s += u8_len;
added++;
}
@@ -45,48 +51,42 @@ u8_string_to_unicode(uint32_t* us, const char* s, const size_t max)
/* max - max Unicode characters to convert to UTF-8
* Returns: number of processed Unicode characters */
size_t
-unicode_string_to_u8(char* s, const size_t s_size, const uint32_t* us,
- const size_t max)
+unicode_string_to_u8(char* s, const uint32_t* us, const size_t max)
{
char ch[8];
+ char* ps = NULL;
const uint32_t* pus = us;
size_t len;
size_t added = 0;
- if (!pus)
- return added;
-
- *s = 0;
+ assert((s != NULL) && (us != NULL));
+ ps = s;
while (*pus)
{
- size_t res;
-
len = tb_utf8_unicode_to_char(ch, *pus);
- ch[len] = 0;
- res = strlcat(s, ch, s_size);
- if (res >= s_size)
- {
- utlerrno = UTLERROVFL;
+ if (added + 1 > max)
return added;
- }
+ memccpy(ps, ch, 0, len);
+ ps += len;
added++;
pus++;
- if (added + 1 > max)
- break;
}
- utlerrno = UTLERRNONE;
+ *ps = 0;
return added;
}
+// TODO: Check
size_t
u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr, size_t max)
{
- uint32_t* buf = calloc(max + 1, sizeof(uint32_t));
- if (!buf)
- return 0;
+ uint32_t* buf = NULL;
size_t buf_len = 0;
size_t msgid_len = 0;
+ buf = calloc(max + 1, sizeof(uint32_t));
+ if (!buf)
+ return 0;
+
buf_len = u32_strncpy(buf, msgstr, (max + 1) * sizeof(uint32_t));
msgid_len = u32_strlen(msgid);
@@ -138,6 +138,7 @@ u32_count_chars(const uint32_t* s, const uint32_t ch)
{
size_t result = 0;
const uint32_t* ps = s;
+ assert(s != NULL);
while (*ps)
{
if (*ps == ch)
@@ -154,9 +155,7 @@ u32_count_strings(const uint32_t* haystack, const uint32_t* needle)
const uint32_t* phaystack = haystack;
size_t needle_len = 0;
- if (!haystack || !needle)
- return 0;
-
+ assert((haystack != NULL) && (needle != NULL));
needle_len = u32_strlen(needle);
if (!needle_len)
return 0;
@@ -173,13 +172,16 @@ u32_count_strings(const uint32_t* haystack, const uint32_t* needle)
size_t
u32_encode_tabs(uint32_t* s, size_t max)
{
- uint32_t* buf = calloc(max + 1, sizeof(uint32_t));
- if (!buf)
- return 0;
- uint32_t* pbuf = buf;
+ uint32_t* buf = NULL;
+ uint32_t* pbuf = NULL;
uint32_t* ps = s;
size_t newlen = 0;
+ assert(s != NULL);
+ buf = calloc(max + 1, sizeof(uint32_t));
+ if (!buf)
+ return 0;
+ pbuf = buf;
while (*ps)
{
if (*ps == '\t')
@@ -204,13 +206,16 @@ u32_encode_tabs(uint32_t* s, size_t max)
size_t
u32_decode_tabs(uint32_t* s, size_t max)
{
- uint32_t* buf = calloc(max + 1, sizeof(uint32_t));
- if (!buf)
- return 0;
- uint32_t* pbuf = buf;
+ uint32_t* buf = NULL;
+ uint32_t* pbuf = NULL;
uint32_t* ps = s;
size_t newlen = 0;
+ assert(s != NULL);
+ buf = calloc(max + 1, sizeof(uint32_t));
+ if (!buf)
+ return 0;
+ pbuf = buf;
while (*ps)
{
if (*ps == '\\' && *(ps + 1) == 't')
@@ -232,12 +237,28 @@ u32_decode_tabs(uint32_t* s, size_t max)
}
size_t
+u8_strlen(const char* s)
+{
+ const char* ps = s;
+ uint32_t uc;
+ size_t len;
+ size_t result = 0;
+ assert(s != NULL);
+ while (*ps)
+ {
+ len = tb_utf8_char_to_unicode(&uc, ps);
+ result += len;
+ ps++;
+ }
+ return result;
+}
+
+size_t
u32_strlen(const uint32_t* s)
{
const uint32_t* ps = s;
size_t len = 0;
- if (!s)
- return 0;
+ assert(s != NULL);
while (*ps++)
len++;
return len;
@@ -247,6 +268,7 @@ const uint32_t*
u32_strstr(const uint32_t* haystack, const uint32_t* needle)
{
const uint32_t* ph = haystack;
+ assert((haystack != NULL) && (needle != NULL));
while (*ph)
{
if (u32_starts_with(ph, needle))
@@ -321,14 +343,16 @@ u32_u8_strncpy(uint32_t* to, const char* from, size_t max)
return len;
}
+// TODO: Check
size_t
u8_u32_strncpy(char* to, const uint32_t* from, size_t max)
{
- char* cfrom = calloc(max + 1, 1);
+ char* cfrom = NULL;
size_t len;
+ cfrom = calloc(max + 1, 1);
if (!cfrom)
return 0;
- unicode_string_to_u8(cfrom, max + 1, from, max);
+ unicode_string_to_u8(cfrom, from, max);
len = strlen(cfrom);
strncpy(to, cfrom, max);
if (len && len < max)
@@ -422,24 +446,31 @@ u32_next_line(const uint32_t* s, const uint32_t** next_line,
return result;
}
-const int
+int
is_word_boundary(const char ch, const int strictly_whitespace)
{
- return strictly_whitespace ? strchr(" \v\t", ch) != NULL
- : strchr(" \v\t()[]{},.;:/\\", ch) != NULL;
+ return strictly_whitespace ? strchr(B_WS, ch) != NULL
+ : strchr(B_FULL, ch) != NULL;
}
-const int
+int
u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace)
{
- return strictly_whitespace
- ? u32_strchr((uint32_t*)L" \v\t", ch) != NULL
- : u32_strchr((uint32_t*)L" \v\t()[]{},.;:/\\", ch) != NULL;
+ static uint32_t uws[sizeof(B_WS) + 1];
+ static uint32_t ufull[sizeof(B_FULL) + 1];
+ if (!*uws)
+ {
+ (void)u8_string_to_unicode(uws, B_WS, sizeof(B_WS));
+ (void)u8_string_to_unicode(ufull, B_FULL, sizeof(B_FULL));
+ }
+ return strictly_whitespace ? u32_strchr(uws, ch) != NULL
+ : u32_strchr(ufull, ch) != NULL;
}
-const int
+int
starts_with(const char* s, const char* with)
{
+ assert((s != NULL) && (with != NULL));
while (*s && *s == *with)
{
if (*s != *with)
@@ -450,9 +481,10 @@ starts_with(const char* s, const char* with)
return *with == 0;
}
-const int
+int
u32_starts_with(const uint32_t* s, const uint32_t* with)
{
+ assert((s != NULL) && (with != NULL));
while (*s && *s == *with)
{
if (*s != *with)
@@ -467,8 +499,7 @@ const char*
u8_basename(const char* path)
{
const char* ppath;
- if (!path)
- return NULL;
+ assert(path != NULL);
ppath = path + strlen(path);
while (ppath != path && *ppath != '/')
ppath--;
diff --git a/util.h b/util.h
@@ -4,11 +4,11 @@
#include <stdint.h>
#include <sys/types.h>
+
#define ABS(a) ((a < 0) ? (-a) : (a))
#define LEN(x) (sizeof(x) / sizeof(x[0]))
#define MAX(a, b) ((a > b) ? a : b)
#define MIN(a, b) ((a < b) ? a : b)
-#define PROGRAMNAME "poe"
#define STRLCAT(tail, head, totallen) \
do \
{ \
@@ -27,22 +27,112 @@
exit(1); \
} \
} while (0)
+#define MEMCCPY(to, from, tolen, temp) \
+ do \
+ { \
+ temp = memccpy(to, from, 0, tolen); \
+ if (!temp) \
+ to[tolen > 0 ? tolen - 1 : 0] = 0; \
+ } while (0)
+#define MEMCCPY_EXT(to, fallbackto, from, tolen, temp) \
+ do \
+ { \
+ temp = memccpy(to, from, 0, tolen); \
+ if (!temp) \
+ fallbackto[tolen > 0 ? tolen - 1 : 0] = 0; \
+ } while (0)
+#define U32_MEMCCPY(to, from, tolen, temp) \
+ do \
+ { \
+ temp = u32_memccpy(to, from, 0L, tolen); \
+ if (!temp) \
+ to[tolen > 0 ? tolen - 1 : 0] = 0; \
+ } while (0)
+#define U32_U8_MEMCCPY(to, from, tolen, temp) \
+ do \
+ { \
+ temp = u32_u8_memccpy(to, from, 0L, tolen); \
+ if (!temp) \
+ to[tolen > 0 ? tolen - 1 : 0] = 0; \
+ } while (0)
+#define U32_MEMCCPY_EXT(to, fallbackto, from, tolen, fbtolen, temp) \
+ do \
+ { \
+ temp = u32_memccpy(to, from, 0L, tolen); \
+ if (!temp) \
+ fallbackto[fbtolen > 0 ? fbtolen - 1 : 0] = 0; \
+ } while (0)
+
+#define ENSURE_SIZE(to, temp, tosize, testsize, fromsize, label, type) \
+ do \
+ { \
+ if (!(to) || (tosize) < (testsize)) \
+ { \
+ temp = realloc(to, (fromsize) * sizeof(type)); \
+ if (!temp) \
+ { \
+ state->running = 0; \
+ goto label; \
+ } \
+ tosize = fromsize; \
+ to = temp; \
+ } \
+ } while (0)
+/*
+ * - Make sure the size of the destination string is large enough to copy, and
+ * copy
+ * - Sizes are in bytes
+ * - We explicitly use fromsize in MEMCCPY, because we ensured that tosize is
+ * at least as big, and to allow a subset of from to be copied
+ */
+#define U8_SAFE_COPY(to, temp, tosize, from, fromsize, label) \
+ do \
+ { \
+ ENSURE_SIZE(to, temp, tosize, fromsize, fromsize, label, char); \
+ MEMCCPY(to, from, fromsize, temp); \
+ } while (0)
+/*
+ * - Make sure the size of the destination string is large enough to copy, and
+ * copy
+ * - Sizes are in uint32_t units
+ * - We explicitly use fromsize in U32_MEMCCPY, because we ensured that tosize
+ * is at least as big, and to allow a subset of from to be copied
+ */
+#define U32_SAFE_COPY(to, temp, tosize, from, fromsize, label) \
+ do \
+ { \
+ ENSURE_SIZE(to, temp, tosize, fromsize, fromsize, label, \
+ uint32_t); \
+ U32_MEMCCPY(to, from, fromsize, temp); \
+ } while (0)
+#define UNUSED(x) ((void)(x))
+
+#define CUSTOM_ERROR_START 200
enum {
+ ERR_NONE,
+ ERR_NO_CL_ARG,
+ ERR_NOTPO_FILE,
+ ERR_NOTREG_FILE,
+ ERR_TB_ERROR,
ERR_UNKNOWN_KEY,
ERR_UNKNOWN_KEY_HELP,
ERR_EXIT_KEY,
- ERR_DLG_OPEN_QUIT,
ERR_DLG_OPEN,
+ ERR_DLG_OPEN_QUIT,
ERR_DLG_FOCUS_EDIT,
ERR_EMPTY_FILE,
ERR_CANT_ALLOC,
ERR_CANT_SAVE,
ERR_CANT_MOVE,
ERR_ILLEGAL_ON_FIRST,
- ERR_NO_PLURAL_FORMS
+ ERR_SYNTAX,
+ ERR_NO_PLURAL_FORMS,
+ ERR_ERRNO_SET
};
+enum { ACT_IDLE, ACT_WRITING, ACT_READING };
+
#define MAXBUFLINE 4096
#define MAXCOPYBUF 8192
#define MAXDATEBUF 80
@@ -58,14 +148,14 @@ extern int utlerrno;
size_t strlcpy(char* dst, const char* src, size_t dsize);
size_t strlcat(char* dst, const char* src, size_t dsize);
size_t u8_string_to_unicode(uint32_t* us, const char* s, const size_t max);
-size_t unicode_string_to_u8(char* s, const size_t s_size, const uint32_t* us,
- const size_t max);
+size_t unicode_string_to_u8(char* s, const uint32_t* us, const size_t max);
size_t u32_match_msgid_ending(const uint32_t* msgid, uint32_t* msgstr,
size_t max);
size_t u32_count_chars(const uint32_t* s, const uint32_t ch);
size_t u32_count_strings(const uint32_t* haystack, const uint32_t* needle);
size_t u32_encode_tabs(uint32_t* s, size_t max);
size_t u32_decode_tabs(uint32_t* s, size_t max);
+size_t u8_strlen(const char* s);
size_t u32_strlen(const uint32_t* s);
const uint32_t* u32_strstr(const uint32_t* haystack, const uint32_t* needle);
const uint32_t* u32_strchr(const uint32_t* haystack, const uint32_t needle);
@@ -77,9 +167,9 @@ size_t u32_lines_in_string(const uint32_t* s, const int include_eol,
const size_t wrap);
size_t u32_next_line(const uint32_t* s, const uint32_t** sptr,
const int include_eol, const size_t wrap);
-const int is_word_boundary(const char ch, const int strictly_whitespace);
-const int u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace);
-const int starts_with(const char* s, const char* with);
-const int u32_starts_with(const uint32_t* s, const uint32_t* with);
+int is_word_boundary(const char ch, const int strictly_whitespace);
+int u32_is_word_boundary(const uint32_t ch, const int strictly_whitespace);
+int starts_with(const char* s, const char* with);
+int u32_starts_with(const uint32_t* s, const uint32_t* with);
const char* u8_basename(const char* path);
/*void poe_log(const char* msg, ...);*/
diff --git a/version.h.in b/version.h.in
@@ -2,5 +2,6 @@
* any later version. Copyright (C) 2021-2026 Страхиња Радић.
* See the file LICENSE for exact copyright and license details. */
-#define VERSION "%VERSION%"
-#define DATE "%DATE%"
+#define PROGRAM_NAME "poe"
+#define VERSION "%VERSION%"
+#define DATE "%DATE%"