poe

Уређивач .po фајлова
git clone https://git.sr.ht/~strahinja/poe
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

poe.c (59311B)


      1 /* This program is licensed under the terms of GNU GPL v3 or (at your option)
      2  * any later version. Copyright (C) 2021-2024  Страхиња Радић.
      3  * See the file LICENSE for exact copyright and license details. */
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <stdarg.h>
      8 #include <stdint.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <sys/stat.h>
     13 #include <unistd.h>
     14 
     15 #define TB_OPT_V1_COMPAT
     16 #define TB_IMPL
     17 
     18 #define COPYRIGHT                                                             \
     19 	("  This program is licensed under the terms of GNU GPL v3"           \
     20 	 " or (at your option)\n"                                             \
     21 	 "  any later version. Copyright (C) 2021-2024  Strahinya Radich.\n"  \
     22 	 "  See the file LICENSE for exact copyright and license "            \
     23 	 "details.\n"                                                         \
     24 	 "  File termbox.h is based on termbox2 TUI library, (c) 2021"        \
     25 	 " termbox developers,\n"                                             \
     26 	 "  which is released under the terms of Expat license, see the file" \
     27 	 "  LICENSE.termbox.\n")
     28 #define POE_C_GLOBALS
     29 
     30 #include "version.h"
     31 #include "util.h"
     32 #include "po.h"
     33 #include "draw.h"
     34 #include "config.h"
     35 
     36 extern const char* backup_suffix;
     37 extern const char* errors[];
     38 extern const char* prompt_dirty;
     39 extern const char* prompt_cancel_dirty;
     40 extern const char* prompt_overwrite;
     41 
     42 void cancel_callback(struct DrawState* state, struct tb_event* ev);
     43 char* dirname(char* path);
     44 void goto_msgid(struct DrawState* state, const size_t msgid_number);
     45 void handle_key_event(struct tb_event* ev, struct DrawState* state);
     46 int insert_char(struct DrawState* state, uint32_t ch);
     47 void insert_search_char(struct DrawState* state, uint32_t ch);
     48 struct DrawState* join_lines(struct DrawState* state, size_t first_line,
     49 	int forward);
     50 struct BufferLine* load_info(struct DrawState* state);
     51 struct BufferLine* load_msgstr(struct DrawState* state);
     52 size_t next_msgid(struct DrawState* state, const int delta);
     53 void overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev);
     54 void overwrite_paste_callback(struct DrawState* state, struct tb_event* ev);
     55 size_t prev_msgid(struct DrawState* state, const int delta);
     56 int print_error(const int code, const char* msg, ...);
     57 void quit_callback(struct DrawState* state, struct tb_event* ev);
     58 size_t real_msgid_to_display_msgid(const struct DrawState* state);
     59 
     60 void
     61 cancel_callback(struct DrawState* state, struct tb_event* ev)
     62 {
     63 	if (ev->ch == 'y')
     64 		save_msgstr(state);
     65 	else
     66 		cancel_msgstr(state);
     67 	state->prompt_callback = NULL;
     68 }
     69 
     70 char*
     71 dirname(char* path)
     72 {
     73 	static char* result    = NULL;
     74 	static const char* dot = ".";
     75 	char* p		       = NULL;
     76 	char* slash	       = NULL;
     77 
     78 	if (!path || !*path)
     79 		return NULL;
     80 
     81 	free(result);
     82 	result = malloc(MAXPATH);
     83 	memset(result, 0, MAXPATH);
     84 
     85 	STRLCPY(path, result, MAXPATH);
     86 	p = result + strlen(result) - 1;
     87 	if (*p == '/')
     88 		*p = 0;
     89 
     90 	slash = strrchr(result, '/');
     91 	if (!slash)
     92 		return (char*)dot;
     93 
     94 	*slash = 0;
     95 
     96 	return result;
     97 }
     98 
     99 void
    100 goto_msgid(struct DrawState* state, const size_t msgid_number)
    101 {
    102 	size_t last_non_obsolete
    103 		= state->real_msgid_count - state->obsolete_at_end_count;
    104 
    105 	if (msgid_number > state->real_msgid_count)
    106 		return;
    107 
    108 	state->msgid_number = msgid_number;
    109 
    110 	if (state->msgid_number < state->first_shown_msgid)
    111 		state->first_shown_msgid = state->msgid_number;
    112 	else if (state->msgid_number
    113 		> state->first_shown_msgid + state->maxy - 2)
    114 		state->first_shown_msgid
    115 			= state->msgid_number - (state->maxy - 2);
    116 	else if (last_non_obsolete < state->msgid_number + state->maxy - 2)
    117 		state->first_shown_msgid = (last_non_obsolete > state->maxy - 2
    118 				? last_non_obsolete - (state->maxy - 2)
    119 				: 1);
    120 	else
    121 		;
    122 }
    123 
    124 void
    125 handle_key_event(struct tb_event* ev, struct DrawState* state)
    126 {
    127 	const struct Key* current = NULL;
    128 
    129 	*state->error = 0;
    130 
    131 	if (state->in_prompt)
    132 	{
    133 		if (ev->ch == 'y' || ev->ch == 'n')
    134 		{
    135 			if (state->prompt_callback)
    136 				state->prompt_callback(state, ev);
    137 			state->in_prompt = 0;
    138 			*state->prompt	 = 0;
    139 		}
    140 		else if (ev->key == TB_KEY_ESC)
    141 			cancel_close(state);
    142 		return;
    143 	}
    144 
    145 	/* Remember to also edit the following condition if you reassign motion
    146 	 * keys */
    147 	if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
    148 		&& state->show_edit)
    149 		state->saved_column = -1;
    150 
    151 	for (int key = 0; key < LEN(top_level_keys); key++)
    152 	{
    153 		current = top_level_keys + key;
    154 		if (((current->ev.key == ev->key) && !ev->ch)
    155 			|| (current->ev.ch && (current->ev.ch == ev->ch)))
    156 			if (current->callback && !current->callback(state))
    157 				return;
    158 	}
    159 
    160 	if (state->show_help)
    161 	{
    162 		STRLCPY(errors[ERR_DLG_OPEN_QUIT], state->error, MAXBUFLINE);
    163 		return;
    164 	}
    165 	else if (state->show_edit)
    166 	{
    167 		for (int key = 0; key < LEN(edit_keys); key++)
    168 		{
    169 			current = edit_keys + key;
    170 			if (((current->ev.key == ev->key) && !ev->ch)
    171 				|| (current->ev.ch
    172 					&& (current->ev.ch == ev->ch)))
    173 				if (current->callback
    174 					&& !current->callback(state))
    175 					return;
    176 		}
    177 		if (!state->edit_info_focused && ev->ch)
    178 		{
    179 			insert_char(state, ev->ch);
    180 			return;
    181 		}
    182 	}
    183 	else if (state->show_search)
    184 	{
    185 		for (int key = 0; key < LEN(search_keys); key++)
    186 		{
    187 			current = search_keys + key;
    188 			if (((current->ev.key == ev->key) && !ev->ch)
    189 				|| (current->ev.ch
    190 					&& (current->ev.ch == ev->ch)))
    191 				if (current->callback
    192 					&& !current->callback(state))
    193 					return;
    194 		}
    195 		if (ev->ch)
    196 		{
    197 			insert_search_char(state, ev->ch);
    198 			return;
    199 		}
    200 	}
    201 	else
    202 	{
    203 		for (int key = 0; key < LEN(main_keys); key++)
    204 		{
    205 			current = main_keys + key;
    206 			if (((current->ev.key == ev->key) && !ev->ch)
    207 				|| (current->ev.ch
    208 					&& (current->ev.ch == ev->ch)))
    209 				if (current->callback)
    210 				{
    211 					if (!current->callback(state))
    212 						return;
    213 					else
    214 						break;
    215 				}
    216 		}
    217 	}
    218 
    219 	if (state->edit_info_focused)
    220 		STRLCPY(errors[ERR_DLG_FOCUS_EDIT], state->error, MAXBUFLINE);
    221 	else if (state->show_edit || state->show_search)
    222 		STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, MAXBUFLINE);
    223 	else
    224 		STRLCPY(errors[ERR_UNKNOWN_KEY_HELP], state->error, MAXBUFLINE);
    225 }
    226 
    227 int
    228 insert_char(struct DrawState* state, uint32_t ch)
    229 {
    230 	if (state->edit_info_focused)
    231 		return 1;
    232 	struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
    233 	size_t len		       = buffer_line->length;
    234 	if (state->input_column < MAXBUFLINE - 1)
    235 	{
    236 		size_t i     = len + 1;
    237 		int add_null = buffer_line->text[state->input_column] == 0;
    238 		while (i < MAXBUFLINE - 2 && i > state->input_column)
    239 		{
    240 			buffer_line->text[i] = buffer_line->text[i - 1];
    241 			i--;
    242 		}
    243 		buffer_line->text[state->input_column++] = ch;
    244 		buffer_line->length++;
    245 		if (state->input_column == MAXBUFLINE)
    246 			state->input_column = MAXBUFLINE - 1;
    247 		state->input_display_column = display_length(state,
    248 			state->input_buffer[state->input_row].text,
    249 			state->input_column);
    250 		if (state->input_display_column - state->input_first_shown_column
    251 			> state->edit_width - 4)
    252 		{
    253 			state->input_first_shown_column
    254 				= state->input_display_column
    255 				- state->edit_width + 4;
    256 			state->input_display_column = display_length(state,
    257 				state->input_buffer[state->input_row].text,
    258 				state->input_column);
    259 		}
    260 		if (add_null)
    261 			buffer_line->text[state->input_column] = 0;
    262 	}
    263 	state->dirty = 1;
    264 	return 0;
    265 }
    266 
    267 void
    268 insert_search_char(struct DrawState* state, uint32_t ch)
    269 {
    270 	size_t len = u32_strlen(state->search);
    271 	if (state->search_column < MAXSEARCH - 1)
    272 	{
    273 		size_t i     = len + 1;
    274 		int add_null = state->search[state->search_column] == 0;
    275 		while (i < MAXSEARCH - 2 && i > state->search_column)
    276 		{
    277 			state->search[i] = state->search[i - 1];
    278 			i--;
    279 		}
    280 		state->search[state->search_column++] = ch;
    281 		if (state->search_column == MAXSEARCH)
    282 			state->search_column = MAXSEARCH - 1;
    283 		state->search_display_column = display_length(state,
    284 			state->search, state->search_column);
    285 		if (state->search_display_column
    286 				- state->search_first_shown_column
    287 			> state->search_width - 4)
    288 		{
    289 			state->search_first_shown_column
    290 				= state->search_display_column
    291 				- state->search_width + 4;
    292 			state->search_display_column = display_length(state,
    293 				state->search, state->search_column);
    294 		}
    295 		if (add_null)
    296 			state->search[state->search_column] = 0;
    297 
    298 		next_match(state);
    299 	}
    300 }
    301 
    302 struct DrawState*
    303 join_lines(struct DrawState* state, size_t first_line, int forward)
    304 {
    305 	state->input_column	    = state->input_buffer[first_line].length;
    306 	state->input_display_column = display_length(state,
    307 		state->input_buffer[state->input_row].text,
    308 		state->input_column);
    309 	u32_strncat(state->input_buffer[first_line].text,
    310 		state->input_buffer[first_line + 1].text, MAXBUFLINE);
    311 	state->input_buffer[first_line].length
    312 		= u32_strlen(state->input_buffer[first_line].text);
    313 	if (!forward)
    314 	{
    315 		state->input_row--;
    316 		if (state->input_row < 0)
    317 			state->input_row = 0;
    318 	}
    319 	for (size_t i = first_line + 1; i < state->input_rows_count - 1; i++)
    320 	{
    321 		u32_strncpy(state->input_buffer[i].text,
    322 			state->input_buffer[i + 1].text, MAXBUFLINE);
    323 		state->input_buffer[i].length
    324 			= state->input_buffer[i + 1].length;
    325 	}
    326 	free_bufferline(state->input_buffer + state->input_rows_count - 1);
    327 	state->input_rows_count--;
    328 	struct BufferLine* newbuf = realloc(state->input_buffer,
    329 		state->input_rows_count * sizeof(struct BufferLine));
    330 	if (!newbuf)
    331 		return NULL;
    332 	state->input_buffer = newbuf;
    333 	state->dirty	    = 1;
    334 
    335 	return state;
    336 }
    337 
    338 struct BufferLine*
    339 load_info(struct DrawState* state)
    340 {
    341 	struct PoEntry* first	= &state->entries[0];
    342 	struct PoEntry* current = &state->entries[state->msgid_number - 1];
    343 	size_t msgid_num_lines	= u32_lines_in_string(current->msgid, 0, 0);
    344 	size_t msgid_plural_num_lines
    345 		= u32_lines_in_string(current->msgid_plural, 0, 0);
    346 	size_t num_lines	   = 0;
    347 	struct BufferLine* newbuf  = NULL;
    348 	struct BufferLine* pnewbuf = NULL;
    349 
    350 	for (size_t i = 0; i < state->info_rows_count; i++)
    351 		free_bufferline(state->info_buffer + i);
    352 	free(state->info_buffer);
    353 
    354 	state->info_rows_maxlen = 0;
    355 
    356 	if (current->msgid)
    357 		num_lines += msgid_num_lines + 1;
    358 	if (current->msgid_plural)
    359 	{
    360 		num_lines += msgid_plural_num_lines + 1;
    361 		if (first->plural_forms)
    362 			num_lines += 3;
    363 	}
    364 	if (current->comments)
    365 		num_lines += current->comment_lines + 1;
    366 	if (current->trans_comments)
    367 		num_lines += current->trans_comment_lines + 1;
    368 	if (current->ref_comments)
    369 		num_lines += current->ref_comment_lines + 1;
    370 	newbuf = calloc(num_lines, sizeof(struct BufferLine));
    371 	if (!newbuf)
    372 		return NULL;
    373 
    374 	pnewbuf = newbuf;
    375 	for (size_t i = 0; i < num_lines; i++)
    376 	{
    377 		init_bufferline(pnewbuf);
    378 		uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
    379 		if (!newtext)
    380 			return NULL;
    381 		pnewbuf->text = newtext;
    382 		pnewbuf++;
    383 	}
    384 
    385 	state->info_rows_count = num_lines;
    386 
    387 	pnewbuf			= newbuf;
    388 	size_t newlen		= 0;
    389 	const uint32_t* pumsgid = NULL;
    390 	if (current->msgid)
    391 	{
    392 		pumsgid = current->msgid;
    393 		newlen	= u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE);
    394 		pnewbuf->length = newlen;
    395 		if (newlen > state->info_rows_maxlen)
    396 			state->info_rows_maxlen = newlen;
    397 		pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    398 		pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    399 		pnewbuf++;
    400 		while (*pumsgid)
    401 		{
    402 			size_t len		    = 0;
    403 			const uint32_t* old_pumsgid = pumsgid;
    404 
    405 			len = u32_next_line(current->msgid, &pumsgid, 0, 0);
    406 			u32_strncpy(pnewbuf->text, old_pumsgid,
    407 				MIN(len + 1, MAXBUFLINE));
    408 			newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
    409 			pnewbuf->length = newlen;
    410 			if (newlen > state->info_rows_maxlen)
    411 				state->info_rows_maxlen = newlen;
    412 			pnewbuf++;
    413 		}
    414 	}
    415 	if (current->msgid_plural)
    416 	{
    417 		newlen		= u32_u8_strncpy(pnewbuf->text,
    418 				 "msgid_plural:", MAXBUFLINE);
    419 		pnewbuf->length = newlen;
    420 		if (newlen > state->info_rows_maxlen)
    421 			state->info_rows_maxlen = newlen;
    422 		pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    423 		pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    424 		pnewbuf++;
    425 		pumsgid = current->msgid_plural;
    426 		while (*pumsgid)
    427 		{
    428 			size_t len		    = 0;
    429 			const uint32_t* old_pumsgid = pumsgid;
    430 
    431 			len = u32_next_line(current->msgid_plural, &pumsgid, 0,
    432 				0);
    433 			u32_strncpy(pnewbuf->text, old_pumsgid,
    434 				MIN(len + 1, MAXBUFLINE));
    435 			newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE);
    436 			pnewbuf->length = newlen;
    437 			if (newlen > state->info_rows_maxlen)
    438 				state->info_rows_maxlen = newlen;
    439 			pnewbuf++;
    440 		}
    441 		if (first->plural_forms)
    442 		{
    443 			newlen		= u32_u8_strncpy(pnewbuf->text,
    444 					 "Plural forms:", MAXBUFLINE);
    445 			pnewbuf->length = newlen;
    446 			if (newlen > state->info_rows_maxlen)
    447 				state->info_rows_maxlen = newlen;
    448 			pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    449 			pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    450 			pnewbuf++;
    451 			newlen = u32_strncpy(pnewbuf->text, first->plural_forms,
    452 				MAXBUFLINE);
    453 			pnewbuf->length = newlen;
    454 			if (newlen > state->info_rows_maxlen)
    455 				state->info_rows_maxlen = newlen;
    456 			pnewbuf++;
    457 		}
    458 	}
    459 	if (current->comments)
    460 	{
    461 		newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE);
    462 		pnewbuf->length = newlen;
    463 		if (newlen > state->info_rows_maxlen)
    464 			state->info_rows_maxlen = newlen;
    465 		pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    466 		pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    467 		pnewbuf++;
    468 		for (size_t i = 0; i < current->comment_lines; i++)
    469 		{
    470 			uint32_t* comment = current->comments[i];
    471 			size_t len	  = u32_strlen(comment);
    472 			newlen		  = u32_strncpy(pnewbuf->text, comment,
    473 					   MIN(len + 1, MAXBUFLINE));
    474 			pnewbuf->length	  = newlen;
    475 			if (newlen > state->info_rows_maxlen)
    476 				state->info_rows_maxlen = newlen;
    477 			pnewbuf++;
    478 		}
    479 	}
    480 	if (current->trans_comments)
    481 	{
    482 		newlen		= u32_u8_strncpy(pnewbuf->text,
    483 				 "Translation comments:", MAXBUFLINE);
    484 		pnewbuf->length = newlen;
    485 		if (newlen > state->info_rows_maxlen)
    486 			state->info_rows_maxlen = newlen;
    487 		pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    488 		pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    489 		pnewbuf++;
    490 		for (size_t i = 0; i < current->trans_comment_lines; i++)
    491 		{
    492 			uint32_t* trans_comment = current->trans_comments[i];
    493 			size_t len		= u32_strlen(trans_comment);
    494 			newlen = u32_strncpy(pnewbuf->text, trans_comment,
    495 				MIN(len + 1, MAXBUFLINE));
    496 			pnewbuf->length = newlen;
    497 			if (newlen > state->info_rows_maxlen)
    498 				state->info_rows_maxlen = newlen;
    499 			pnewbuf++;
    500 		}
    501 	}
    502 	if (current->ref_comments)
    503 	{
    504 		newlen		= u32_u8_strncpy(pnewbuf->text,
    505 				 "References:", MAXBUFLINE);
    506 		pnewbuf->length = newlen;
    507 		if (newlen > state->info_rows_maxlen)
    508 			state->info_rows_maxlen = newlen;
    509 		pnewbuf->fg = EDIT_MSGID_HEADING_FG;
    510 		pnewbuf->bg = EDIT_MSGID_HEADING_BG;
    511 		pnewbuf++;
    512 		for (size_t i = 0; i < current->ref_comment_lines; i++)
    513 		{
    514 			char* ref_comment = current->ref_comments[i];
    515 			size_t len	  = strlen(ref_comment);
    516 			newlen = u32_u8_strncpy(pnewbuf->text, ref_comment,
    517 				MIN(len + 1, MAXBUFLINE));
    518 			pnewbuf->length = newlen;
    519 			if (newlen > state->info_rows_maxlen)
    520 				state->info_rows_maxlen = newlen;
    521 			pnewbuf++;
    522 		}
    523 	}
    524 
    525 	state->info_buffer = newbuf;
    526 
    527 	return state->info_buffer;
    528 }
    529 
    530 struct BufferLine*
    531 load_msgstr(struct DrawState* state)
    532 {
    533 	int msgstr_index	   = state->msgstr_index;
    534 	struct PoEntry* current	   = &state->entries[state->msgid_number - 1];
    535 	size_t num_lines	   = current->msgstr_count == 0
    536 			  ? 1
    537 			  : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0);
    538 	struct BufferLine* newbuf  = NULL;
    539 	struct BufferLine* pnewbuf = NULL;
    540 
    541 	for (size_t i = 0; i < state->input_rows_count; i++)
    542 		free_bufferline(state->input_buffer + i);
    543 	free(state->input_buffer);
    544 
    545 	newbuf = calloc(num_lines, sizeof(struct BufferLine));
    546 	if (!newbuf)
    547 		return NULL;
    548 
    549 	pnewbuf = newbuf;
    550 	for (size_t i = 0; i < num_lines; i++)
    551 	{
    552 		init_bufferline(pnewbuf);
    553 		uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
    554 		if (!newtext)
    555 			return NULL;
    556 		pnewbuf->text = newtext;
    557 		pnewbuf++;
    558 	}
    559 
    560 	state->input_rows_count = num_lines;
    561 
    562 	if (current->msgstr_count > 0)
    563 	{
    564 		const uint32_t* pumsgstr = *(current->msgstr + msgstr_index);
    565 		if (pumsgstr)
    566 		{
    567 			pnewbuf = newbuf;
    568 			while (*pumsgstr)
    569 			{
    570 				size_t len		     = 0;
    571 				size_t newlen		     = 0;
    572 				const uint32_t* old_pumsgstr = pumsgstr;
    573 				len = u32_next_line(*(current->msgstr
    574 							    + msgstr_index),
    575 					&pumsgstr, 0, 0);
    576 				u32_strncpy(pnewbuf->text, old_pumsgstr,
    577 					MIN(len + 1, MAXBUFLINE));
    578 				newlen		= u32_decode_tabs(pnewbuf->text,
    579 						 MAXBUFLINE);
    580 				pnewbuf->length = newlen;
    581 				pnewbuf++;
    582 			}
    583 		}
    584 		else
    585 			newbuf[0].length = 0;
    586 	}
    587 	else
    588 		newbuf[0].length = 0;
    589 
    590 	state->input_buffer = newbuf;
    591 	return state->input_buffer;
    592 }
    593 
    594 size_t
    595 next_msgid(struct DrawState* state, const int delta)
    596 {
    597 	size_t ret   = state->msgid_number;
    598 	size_t start = ret;
    599 	size_t last_non_obsolete
    600 		= state->real_msgid_count - state->obsolete_at_end_count;
    601 
    602 	while (ret < last_non_obsolete + 1)
    603 	{
    604 		ret++;
    605 		if (!state->entries[ret - 1].obsolete && ret >= start + delta)
    606 			break;
    607 	}
    608 
    609 	return ret == last_non_obsolete + 1 ? last_non_obsolete : ret;
    610 }
    611 
    612 void
    613 overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev)
    614 {
    615 	if (ev->ch == 'y')
    616 		copy_msgid_to_input(state);
    617 	state->prompt_callback = NULL;
    618 }
    619 
    620 void
    621 overwrite_paste_callback(struct DrawState* state, struct tb_event* ev)
    622 {
    623 	if (ev->ch == 'y')
    624 		paste_paste_buffer_to_input(state);
    625 	state->prompt_callback = NULL;
    626 }
    627 
    628 size_t
    629 prev_msgid(struct DrawState* state, const int delta)
    630 {
    631 	size_t ret   = state->msgid_number;
    632 	size_t start = ret;
    633 
    634 	if (start < delta)
    635 		return state->real_msgid_count ? 1 : 0;
    636 
    637 	while (ret > 1)
    638 	{
    639 		ret--;
    640 		if (!state->entries[ret - 1].obsolete && ret <= start - delta)
    641 			break;
    642 	}
    643 
    644 	return ret;
    645 }
    646 
    647 int
    648 print_error(const int code, const char* msg, ...)
    649 {
    650 	char buf[MAXBUFLINE];
    651 	va_list args;
    652 	va_start(args, msg);
    653 	vsnprintf(buf, sizeof(buf), msg, args);
    654 	va_end(args);
    655 	fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf);
    656 	return code;
    657 }
    658 
    659 void
    660 quit_callback(struct DrawState* state, struct tb_event* ev)
    661 {
    662 	if (ev->ch == 'y')
    663 		write_file(state);
    664 	state->prompt_callback = NULL;
    665 	state->running	       = 0;
    666 }
    667 
    668 size_t
    669 real_msgid_to_display_msgid(const struct DrawState* state)
    670 {
    671 	struct PoEntry* current = state->entries;
    672 	size_t ret		= 1;
    673 
    674 	if (!state->msgid_count)
    675 		return 0;
    676 
    677 	while (current != state->entries + state->msgid_number - 1)
    678 	{
    679 		if (current != state->entries + state->msgid_number - 1
    680 			&& !current->obsolete)
    681 			ret++;
    682 		current++;
    683 	}
    684 
    685 	return ret;
    686 }
    687 
    688 int
    689 cancel_close(struct DrawState* state)
    690 {
    691 	*state->error = 0;
    692 	if (state->in_prompt)
    693 	{
    694 		state->in_prompt = 0;
    695 		*state->prompt	 = 0;
    696 	}
    697 	else if (state->show_help)
    698 		state->show_help = 0;
    699 	else if (state->show_edit)
    700 		cancel_msgstr(state);
    701 	else if (state->show_search)
    702 		cancel_search(state);
    703 	else
    704 		STRLCPY(errors[ERR_EXIT_KEY], state->error, MAXBUFLINE);
    705 
    706 	return 0;
    707 }
    708 
    709 int
    710 cancel_msgstr(struct DrawState* state)
    711 {
    712 	if (!state->in_prompt && state->dirty)
    713 	{
    714 		state->in_prompt = 1;
    715 		STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE);
    716 		state->prompt_callback = cancel_callback;
    717 		return 0;
    718 	}
    719 
    720 	state->show_edit	 = 0;
    721 	state->edit_info_focused = 0;
    722 
    723 	for (size_t i = 0; i < state->input_rows_count; i++)
    724 		free_bufferline(state->input_buffer + i);
    725 	free(state->input_buffer);
    726 	state->input_buffer	= NULL;
    727 	state->input_rows_count = 0;
    728 	if (state->info_buffer)
    729 		for (size_t i = 0; i < state->info_rows_count; i++)
    730 			free_bufferline(state->info_buffer + i);
    731 	free(state->info_buffer);
    732 	state->info_buffer     = NULL;
    733 	state->info_rows_count = 0;
    734 	state->dirty	       = state->dirty_before_edit;
    735 	state->fuzzy_count     = state->fuzzy_before_edit;
    736 	switch (state->input_changed_fuzzy)
    737 	{
    738 	case SET:
    739 		state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY;
    740 		break;
    741 	case CLEARED:
    742 		state->entries[state->msgid_number - 1].flags |= FL_FUZZY;
    743 		break;
    744 	default:;
    745 	}
    746 	state->input_changed_fuzzy = UNCHANGED;
    747 	tb_hide_cursor();
    748 	return 0;
    749 }
    750 
    751 int
    752 cancel_search(struct DrawState* state)
    753 {
    754 	state->show_search = 0;
    755 	*state->search	   = 0;
    756 	tb_hide_cursor();
    757 	return 0;
    758 }
    759 
    760 int
    761 copy_msgid_to_input(struct DrawState* state)
    762 {
    763 	if (state->edit_info_focused)
    764 		return 0;
    765 
    766 	if (!state->in_prompt && *state->input_buffer->text)
    767 	{
    768 		state->in_prompt = 1;
    769 		STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE);
    770 		state->prompt_callback = overwrite_copy_msgid_callback;
    771 		return 0;
    772 	}
    773 
    774 	if (state->msgid_number == 1)
    775 	{
    776 		STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
    777 		return 0;
    778 	}
    779 
    780 	struct PoEntry* current = &state->entries[state->msgid_number - 1];
    781 	size_t msgid_num_lines	= u32_lines_in_string(current->msgid, 0, 0);
    782 	size_t lines_to_init	= state->input_rows_count < msgid_num_lines
    783 		   ? msgid_num_lines - state->input_rows_count
    784 		   : 0;
    785 	size_t lines_to_free	= state->input_rows_count > msgid_num_lines
    786 		   ? state->input_rows_count - msgid_num_lines
    787 		   : 0;
    788 	for (size_t i = 0; i < lines_to_free; i++)
    789 		free_bufferline(
    790 			state->input_buffer + state->input_rows_count - i - 1);
    791 	struct BufferLine* newbuf = realloc(state->input_buffer,
    792 		msgid_num_lines * sizeof(struct BufferLine));
    793 	const uint32_t* pmsgid	  = current->msgid;
    794 
    795 	if (!newbuf)
    796 		return 0;
    797 
    798 	struct BufferLine* pbuffer = NULL;
    799 	for (size_t i = 0; i < lines_to_init; i++)
    800 	{
    801 		pbuffer = newbuf + msgid_num_lines - 1 - i;
    802 		init_bufferline(pbuffer);
    803 		uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
    804 		if (!newtext)
    805 			return 0;
    806 		pbuffer->text = newtext;
    807 	}
    808 
    809 	pbuffer = newbuf;
    810 	while (pmsgid && *pmsgid)
    811 	{
    812 		size_t len		   = 0;
    813 		size_t newlen		   = 0;
    814 		const uint32_t* old_pmsgid = pmsgid;
    815 		len = u32_next_line(current->msgid, &pmsgid, 0, 0);
    816 		u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE));
    817 		newlen		= u32_decode_tabs(pbuffer->text, MAXBUFLINE);
    818 		pbuffer->length = newlen;
    819 		pbuffer++;
    820 	}
    821 
    822 	state->input_rows_count = msgid_num_lines;
    823 	state->input_buffer	= newbuf;
    824 	state->dirty		= 1;
    825 
    826 	return 0;
    827 }
    828 
    829 int
    830 edit_page_down(struct DrawState* state)
    831 {
    832 	if (state->edit_info_focused)
    833 	{
    834 		if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2
    835 			< state->info_rows_count - 1)
    836 			state->info_first_shown_row += (EDIT_HEIGHT - 3) / 2;
    837 		return 0;
    838 	}
    839 
    840 	if (state->dirty)
    841 		save_msgstr(state);
    842 	else /* No need to call cancel_msgstr */
    843 		state->dirty = state->dirty_before_edit;
    844 	move_list_down(state);
    845 	state->msgstr_index = 0;
    846 	return show_edit(state);
    847 }
    848 
    849 int
    850 edit_page_up(struct DrawState* state)
    851 {
    852 	if (state->edit_info_focused)
    853 	{
    854 		if (state->info_first_shown_row > (EDIT_HEIGHT - 3) / 2)
    855 			state->info_first_shown_row -= (EDIT_HEIGHT - 3) / 2;
    856 		else
    857 			move_start(state);
    858 		return 0;
    859 	}
    860 
    861 	if (state->dirty)
    862 		save_msgstr(state);
    863 	else /* No need to call cancel_msgstr */
    864 		state->dirty = state->dirty_before_edit;
    865 	move_list_up(state);
    866 	state->msgstr_index = 0;
    867 	return show_edit(state);
    868 }
    869 
    870 int
    871 erase_backwards(struct DrawState* state)
    872 {
    873 	struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
    874 	if (state->edit_info_focused)
    875 		return 1;
    876 
    877 	if (state->input_column > 0)
    878 	{
    879 		size_t i = state->input_column;
    880 		do
    881 		{
    882 			buffer_line->text[i - 1] = buffer_line->text[i];
    883 			i++;
    884 		} while (buffer_line->text[i]);
    885 		buffer_line->text[i - 1] = 0;
    886 		buffer_line->length--;
    887 		state->input_column--;
    888 		if (state->input_first_shown_column > 0)
    889 		{
    890 			const struct BufferLine* current_line
    891 				= &state->input_buffer[state->input_row];
    892 			if (display_length(state, current_line->text,
    893 				    current_line->length)
    894 				>= state->edit_width - 4)
    895 				state->input_first_shown_column--;
    896 		}
    897 		state->input_display_column = display_length(state,
    898 			state->input_buffer[state->input_row].text,
    899 			state->input_column);
    900 		if (state->input_display_column
    901 			< state->input_first_shown_column)
    902 			state->input_first_shown_column
    903 				= state->input_display_column;
    904 		state->dirty = 1;
    905 	}
    906 	else if (state->input_row > 0)
    907 	{
    908 		move_up(state);
    909 		move_end(state);
    910 		erase_forward(state);
    911 	}
    912 	return 0;
    913 }
    914 
    915 int
    916 erase_forward(struct DrawState* state)
    917 {
    918 	struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
    919 	if (state->edit_info_focused)
    920 		return 1;
    921 
    922 	if (buffer_line->text[state->input_column])
    923 	{
    924 		size_t i = state->input_column;
    925 		while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1])
    926 		{
    927 			buffer_line->text[i] = buffer_line->text[i + 1];
    928 			i++;
    929 		}
    930 		buffer_line->text[i] = 0;
    931 		buffer_line->length--;
    932 		state->dirty = 1;
    933 	}
    934 	else if (state->input_row < state->input_rows_count - 1)
    935 	{
    936 		if (!join_lines(state, state->input_row, 1))
    937 		{
    938 			STRLCPY(errors[ERR_CANT_ALLOC], state->error,
    939 				MAXBUFLINE);
    940 			state->running = 0;
    941 		}
    942 	}
    943 	return 0;
    944 }
    945 
    946 int
    947 erase_prev_word(struct DrawState* state)
    948 {
    949 	if (state->edit_info_focused)
    950 		return 1;
    951 
    952 	int i	   = state->input_column;
    953 	size_t len = state->input_buffer[state->input_row].length;
    954 
    955 	uint32_t* pline = state->input_buffer[state->input_row].text;
    956 
    957 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
    958 		i--;
    959 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
    960 		i--;
    961 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
    962 		&& !u32_is_word_boundary(pline[i - 1], 0))
    963 		i--;
    964 
    965 	int d = state->input_column - i;
    966 
    967 	for (int j = i; j < len - d; j++)
    968 		pline[j] = pline[j + d];
    969 
    970 	len -= d;
    971 	state->input_buffer[state->input_row].length = len;
    972 	pline[len]				     = 0;
    973 	state->input_column			     = i;
    974 	state->input_display_column		     = display_length(state,
    975 				 state->input_buffer[state->input_row].text,
    976 				 state->input_column);
    977 	if (state->input_display_column < state->input_first_shown_column)
    978 		state->input_first_shown_column = state->input_display_column;
    979 	state->dirty = 1;
    980 	return 0;
    981 }
    982 
    983 int
    984 erase_search_backwards(struct DrawState* state)
    985 {
    986 	if (state->search_column > 0)
    987 	{
    988 		size_t i = state->search_column;
    989 		do
    990 		{
    991 			state->search[i - 1] = state->search[i];
    992 			i++;
    993 		} while (state->search[i]);
    994 		state->search[i - 1] = 0;
    995 		state->search_column--;
    996 		if (state->search_column < state->search_first_shown_column)
    997 		{
    998 			if (state->search_column > state->search_width - 4)
    999 				state->search_first_shown_column
   1000 					= state->search_column
   1001 					- state->search_width + 4;
   1002 			else
   1003 				state->search_first_shown_column = 0;
   1004 		}
   1005 		state->search_display_column = display_length(state,
   1006 			state->search, state->search_column);
   1007 
   1008 		return next_match(state);
   1009 	}
   1010 	return 0;
   1011 }
   1012 
   1013 int
   1014 erase_search_forward(struct DrawState* state)
   1015 {
   1016 	if (state->search[state->search_column])
   1017 	{
   1018 		size_t i = state->search_column;
   1019 		while (i < MAXSEARCH - 2 && state->search[i + 1])
   1020 		{
   1021 			state->search[i] = state->search[i + 1];
   1022 			i++;
   1023 		}
   1024 		state->search[i] = 0;
   1025 
   1026 		return next_match(state);
   1027 	}
   1028 	return 0;
   1029 }
   1030 
   1031 int
   1032 erase_search_prev_word(struct DrawState* state)
   1033 {
   1034 	int i	   = state->search_column;
   1035 	size_t len = u32_strlen(state->search);
   1036 
   1037 	uint32_t* pline = state->search;
   1038 
   1039 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
   1040 		i--;
   1041 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
   1042 		i--;
   1043 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
   1044 		&& !u32_is_word_boundary(pline[i - 1], 0))
   1045 		i--;
   1046 
   1047 	int d = state->search_column - i;
   1048 
   1049 	for (int j = i; j < len - d; j++)
   1050 		pline[j] = pline[j + d];
   1051 
   1052 	len -= state->search_column - i;
   1053 	pline[len]	     = 0;
   1054 	state->search_column = i;
   1055 	state->search_display_column
   1056 		= display_length(state, state->search, state->search_column);
   1057 
   1058 	if (state->search_column < state->search_first_shown_column)
   1059 	{
   1060 		if (state->search_column > state->search_width - 4)
   1061 			state->search_first_shown_column = state->search_column
   1062 				- state->search_width + 4;
   1063 		else
   1064 			state->search_first_shown_column = 0;
   1065 	}
   1066 	state->search_display_column
   1067 		= display_length(state, state->search, state->search_column);
   1068 
   1069 	return next_match(state);
   1070 }
   1071 
   1072 int
   1073 erase_search_to_end(struct DrawState* state)
   1074 {
   1075 	state->search[state->search_column] = 0;
   1076 	move_search_end(state);
   1077 	return next_match(state);
   1078 }
   1079 
   1080 int
   1081 erase_search_to_start(struct DrawState* state)
   1082 {
   1083 	size_t i   = state->search_column;
   1084 	size_t len = u32_strlen(state->search);
   1085 	do
   1086 	{
   1087 		state->search[i - state->search_column] = state->search[i];
   1088 		i++;
   1089 	} while (state->search[i]);
   1090 	state->search[len - state->search_column] = 0;
   1091 	move_search_start(state);
   1092 	return next_match(state);
   1093 }
   1094 
   1095 int
   1096 erase_to_end(struct DrawState* state)
   1097 {
   1098 	struct BufferLine* buffer_line = NULL;
   1099 
   1100 	if (state->edit_info_focused)
   1101 		return 1;
   1102 
   1103 	buffer_line = &state->input_buffer[state->input_row];
   1104 	buffer_line->text[state->input_column] = 0;
   1105 	buffer_line->length		       = state->input_column;
   1106 	move_end(state);
   1107 	state->dirty = 1;
   1108 	return 0;
   1109 }
   1110 
   1111 int
   1112 erase_to_start(struct DrawState* state)
   1113 {
   1114 	size_t i		       = state->input_column;
   1115 	struct BufferLine* buffer_line = &state->input_buffer[state->input_row];
   1116 	size_t len		       = buffer_line->length;
   1117 
   1118 	if (state->edit_info_focused)
   1119 		return 1;
   1120 
   1121 	do
   1122 	{
   1123 		buffer_line->text[i - state->input_column]
   1124 			= buffer_line->text[i];
   1125 		i++;
   1126 	} while (buffer_line->text[i]);
   1127 	buffer_line->text[len - state->input_column] = 0;
   1128 	buffer_line->length = len - state->input_column;
   1129 	move_start(state);
   1130 	state->dirty = 1;
   1131 	return 0;
   1132 }
   1133 
   1134 int
   1135 exit_program(struct DrawState* state)
   1136 {
   1137 	if (state->show_edit)
   1138 	{
   1139 		STRLCPY(errors[ERR_DLG_OPEN], state->error, MAXBUFLINE);
   1140 		return 0;
   1141 	}
   1142 	if (state->dirty)
   1143 	{
   1144 		state->in_prompt = 1;
   1145 		STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE);
   1146 		state->prompt_callback = quit_callback;
   1147 	}
   1148 	else
   1149 		state->running = 0;
   1150 	*state->error = 0;
   1151 	return 0;
   1152 }
   1153 
   1154 int
   1155 hide_search(struct DrawState* state)
   1156 {
   1157 	state->show_search = 0;
   1158 	tb_hide_cursor();
   1159 	return 0;
   1160 }
   1161 
   1162 int
   1163 insert_line(struct DrawState* state)
   1164 {
   1165 	struct BufferLine* newbuf = NULL;
   1166 
   1167 	if (state->edit_info_focused)
   1168 		return 1;
   1169 
   1170 	state->input_rows_count++;
   1171 	newbuf = realloc(state->input_buffer,
   1172 		state->input_rows_count * sizeof(struct BufferLine));
   1173 	if (!newbuf)
   1174 	{
   1175 		STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
   1176 		state->running = 0;
   1177 		return 0;
   1178 	}
   1179 
   1180 	state->input_buffer = newbuf;
   1181 	init_bufferline(&state->input_buffer[state->input_rows_count - 1]);
   1182 	uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
   1183 	if (!newtext)
   1184 	{
   1185 		STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
   1186 		state->running = 0;
   1187 		return 0;
   1188 	}
   1189 	state->input_buffer[state->input_rows_count - 1].text = newtext;
   1190 	for (size_t i = state->input_rows_count - 1; i > state->input_row + 1;
   1191 		i--)
   1192 	{
   1193 		u32_strncpy(state->input_buffer[i].text,
   1194 			state->input_buffer[i - 1].text, MAXBUFLINE);
   1195 		state->input_buffer[i].length
   1196 			= state->input_buffer[i - 1].length;
   1197 	}
   1198 	u32_strncpy(state->input_buffer[state->input_row + 1].text,
   1199 		state->input_buffer[state->input_row].text + state->input_column,
   1200 		MAXBUFLINE);
   1201 	state->input_buffer[state->input_row + 1].length
   1202 		= u32_strlen(state->input_buffer[state->input_row + 1].text);
   1203 	state->input_buffer[state->input_row].text[state->input_column] = 0;
   1204 	state->input_buffer[state->input_row].length
   1205 		= u32_strlen(state->input_buffer[state->input_row].text);
   1206 
   1207 	state->input_row++;
   1208 	if (state->input_row
   1209 		> state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2)
   1210 		state->input_first_shown_row
   1211 			= state->input_row - (EDIT_HEIGHT - 3) / 2;
   1212 	move_start(state);
   1213 	state->dirty = 1;
   1214 
   1215 	return 0;
   1216 }
   1217 
   1218 int
   1219 insert_search_space(struct DrawState* state)
   1220 {
   1221 	insert_search_char(state, (uint32_t)L' ');
   1222 	return 0;
   1223 }
   1224 
   1225 int
   1226 insert_search_tab(struct DrawState* state)
   1227 {
   1228 	insert_search_char(state, (uint32_t)L'\t');
   1229 	return 0;
   1230 }
   1231 
   1232 int
   1233 insert_space(struct DrawState* state)
   1234 {
   1235 	insert_char(state, (uint32_t)L' ');
   1236 	return 0;
   1237 }
   1238 
   1239 int
   1240 insert_tab(struct DrawState* state)
   1241 {
   1242 	insert_char(state, (uint32_t)L'\t');
   1243 	return 0;
   1244 }
   1245 
   1246 int
   1247 move_end(struct DrawState* state)
   1248 {
   1249 	if (state->edit_info_focused)
   1250 	{
   1251 		if (state->info_rows_count > (EDIT_HEIGHT - 3) / 2)
   1252 			state->info_first_shown_row = state->info_rows_count
   1253 				- (EDIT_HEIGHT - 3) / 2;
   1254 		state->info_first_shown_column = 0;
   1255 		return 0;
   1256 	}
   1257 
   1258 	size_t len	    = state->input_buffer[state->input_row].length;
   1259 	state->input_column = len;
   1260 	size_t dlen	    = display_length(state,
   1261 			state->input_buffer[state->input_row].text,
   1262 			state->input_column);
   1263 	state->input_display_column = dlen;
   1264 	if (dlen > state->edit_width - 4)
   1265 		state->input_first_shown_column = dlen - state->edit_width + 4;
   1266 	else
   1267 		state->input_first_shown_column = 0;
   1268 	return 0;
   1269 }
   1270 
   1271 int
   1272 move_down(struct DrawState* state)
   1273 {
   1274 	if (state->edit_info_focused)
   1275 	{
   1276 		if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2
   1277 			< state->info_rows_count)
   1278 			state->info_first_shown_row++;
   1279 		return 0;
   1280 	}
   1281 
   1282 	if (state->input_row < state->input_rows_count - 1)
   1283 		state->input_row++;
   1284 	if (state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2
   1285 		< state->input_row)
   1286 		state->input_first_shown_row
   1287 			= state->input_row - (EDIT_HEIGHT - 3) / 2;
   1288 	if (state->saved_column == -1)
   1289 		state->saved_column = state->input_display_column;
   1290 	size_t dlen = display_length(state,
   1291 		state->input_buffer[state->input_row].text,
   1292 		state->input_buffer[state->input_row].length);
   1293 	if (dlen < state->saved_column)
   1294 		state->input_display_column = dlen;
   1295 	else
   1296 		state->input_display_column = state->saved_column;
   1297 	state->input_column = input_length(state,
   1298 		state->input_buffer[state->input_row].text,
   1299 		state->input_display_column);
   1300 	if (state->input_first_shown_column > state->input_display_column)
   1301 		state->input_first_shown_column = state->input_display_column;
   1302 	if (state->input_first_shown_column + state->edit_width - 4
   1303 		< state->input_display_column)
   1304 		state->input_first_shown_column
   1305 			= state->input_display_column - state->edit_width + 4;
   1306 	state->input_display_column = display_length(state,
   1307 		state->input_buffer[state->input_row].text,
   1308 		state->input_column);
   1309 	return 0;
   1310 }
   1311 
   1312 int
   1313 move_left(struct DrawState* state)
   1314 {
   1315 	if (state->edit_info_focused)
   1316 	{
   1317 		if (state->info_first_shown_column > 0)
   1318 			state->info_first_shown_column--;
   1319 		return 0;
   1320 	}
   1321 
   1322 	if (state->input_column == 0)
   1323 		return 0;
   1324 
   1325 	if (state->input_buffer[state->input_row].text[state->input_column - 1]
   1326 		== '\t')
   1327 		state->input_display_column = display_length(state,
   1328 			state->input_buffer[state->input_row].text,
   1329 			state->input_column - 1);
   1330 	else
   1331 		state->input_display_column--;
   1332 	state->input_column--;
   1333 
   1334 	if (state->input_display_column < state->input_first_shown_column)
   1335 		state->input_first_shown_column = state->input_display_column;
   1336 	return 0;
   1337 }
   1338 
   1339 int
   1340 move_list_down(struct DrawState* state)
   1341 {
   1342 	goto_msgid(state, next_msgid(state, 1));
   1343 	return 0;
   1344 }
   1345 
   1346 int
   1347 move_list_end(struct DrawState* state)
   1348 {
   1349 	goto_msgid(state,
   1350 		next_msgid(state,
   1351 			state->real_msgid_count - state->msgid_number));
   1352 	return 0;
   1353 }
   1354 
   1355 int
   1356 move_list_page_down(struct DrawState* state)
   1357 {
   1358 	*state->error = 0;
   1359 	goto_msgid(state, next_msgid(state, state->maxy - 2));
   1360 	return 0;
   1361 }
   1362 
   1363 int
   1364 move_list_page_up(struct DrawState* state)
   1365 {
   1366 	*state->error = 0;
   1367 	goto_msgid(state, prev_msgid(state, state->maxy - 2));
   1368 	return 0;
   1369 }
   1370 
   1371 int
   1372 move_list_start(struct DrawState* state)
   1373 {
   1374 	goto_msgid(state, 1);
   1375 	return 0;
   1376 }
   1377 
   1378 int
   1379 move_list_up(struct DrawState* state)
   1380 {
   1381 	goto_msgid(state, prev_msgid(state, 1));
   1382 	return 0;
   1383 }
   1384 
   1385 int
   1386 move_next_word(struct DrawState* state)
   1387 {
   1388 	if (state->edit_info_focused)
   1389 		return 1;
   1390 
   1391 	int i		= state->input_column;
   1392 	uint32_t* pline = state->input_buffer[state->input_row].text;
   1393 	size_t len	= state->input_buffer[state->input_row].length;
   1394 
   1395 	while (i < len && !u32_is_word_boundary(pline[i], 0))
   1396 		i++;
   1397 	while (i < len && u32_is_word_boundary(pline[i], 0))
   1398 		i++;
   1399 	state->input_column	    = i;
   1400 	state->input_display_column = display_length(state,
   1401 		state->input_buffer[state->input_row].text,
   1402 		state->input_column);
   1403 	if (state->input_display_column - state->input_first_shown_column
   1404 		> state->edit_width - 4)
   1405 		state->input_first_shown_column
   1406 			= state->input_display_column - state->edit_width + 4;
   1407 	return 0;
   1408 }
   1409 
   1410 int
   1411 move_prev_word(struct DrawState* state)
   1412 {
   1413 	if (state->edit_info_focused)
   1414 		return 1;
   1415 
   1416 	int i		= state->input_column;
   1417 	uint32_t* pline = state->input_buffer[state->input_row].text;
   1418 
   1419 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
   1420 		i--;
   1421 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
   1422 		i--;
   1423 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
   1424 		&& !u32_is_word_boundary(pline[i - 1], 0))
   1425 		i--;
   1426 
   1427 	state->input_column	    = i;
   1428 	state->input_display_column = display_length(state,
   1429 		state->input_buffer[state->input_row].text,
   1430 		state->input_column);
   1431 	if (state->input_display_column < state->input_first_shown_column)
   1432 		state->input_first_shown_column = state->input_display_column;
   1433 	return 0;
   1434 }
   1435 
   1436 int
   1437 move_right(struct DrawState* state)
   1438 {
   1439 	if (state->edit_info_focused)
   1440 	{
   1441 		if (state->info_first_shown_column + state->edit_width - 3
   1442 			< state->info_rows_maxlen + 1)
   1443 			state->info_first_shown_column++;
   1444 		return 0;
   1445 	}
   1446 
   1447 	size_t len = state->input_buffer[state->input_row].length;
   1448 	if (state->input_column == len)
   1449 		return 0;
   1450 
   1451 	if (state->input_buffer[state->input_row].text[state->input_column]
   1452 		== '\t')
   1453 		state->input_display_column = display_length(state,
   1454 			state->input_buffer[state->input_row].text,
   1455 			state->input_column + 1);
   1456 	else
   1457 		state->input_display_column++;
   1458 	state->input_column++;
   1459 
   1460 	if (state->input_display_column - state->input_first_shown_column
   1461 		> state->edit_width - 4)
   1462 		state->input_first_shown_column
   1463 			= state->input_display_column - state->edit_width + 4;
   1464 	return 0;
   1465 }
   1466 
   1467 int
   1468 move_search_end(struct DrawState* state)
   1469 {
   1470 	size_t len	     = u32_strlen(state->search);
   1471 	state->search_column = len;
   1472 	size_t dlen
   1473 		= display_length(state, state->search, state->search_column);
   1474 	state->search_display_column = dlen;
   1475 	if (dlen > state->search_width - 4)
   1476 		state->search_first_shown_column
   1477 			= dlen - state->search_width + 4;
   1478 	else
   1479 		state->search_first_shown_column = 0;
   1480 	return 0;
   1481 }
   1482 
   1483 int
   1484 move_search_left(struct DrawState* state)
   1485 {
   1486 	if (state->search_column == 0)
   1487 		return 0;
   1488 
   1489 	if (state->search[state->search_column - 1] == '\t')
   1490 		state->search_display_column = display_length(state,
   1491 			state->search, state->search_column - 1);
   1492 	else
   1493 		state->search_display_column--;
   1494 	state->search_column--;
   1495 
   1496 	if (state->search_display_column < state->search_first_shown_column)
   1497 		state->search_first_shown_column = state->search_display_column;
   1498 	return 0;
   1499 }
   1500 
   1501 int
   1502 move_search_next_word(struct DrawState* state)
   1503 {
   1504 	int i		= state->search_column;
   1505 	uint32_t* pline = state->search;
   1506 	size_t len	= u32_strlen(state->search);
   1507 
   1508 	while (i < len && !u32_is_word_boundary(pline[i], 0))
   1509 		i++;
   1510 	while (i < len && u32_is_word_boundary(pline[i], 0))
   1511 		i++;
   1512 	state->search_column = i;
   1513 	state->search_display_column
   1514 		= display_length(state, state->search, state->search_column);
   1515 
   1516 	if (state->search_display_column - state->search_first_shown_column
   1517 		> state->search_width - 4)
   1518 	{
   1519 		state->search_first_shown_column = state->search_display_column
   1520 			- state->search_width + 4;
   1521 		state->search_display_column = display_length(state,
   1522 			state->search, state->search_column);
   1523 	}
   1524 	return 0;
   1525 }
   1526 
   1527 int
   1528 move_search_prev_word(struct DrawState* state)
   1529 {
   1530 	int i		= state->search_column;
   1531 	uint32_t* pline = state->search;
   1532 
   1533 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
   1534 		i--;
   1535 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
   1536 		i--;
   1537 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
   1538 		&& !u32_is_word_boundary(pline[i - 1], 0))
   1539 		i--;
   1540 
   1541 	state->search_column = i;
   1542 	state->search_display_column
   1543 		= display_length(state, state->search, state->search_column);
   1544 
   1545 	if (state->search_column < state->search_first_shown_column)
   1546 	{
   1547 		if (state->search_column > state->search_width - 4)
   1548 			state->search_first_shown_column = state->search_column
   1549 				- state->search_width + 4;
   1550 		else
   1551 			state->search_first_shown_column = 0;
   1552 	}
   1553 	state->search_display_column
   1554 		= display_length(state, state->search, state->search_column);
   1555 	return 0;
   1556 }
   1557 
   1558 int
   1559 move_search_right(struct DrawState* state)
   1560 {
   1561 	size_t len = u32_strlen(state->search);
   1562 	if (state->search_column == len)
   1563 		return 0;
   1564 
   1565 	if (state->search[state->search_column] == '\t')
   1566 		state->search_display_column = display_length(state,
   1567 			state->search, state->search_column + 1);
   1568 	else
   1569 		state->search_display_column++;
   1570 	state->search_column++;
   1571 
   1572 	if (state->search_display_column - state->search_first_shown_column
   1573 		> state->search_width - 4)
   1574 		state->search_first_shown_column = state->search_display_column
   1575 			- state->search_width + 4;
   1576 	return 0;
   1577 }
   1578 
   1579 int
   1580 move_search_start(struct DrawState* state)
   1581 {
   1582 	state->search_column		 = 0;
   1583 	state->search_display_column	 = 0;
   1584 	state->search_first_shown_column = 0;
   1585 	return 0;
   1586 }
   1587 
   1588 int
   1589 move_start(struct DrawState* state)
   1590 {
   1591 	if (state->edit_info_focused)
   1592 	{
   1593 		state->info_first_shown_row    = 0;
   1594 		state->info_first_shown_column = 0;
   1595 		return 0;
   1596 	}
   1597 
   1598 	state->input_column		= 0;
   1599 	state->input_display_column	= 0;
   1600 	state->input_first_shown_column = 0;
   1601 	return 0;
   1602 }
   1603 
   1604 int
   1605 move_up(struct DrawState* state)
   1606 {
   1607 	if (state->edit_info_focused)
   1608 	{
   1609 		if (state->info_first_shown_row > 0)
   1610 			state->info_first_shown_row--;
   1611 		return 0;
   1612 	}
   1613 
   1614 	if (state->input_row > 0)
   1615 		state->input_row--;
   1616 	if (state->input_first_shown_row > state->input_row)
   1617 		state->input_first_shown_row = state->input_row;
   1618 	if (state->saved_column == -1)
   1619 		state->saved_column = state->input_display_column;
   1620 	size_t dlen = display_length(state,
   1621 		state->input_buffer[state->input_row].text,
   1622 		state->input_buffer[state->input_row].length);
   1623 	if (dlen < state->saved_column)
   1624 		state->input_display_column = dlen;
   1625 	else
   1626 		state->input_display_column = state->saved_column;
   1627 	state->input_column = input_length(state,
   1628 		state->input_buffer[state->input_row].text,
   1629 		state->input_display_column);
   1630 	if (state->input_first_shown_column > state->input_display_column)
   1631 		state->input_first_shown_column = state->input_display_column;
   1632 	if (state->input_first_shown_column + state->edit_width - 4
   1633 		< state->input_display_column)
   1634 		state->input_first_shown_column
   1635 			= state->input_display_column - state->edit_width + 4;
   1636 	state->input_display_column = display_length(state,
   1637 		state->input_buffer[state->input_row].text,
   1638 		state->input_column);
   1639 	return 0;
   1640 }
   1641 
   1642 int
   1643 next_fuzzy(struct DrawState* state)
   1644 {
   1645 	if (state->msgid_count == 0)
   1646 		return 0;
   1647 
   1648 	*state->error = 0;
   1649 
   1650 	struct PoEntry* current = NULL;
   1651 	for (size_t e = state->msgid_number; e < state->msgid_count; e++)
   1652 	{
   1653 		current = state->entries + e;
   1654 		if (current->flags & FL_FUZZY)
   1655 		{
   1656 			int saved_show_edit = state->show_edit;
   1657 			if (saved_show_edit)
   1658 			{
   1659 				if (state->dirty)
   1660 					save_msgstr(state);
   1661 				else /* No need to call cancel_msgstr */
   1662 					state->dirty = state->dirty_before_edit;
   1663 			}
   1664 
   1665 			goto_msgid(state, e + 1);
   1666 
   1667 			if (saved_show_edit)
   1668 			{
   1669 				state->msgstr_index = 0;
   1670 				show_edit(state);
   1671 			}
   1672 
   1673 			return 0;
   1674 		}
   1675 	}
   1676 	return 0;
   1677 }
   1678 
   1679 int
   1680 next_match(struct DrawState* state)
   1681 {
   1682 	if (state->msgid_count == 0 || !state->search || !*state->search)
   1683 		return 0;
   1684 
   1685 	*state->error		= 0;
   1686 	struct PoEntry* current = NULL;
   1687 	for (size_t e = state->msgid_number; e < state->real_msgid_count; e++)
   1688 	{
   1689 		current = state->entries + e;
   1690 		if (current->obsolete)
   1691 			continue;
   1692 		if ((current->msgid && *current->msgid
   1693 			    && u32_strstr(current->msgid, state->search))
   1694 			|| (current->msgstr && *current->msgstr
   1695 				&& **current->msgstr
   1696 				&& u32_strstr(*current->msgstr, state->search)))
   1697 		{
   1698 			goto_msgid(state, e + 1);
   1699 			return 0;
   1700 		}
   1701 	}
   1702 	return 0;
   1703 }
   1704 
   1705 int
   1706 next_plural(struct DrawState* state)
   1707 {
   1708 	struct PoEntry* current = NULL;
   1709 	if (state->edit_info_focused)
   1710 		return 1;
   1711 	current = &state->entries[state->msgid_number - 1];
   1712 	save_msgstr(state);
   1713 	if (state->msgstr_index + 1 < current->msgstr_count)
   1714 		state->msgstr_index++;
   1715 	show_edit(state);
   1716 	return 0;
   1717 }
   1718 
   1719 int
   1720 next_untranslated(struct DrawState* state)
   1721 {
   1722 	if (state->msgid_count == 0)
   1723 		return 0;
   1724 
   1725 	*state->error = 0;
   1726 
   1727 	struct PoEntry* current = NULL;
   1728 	for (size_t e = state->msgid_number; e < state->msgid_count; e++)
   1729 	{
   1730 		current = state->entries + e;
   1731 		if (!current->msgstr || !*current->msgstr
   1732 			|| u32_strlen(*current->msgstr) == 0)
   1733 		{
   1734 			int saved_show_edit = state->show_edit;
   1735 			if (saved_show_edit)
   1736 			{
   1737 				if (state->dirty)
   1738 					save_msgstr(state);
   1739 				else /* No need to call cancel_msgstr */
   1740 					state->dirty = state->dirty_before_edit;
   1741 			}
   1742 
   1743 			goto_msgid(state, e + 1);
   1744 
   1745 			if (saved_show_edit)
   1746 			{
   1747 				state->msgstr_index = 0;
   1748 				show_edit(state);
   1749 			}
   1750 
   1751 			return 0;
   1752 		}
   1753 	}
   1754 	return 0;
   1755 }
   1756 
   1757 int
   1758 paste_paste_buffer_to_input(struct DrawState* state)
   1759 {
   1760 	if (state->edit_info_focused)
   1761 		return 1;
   1762 
   1763 	if (!state->in_prompt && *state->input_buffer->text)
   1764 	{
   1765 		state->in_prompt = 1;
   1766 		STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE);
   1767 		state->prompt_callback = overwrite_paste_callback;
   1768 		return 0;
   1769 	}
   1770 
   1771 	if (state->msgid_number == 1)
   1772 	{
   1773 		STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
   1774 		return 0;
   1775 	}
   1776 
   1777 	size_t lines_to_init	  = 0;
   1778 	size_t lines_to_free	  = 0;
   1779 	struct BufferLine* pinput = NULL;
   1780 	struct BufferLine* newbuf = NULL;
   1781 	struct BufferLine* ppaste = NULL;
   1782 
   1783 	if (!state->paste_buffer || !(*state->paste_buffer).text
   1784 		|| !*(*state->paste_buffer).text)
   1785 	{
   1786 		/* leave one row */
   1787 		for (size_t i = 1; i < state->input_rows_count; i++)
   1788 			free_bufferline(state->input_buffer + i);
   1789 		state->input_buffer		= realloc(state->input_buffer,
   1790 				    sizeof(struct BufferLine));
   1791 		*(state->input_buffer->text)	= 0;
   1792 		state->input_column		= 0;
   1793 		state->input_display_column	= 0;
   1794 		state->input_first_shown_column = 0;
   1795 		state->input_first_shown_row	= 0;
   1796 		state->input_row		= 0;
   1797 		state->input_rows_count		= 1;
   1798 		state->dirty			= 1;
   1799 		return 0;
   1800 	}
   1801 
   1802 	lines_to_init = state->input_rows_count < state->paste_rows_count
   1803 		? state->paste_rows_count - state->input_rows_count
   1804 		: 0;
   1805 	lines_to_free = state->input_rows_count > state->paste_rows_count
   1806 		? state->input_rows_count - state->paste_rows_count
   1807 		: 0;
   1808 
   1809 	for (size_t i = 0; i < lines_to_free; i++)
   1810 		free_bufferline(
   1811 			state->input_buffer + state->input_rows_count - i - 1);
   1812 	newbuf = realloc(state->input_buffer,
   1813 		state->paste_rows_count * sizeof(struct BufferLine));
   1814 	ppaste = state->paste_buffer;
   1815 
   1816 	if (!newbuf)
   1817 		return 0;
   1818 
   1819 	for (size_t i = 0; i < lines_to_init; i++)
   1820 	{
   1821 		pinput = newbuf + state->paste_rows_count - 1 - i;
   1822 		init_bufferline(pinput);
   1823 		uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
   1824 		if (!newtext)
   1825 			return 0;
   1826 		pinput->text = newtext;
   1827 	}
   1828 
   1829 	if (ppaste)
   1830 		for (size_t i = 0; i < state->paste_rows_count; i++)
   1831 		{
   1832 			pinput = newbuf + i;
   1833 			u32_strncpy(pinput->text, ppaste->text,
   1834 				MIN(ppaste->length + 1, MAXBUFLINE));
   1835 			pinput->length = ppaste->length;
   1836 			ppaste++;
   1837 		}
   1838 
   1839 	state->input_rows_count = state->paste_rows_count;
   1840 	state->input_buffer	= newbuf;
   1841 	state->dirty		= 1;
   1842 
   1843 	return 0;
   1844 }
   1845 
   1846 int
   1847 prev_match(struct DrawState* state)
   1848 {
   1849 	if (state->msgid_count == 0 || !state->search || !*state->search)
   1850 		return 0;
   1851 
   1852 	*state->error		= 0;
   1853 	struct PoEntry* current = NULL;
   1854 	for (size_t e = state->msgid_number - 1; e > 0; e--)
   1855 	{
   1856 		current = state->entries + e - 1;
   1857 		if ((current->msgid && *current->msgid
   1858 			    && u32_strstr(current->msgid, state->search))
   1859 			|| (current->msgstr && *current->msgstr
   1860 				&& **current->msgstr
   1861 				&& u32_strstr(*current->msgstr, state->search)))
   1862 		{
   1863 			goto_msgid(state, e);
   1864 			return 0;
   1865 		}
   1866 	}
   1867 	return 0;
   1868 }
   1869 
   1870 int
   1871 prev_plural(struct DrawState* state)
   1872 {
   1873 	if (state->edit_info_focused)
   1874 		return 1;
   1875 	save_msgstr(state);
   1876 	if (state->msgstr_index > 0)
   1877 		state->msgstr_index--;
   1878 	show_edit(state);
   1879 	return 0;
   1880 }
   1881 
   1882 int
   1883 save_msgstr(struct DrawState* state)
   1884 {
   1885 	uint32_t* to_save	= NULL;
   1886 	size_t to_save_size	= 0;
   1887 	struct PoEntry* current = NULL;
   1888 
   1889 	state->show_edit	 = 0;
   1890 	state->edit_info_focused = 0;
   1891 
   1892 	if (state->input_buffer)
   1893 	{
   1894 		to_save_size = state->input_rows_count * MAXMSGLINE;
   1895 		to_save	     = calloc(to_save_size, sizeof(uint32_t));
   1896 		if (!to_save)
   1897 		{
   1898 			STRLCPY(errors[ERR_CANT_ALLOC], state->error,
   1899 				MAXBUFLINE);
   1900 			return 0;
   1901 		}
   1902 		struct BufferLine* pinput_buffer = NULL;
   1903 		for (size_t i = 0; i < state->input_rows_count; i++)
   1904 		{
   1905 			pinput_buffer = state->input_buffer + i;
   1906 			if (i == 0)
   1907 				u32_strncpy(to_save, pinput_buffer->text,
   1908 					to_save_size * sizeof(uint32_t));
   1909 			else
   1910 			{
   1911 				u32_strncat(to_save, (uint32_t*)L"\\n",
   1912 					to_save_size * sizeof(uint32_t));
   1913 				u32_strncat(to_save, pinput_buffer->text,
   1914 					to_save_size * sizeof(uint32_t));
   1915 			}
   1916 			free_bufferline(pinput_buffer);
   1917 		}
   1918 	}
   1919 	free(state->input_buffer);
   1920 	state->input_buffer	= NULL;
   1921 	state->input_rows_count = 0;
   1922 	for (size_t i = 0; i < state->info_rows_count; i++)
   1923 		free_bufferline(state->info_buffer + i);
   1924 	free(state->info_buffer);
   1925 	state->dirty	       = (state->dirty_before_edit || state->dirty);
   1926 	state->info_rows_count = 0;
   1927 	state->info_buffer     = NULL;
   1928 	u32_encode_tabs(to_save, to_save_size * sizeof(uint32_t));
   1929 	current = &state->entries[state->msgid_number - 1];
   1930 	u32_match_msgid_ending(current->msgid, to_save,
   1931 		to_save_size * sizeof(uint32_t));
   1932 	u32_set_msgstr(current, to_save, state->msgstr_index, 0, state);
   1933 	update_warning(current->msgid, to_save, &current->warning);
   1934 	state->input_changed_fuzzy = UNCHANGED;
   1935 
   1936 	tb_hide_cursor();
   1937 	free(to_save);
   1938 	return 0;
   1939 }
   1940 
   1941 int
   1942 show_edit(struct DrawState* state)
   1943 {
   1944 	if (!load_msgstr(state) || !load_info(state))
   1945 	{
   1946 		STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
   1947 		state->running = 0;
   1948 		return 0;
   1949 	}
   1950 
   1951 	state->dirty_before_edit	= state->dirty;
   1952 	state->dirty			= 0;
   1953 	state->fuzzy_before_edit	= state->fuzzy_count;
   1954 	state->input_changed_fuzzy	= UNCHANGED;
   1955 	state->show_edit		= 1;
   1956 	state->input_row		= 0;
   1957 	state->input_first_shown_row	= 0;
   1958 	state->input_first_shown_column = 0;
   1959 	state->info_first_shown_row	= 0;
   1960 	state->info_first_shown_column	= 0;
   1961 	move_start(state);
   1962 	*state->error = 0;
   1963 	return 0;
   1964 }
   1965 
   1966 int
   1967 show_help(struct DrawState* state)
   1968 {
   1969 	state->show_help = 1;
   1970 	*state->error	 = 0;
   1971 	return 0;
   1972 }
   1973 
   1974 int
   1975 show_search(struct DrawState* state)
   1976 {
   1977 	state->show_search		 = 1;
   1978 	state->search_column		 = 0;
   1979 	state->search_display_column	 = 0;
   1980 	state->search_first_shown_column = 0;
   1981 	if (!state->search)
   1982 		state->search = calloc(MAXSEARCH, sizeof(uint32_t));
   1983 	*state->search = 0;
   1984 	tb_set_cursor((state->maxx - state->search_width + 2) / 2,
   1985 		state->maxy - 1 - SEARCH_HEIGHT + 1);
   1986 	return 0;
   1987 }
   1988 
   1989 int
   1990 toggle_fuzzy(struct DrawState* state)
   1991 {
   1992 	if (state->msgid_number == 1)
   1993 	{
   1994 		STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
   1995 		return 0;
   1996 	}
   1997 	if (state->msgid_count > 0)
   1998 	{
   1999 		state->entries[state->msgid_number - 1].flags ^= FL_FUZZY;
   2000 		if (state->entries[state->msgid_number - 1].flags & FL_FUZZY)
   2001 		{
   2002 			state->fuzzy_count++;
   2003 			if (state->input_changed_fuzzy == CLEARED)
   2004 				state->input_changed_fuzzy = UNCHANGED;
   2005 			else
   2006 				state->input_changed_fuzzy = SET;
   2007 		}
   2008 		else
   2009 		{
   2010 			state->fuzzy_count--;
   2011 			if (state->input_changed_fuzzy == SET)
   2012 				state->input_changed_fuzzy = UNCHANGED;
   2013 			else
   2014 				state->input_changed_fuzzy = CLEARED;
   2015 		}
   2016 		state->dirty = 1;
   2017 	}
   2018 	return 0;
   2019 }
   2020 
   2021 int
   2022 toggle_info_focused(struct DrawState* state)
   2023 {
   2024 	state->edit_info_focused = !state->edit_info_focused;
   2025 	return 0;
   2026 }
   2027 
   2028 int
   2029 yank_input_to_paste_buffer(struct DrawState* state)
   2030 {
   2031 	if (state->edit_info_focused)
   2032 		return 1;
   2033 
   2034 	if (state->msgid_number == 1)
   2035 	{
   2036 		STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE);
   2037 		return 0;
   2038 	}
   2039 
   2040 	size_t lines_to_init	  = 0;
   2041 	size_t lines_to_free	  = 0;
   2042 	struct BufferLine* ppaste = NULL;
   2043 	struct BufferLine* newbuf = NULL;
   2044 	struct BufferLine* pinput = NULL;
   2045 
   2046 	if (!state->input_buffer || !(*state->input_buffer).text
   2047 		|| !*(*state->input_buffer).text)
   2048 	{
   2049 		for (size_t i = 0; i < state->paste_rows_count; i++)
   2050 			free_bufferline(state->paste_buffer + i);
   2051 		free(state->paste_buffer);
   2052 		state->paste_buffer	= NULL;
   2053 		state->paste_rows_count = 0;
   2054 		return 0;
   2055 	}
   2056 
   2057 	lines_to_init = state->paste_rows_count < state->input_rows_count
   2058 		? state->input_rows_count - state->paste_rows_count
   2059 		: 0;
   2060 	lines_to_free = state->paste_rows_count > state->input_rows_count
   2061 		? state->paste_rows_count - state->input_rows_count
   2062 		: 0;
   2063 
   2064 	for (size_t i = 0; i < lines_to_free; i++)
   2065 		free_bufferline(
   2066 			state->paste_buffer + state->paste_rows_count - i - 1);
   2067 	if (!state->paste_buffer)
   2068 		newbuf = calloc(state->input_rows_count,
   2069 			sizeof(struct BufferLine));
   2070 	else
   2071 		newbuf = realloc(state->paste_buffer,
   2072 			state->input_rows_count * sizeof(struct BufferLine));
   2073 	pinput = state->input_buffer;
   2074 
   2075 	if (!newbuf)
   2076 		return 0;
   2077 
   2078 	for (size_t i = 0; i < lines_to_init; i++)
   2079 	{
   2080 		ppaste = newbuf + state->input_rows_count - 1 - i;
   2081 		init_bufferline(ppaste);
   2082 		uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t));
   2083 		if (!newtext)
   2084 			return 0;
   2085 		ppaste->text = newtext;
   2086 	}
   2087 
   2088 	if (pinput)
   2089 		for (size_t i = 0; i < state->input_rows_count; i++)
   2090 		{
   2091 			ppaste = newbuf + i;
   2092 			u32_strncpy(ppaste->text, pinput->text,
   2093 				MIN(pinput->length + 1, MAXBUFLINE));
   2094 			ppaste->length = pinput->length;
   2095 			pinput++;
   2096 		}
   2097 
   2098 	state->paste_rows_count = state->input_rows_count;
   2099 	state->paste_buffer	= newbuf;
   2100 
   2101 	return 0;
   2102 }
   2103 
   2104 int
   2105 write_file(struct DrawState* state)
   2106 {
   2107 	int result = save_file(state->entries, state->real_msgid_count,
   2108 		state->filename, state->nplurals);
   2109 	switch (result)
   2110 	{
   2111 	case SAVE_ERR_CANT_ALLOC:
   2112 		STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE);
   2113 		state->running = 0;
   2114 		break;
   2115 	case SAVE_ERR_CANT_OPEN_FILE:
   2116 		STRLCPY(errors[ERR_CANT_SAVE], state->error, MAXBUFLINE);
   2117 		state->running = 0;
   2118 		break;
   2119 	case SAVE_ERR_CANT_MOVE_FILE:
   2120 		STRLCPY(strerror(errno), state->error, MAXBUFLINE);
   2121 		state->running = 0;
   2122 		break;
   2123 	default:
   2124 		*state->error = 0;
   2125 		state->dirty  = 0;
   2126 	}
   2127 	return 0;
   2128 }
   2129 
   2130 int
   2131 main(int argc, char** argv)
   2132 {
   2133 	struct tb_event ev;
   2134 	char error[MAXBUFLINE];
   2135 	char prompt[MAXBUFLINE];
   2136 	struct DrawState state;
   2137 	char filename[MAXPATH];
   2138 	int recalculate_size = 1;
   2139 	struct stat st;
   2140 	long lineno;
   2141 	long col;
   2142 	int result;
   2143 	int dir_access;
   2144 	int dir_access_errno;
   2145 	char* dir = NULL;
   2146 
   2147 	*error	  = 0;
   2148 	*prompt	  = 0;
   2149 	*filename = 0;
   2150 
   2151 #ifdef __OpenBSD__
   2152 	if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0)
   2153 	{
   2154 		perror(PROGRAMNAME ": pledge");
   2155 		exit(1);
   2156 	}
   2157 #endif
   2158 	if (argc == 1)
   2159 		return print_error(1, "No filename given");
   2160 
   2161 	if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
   2162 	{
   2163 		printf("Usage:\t%s -h | --help | -V | --full-version | -v |"
   2164 		       " --version\n"
   2165 		       "\t%s filename.po\n",
   2166 			PROGRAMNAME, PROGRAMNAME);
   2167 		return 0;
   2168 	}
   2169 	else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
   2170 	{
   2171 		printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
   2172 		return 0;
   2173 	}
   2174 	else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--full-version"))
   2175 	{
   2176 		printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE);
   2177 		puts(COPYRIGHT);
   2178 		printf("Features set in config.h:\n");
   2179 #ifdef CREATE_BACKUPS
   2180 		printf("  CREATE_BACKUPS:\tset\n");
   2181 #else
   2182 		printf("  CREATE_BACKUPS:\tunset\n");
   2183 #endif
   2184 #ifdef MATCH_MSGSTR_ENDING
   2185 		printf("  MATCH_MSGSTR_ENDING:\tset\n");
   2186 #else
   2187 		printf("  MATCH_MSGSTR_ENDING:\tunset\n");
   2188 #endif
   2189 #ifdef PLURAL_STRING
   2190 		printf("  PLURAL_STRING:\t\"%s\"\n", PLURAL_STRING);
   2191 #else
   2192 		printf("  PLURAL_STRING:\tunset\n");
   2193 #endif
   2194 		printf("  SAVE_WRAP_WIDTH:\t%d\n", SAVE_WRAP_WIDTH);
   2195 		printf("  WRAP_FIRST_MSGSTR:\t%d\n", WRAP_FIRST_MSGSTR);
   2196 		printf("  WARN_COUNT_DOTS:\t%d\n", WARN_COUNT_DOTS);
   2197 		return 0;
   2198 	}
   2199 	else
   2200 	{
   2201 		strncpy(filename, argv[1], MAXPATH - 1);
   2202 		filename[MAXPATH - 1] = 0;
   2203 		if (!*filename)
   2204 			print_error(EINVAL, "No filename given");
   2205 	}
   2206 
   2207 	init_drawstate(&state, error, filename, prompt);
   2208 
   2209 	if (stat(filename, &st) == -1 || access(filename, R_OK | W_OK) == -1)
   2210 		goto file_error;
   2211 
   2212 	if (!S_ISREG(st.st_mode))
   2213 	{
   2214 		result = print_error(EINVAL, "Is not a regular file");
   2215 		goto general_error;
   2216 	}
   2217 
   2218 #ifdef __OpenBSD__
   2219 	if (unveil(state.filename, "rwc") < 0)
   2220 	{
   2221 		perror(PROGRAMNAME ": unveil");
   2222 		exit(1);
   2223 	}
   2224 #endif
   2225 
   2226 	dir = dirname(state.filename);
   2227 #ifdef __OpenBSD__
   2228 	if (unveil(dir, "rwc") < 0)
   2229 	{
   2230 		perror(PROGRAMNAME ": unveil");
   2231 		exit(1);
   2232 	}
   2233 #endif
   2234 #ifdef CREATE_BACKUPS
   2235 	errno = 0;
   2236 	if (lstat(state.filename, &st) != -1)
   2237 	{
   2238 		if (S_ISLNK(st.st_mode))
   2239 		{
   2240 			if (!(state.real_filename = calloc(MAXPATH, 1)))
   2241 				return print_error(1,
   2242 					"Allocation failed (out of memory?)");
   2243 			state.rfn_len = readlink(state.filename,
   2244 				state.real_filename, MAXPATH);
   2245 			if (state.rfn_len == -1)
   2246 				goto file_error;
   2247 			state.real_filename[state.rfn_len] = 0;
   2248 #ifdef __OpenBSD__
   2249 			if (unveil(state.real_filename, "rwc") < 0)
   2250 			{
   2251 				perror(PROGRAMNAME ": unveil");
   2252 				exit(1);
   2253 			}
   2254 #endif
   2255 			if (stat(state.real_filename, &st) == -1)
   2256 				goto file_error;
   2257 		}
   2258 		if (!(state.backup_filename = calloc(MAXPATH, 1)))
   2259 		{
   2260 			free(state.real_filename);
   2261 			return print_error(1,
   2262 				"Allocation failed (out of memory?)");
   2263 		}
   2264 
   2265 		/* Backup filename is the target file + suffix if the file to
   2266 		 * write is a symlink, otherwise file + suffix */
   2267 		STRLCPY(state.rfn_len > 0 ? state.real_filename : state.filename,
   2268 			state.backup_filename, MAXPATH);
   2269 		STRLCAT(backup_suffix, state.backup_filename, MAXPATH);
   2270 #ifdef __OpenBSD__
   2271 		if (unveil(state.backup_filename, "rwc") < 0)
   2272 		{
   2273 			perror(PROGRAMNAME ": unveil");
   2274 			exit(1);
   2275 		}
   2276 #endif
   2277 	}
   2278 	else
   2279 		goto file_error;
   2280 #endif /* CREATE_BACKUPS */
   2281 
   2282 	if (state.rfn_len > 0)
   2283 	{
   2284 		dir = dirname(state.real_filename);
   2285 #ifdef __OpenBSD__
   2286 		if (unveil(dir, "rwc") < 0)
   2287 		{
   2288 			perror(PROGRAMNAME ": unveil");
   2289 			exit(1);
   2290 		}
   2291 #endif
   2292 	}
   2293 
   2294 	dir_access	 = access(dir, W_OK | X_OK);
   2295 	dir_access_errno = errno;
   2296 #ifdef BACKUP_FAIL_OK
   2297 	state.backup_possible = dir_access != -1;
   2298 #else  /* !BACKUP_FAIL_OK */
   2299 	if (dir_access == -1)
   2300 	{
   2301 		errno = dir_access_errno;
   2302 		goto file_error;
   2303 	}
   2304 #endif /* BACKUP_FAIL_OK */
   2305 	if (stat(state.filename, &st) == -1)
   2306 	{
   2307 		errno = dir_access_errno;
   2308 		goto file_error;
   2309 	}
   2310 
   2311 	if (!S_ISREG(st.st_mode))
   2312 	{
   2313 		result = print_error(EINVAL, "Is not a regular file");
   2314 		goto general_error;
   2315 	}
   2316 
   2317 #ifdef __OpenBSD__
   2318 	if (unveil(NULL, NULL) < 0)
   2319 	{
   2320 		perror(PROGRAMNAME ": unveil");
   2321 		exit(1);
   2322 	}
   2323 #endif
   2324 
   2325 	if (access(state.filename, F_OK | R_OK | W_OK) == -1)
   2326 		goto file_error;
   2327 
   2328 	if (state.rfn_len > 0
   2329 		&& access(state.real_filename, F_OK | R_OK | W_OK) == -1)
   2330 		goto file_error;
   2331 
   2332 	lineno = 1;
   2333 	col    = 1;
   2334 	result = load_file(&state, &lineno, &col);
   2335 	switch (result)
   2336 	{
   2337 	case LOAD_ERR_CANT_ALLOC:
   2338 	case LOAD_ERR_P_ALLOC:
   2339 		(void)print_error(result, "Allocation failed (out of memory?)");
   2340 		goto general_error;
   2341 	case LOAD_ERR_CANT_OPEN_FILE:
   2342 		(void)print_error(result, "Cannot open file `%s'", filename);
   2343 		goto general_error;
   2344 	case LOAD_ERR_NOT_PO_FILE:
   2345 		(void)print_error(result, "Not a .po file `%s'", filename);
   2346 		goto general_error;
   2347 	case LOAD_ERR_P_INVAL:
   2348 		(void)print_error(result, "Invalid value");
   2349 		goto general_error;
   2350 	case LOAD_ERR_P_SYNTAX:
   2351 		(void)print_error(result, "%s:%ld:%ld: Syntax error", filename,
   2352 			lineno, col);
   2353 		goto general_error;
   2354 	case LOAD_ERR_NONE:
   2355 		if (*error)
   2356 		{
   2357 			result = print_error(1, error);
   2358 			goto general_error;
   2359 		}
   2360 		break;
   2361 	default:
   2362 		result = print_error(1, "Unknown error (%d)", result);
   2363 		goto general_error;
   2364 	}
   2365 	state.msgid_number	= state.msgid_count > 0 ? 1 : 0;
   2366 	state.first_shown_msgid = state.msgid_number;
   2367 	goto init_termbox;
   2368 
   2369 file_error:
   2370 	result = errno;
   2371 	perror(PROGRAMNAME);
   2372 	/* FALLTHROUGH */
   2373 
   2374 general_error:
   2375 	free(state.real_filename);
   2376 	free(state.backup_filename);
   2377 	return result;
   2378 
   2379 init_termbox:
   2380 	tb_init();
   2381 	*error	   = 0;
   2382 	state.maxx = 0;
   2383 
   2384 	while (state.running)
   2385 	{
   2386 		tb_clear();
   2387 
   2388 		if (recalculate_size)
   2389 		{
   2390 			state.maxx	   = tb_width();
   2391 			state.help_width   = calculate_help_width(&state);
   2392 			state.edit_width   = calculate_edit_width(&state);
   2393 			state.search_width = calculate_search_width(&state);
   2394 			recalculate_size   = 0;
   2395 		}
   2396 		state.maxy = tb_height();
   2397 
   2398 		if (!draw(&state))
   2399 			STRLCPY(errors[ERR_CANT_ALLOC], state.error, MAXBUFLINE);
   2400 		tb_present();
   2401 		result = tb_poll_event(&ev);
   2402 		if (result == TB_OK && ev.type == TB_EVENT_RESIZE)
   2403 			recalculate_size = 1;
   2404 		else if (result == TB_OK && ev.type == TB_EVENT_KEY)
   2405 			handle_key_event(&ev, &state);
   2406 	}
   2407 	tb_shutdown();
   2408 	if (state.error && *state.error)
   2409 		return print_error(1, state.error);
   2410 
   2411 	free_drawstate(&state);
   2412 	return 0;
   2413 }