sled

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

sled.c (75685B)


      1 /* This program is licensed under the terms of GNU GPL v3 or (at your option)
      2  * any later version. Copyright (C) 2022-2026  Страхиња Радић.
      3  * See the file LICENSE for exact copyright and license details. */
      4 
      5 #include <assert.h>
      6 #include <errno.h>
      7 #include <fcntl.h>
      8 #include <locale.h>
      9 #include <signal.h>
     10 #include <stdarg.h>
     11 #include <stdint.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <sys/stat.h>
     16 #include <unistd.h>
     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) 2022-2026  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 
     29 #define SLED_C_GLOBALS
     30 #define TB_IMPL
     31 
     32 #include "draw.h"
     33 #include "sled.h"
     34 #include "config.h"
     35 #include "limit.h"
     36 #include "util.h"
     37 #include "version.h"
     38 
     39 #define SEL_NOT_EMPTY(state)                          \
     40 	((state->sel_start.row != state->sel_end.row) \
     41 		|| (state->sel_start.col != state->sel_end.col))
     42 
     43 extern const char* errors[];
     44 extern const char* actions[];
     45 extern const char* mode_labels[];
     46 extern const char* go_to_dialog_title;
     47 extern const char* prompt_dirty;
     48 extern const char* prompt_overwrite;
     49 extern const char* prompt_reread_dirty;
     50 extern const char* replace_dialog_title;
     51 extern const char* search_dialog_title;
     52 
     53 static int
     54 cancel_close(struct DrawState* state)
     55 {
     56 	assert(state != NULL);
     57 	if (!state->error && !IN(state->mode, M_PROMPT)
     58 		&& !IN(state->mode, M_HELP))
     59 	{
     60 		if (SEL_NOT_EMPTY(state))
     61 			clear_selection(state);
     62 		else
     63 			state->error = ERR_EXIT_KEY;
     64 	}
     65 	else
     66 	{
     67 		state->error   = ERR_NONE;
     68 		*state->prompt = 0;
     69 		CLEAR(state->mode, M_PROMPT | M_HELP);
     70 	}
     71 
     72 	return 0;
     73 }
     74 
     75 static int
     76 clear_selection(struct DrawState* state)
     77 {
     78 	assert(state != NULL);
     79 	CLEAR(state->mode, M_SELECTION);
     80 	init_coord(&state->sel_start, 0, 0);
     81 	init_coord(&state->sel_end, 0, 0);
     82 	return 0;
     83 }
     84 
     85 #define MAXCOPYBUF 8192
     86 
     87 static int
     88 cp(const char* from, const char* to)
     89 {
     90 	struct stat st;
     91 	struct timespec times[2];
     92 	char buf[MAXCOPYBUF];
     93 	int fromfd, tofd;
     94 	int nread    = 0;
     95 	int nwritten = 0;
     96 	int result;
     97 	int saved_errno;
     98 
     99 	assert((from != NULL) && (to != NULL));
    100 
    101 	tofd   = 0;
    102 	fromfd = open(from, O_RDONLY);
    103 	if (fromfd < 0)
    104 		return 1;
    105 	if (fstat(fromfd, &st) < 0)
    106 	{
    107 		result = 1;
    108 		goto cp_cleanup;
    109 	}
    110 
    111 	tofd = open(to, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
    112 	if (tofd < 0)
    113 		return 1;
    114 	if (fchmod(tofd, st.st_mode) < 0)
    115 	{
    116 		result = 1;
    117 		goto cp_cleanup;
    118 	}
    119 	times[0] = st.st_atim;
    120 	times[1] = st.st_mtim;
    121 
    122 	result = 0;
    123 	while ((nread = read(fromfd, buf, MAXCOPYBUF)) > 0)
    124 	{
    125 		nwritten = write(tofd, buf, nread);
    126 		if (nwritten < nread)
    127 		{
    128 			result = 1;
    129 			goto cp_cleanup;
    130 		}
    131 	}
    132 
    133 	if (nread < 0)
    134 	{
    135 		result = 1;
    136 		goto cp_cleanup;
    137 	}
    138 
    139 	errno = 0;
    140 	if (futimens(tofd, times) < 0)
    141 		result = 1;
    142 
    143 cp_cleanup:
    144 	saved_errno = errno;
    145 	if (tofd)
    146 		close(tofd);
    147 	close(fromfd);
    148 	errno = saved_errno;
    149 	return result;
    150 }
    151 
    152 static int
    153 create_file(struct DrawState* state)
    154 {
    155 	struct BufferLine* newchunk = NULL;
    156 	uint32_t* newtext	    = NULL;
    157 
    158 	assert(state != NULL);
    159 	newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
    160 	if (!newchunk)
    161 	{
    162 		state->running = 0;
    163 		return 1;
    164 	}
    165 	for (ssize_t row = 0; row < ALLOC_DELTA; row++)
    166 	{
    167 		init_bufferline(newchunk + row);
    168 		newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
    169 		if (!newtext)
    170 		{
    171 			state->running = 0;
    172 			return 1;
    173 		}
    174 		newchunk[row].size = LINE_DEFAULT;
    175 		newchunk[row].text = newtext;
    176 	}
    177 	state->buffer	   = newchunk;
    178 	state->buffer_size = ALLOC_DELTA;
    179 	state->rows_count  = 1;
    180 	recalculate_line_numbers(state);
    181 	state->coord.row = 0;
    182 	state->dirty	 = 1;
    183 
    184 	return 0;
    185 }
    186 
    187 static int
    188 cut_to_paste(struct DrawState* state)
    189 {
    190 	assert(state != NULL);
    191 	simple_yank(state);
    192 	erase_selection(state);
    193 	position_to_selection_start(state);
    194 	clear_selection(state);
    195 	return 0;
    196 }
    197 
    198 static char*
    199 dirname(char* dir, char* path, const ssize_t dir_size)
    200 {
    201 	char* p = NULL;
    202 
    203 	assert((dir != NULL) && (path != NULL) && (dir_size > 0));
    204 	if (!*path)
    205 		return NULL;
    206 
    207 	MEMCCPY(dir, path, dir_size, p);
    208 
    209 	if (p > dir + 2 && *(p - 2) == '/')
    210 		*p = 0;
    211 	if (p > dir + 1)
    212 		p--;
    213 
    214 	while (p != dir && *p != '/')
    215 	{
    216 		p--;
    217 		if (p != dir && *p == '/')
    218 		{
    219 			*p = 0;
    220 			return dir;
    221 		}
    222 		else if (p == dir && *p == '/')
    223 			return dir;
    224 	}
    225 	dir[0] = '.';
    226 	dir[1] = 0;
    227 	return dir;
    228 }
    229 
    230 static int
    231 do_search_replace(struct DrawState* state)
    232 {
    233 	uint32_t* buf	 = NULL;
    234 	uint32_t* temp	 = NULL;
    235 	uint32_t* before = NULL;
    236 	uint32_t* found	 = NULL;
    237 	uint32_t* after	 = NULL;
    238 	/* minimum size is LINE_DEFAULT to avoid 0-allocation */
    239 	ssize_t bufsize	   = LINE_DEFAULT; /* size in uint32_t units */
    240 	ssize_t buflen	   = 0;
    241 	ssize_t old_buflen = 0;
    242 	ssize_t search_len;
    243 	ssize_t input_len;
    244 	ssize_t after_len = 0;
    245 	int start_row	  = 0;
    246 	int end_row;
    247 
    248 	assert((state != NULL) && (state->search != NULL)
    249 		&& (state->input != NULL));
    250 	search_len = u32_strlen(state->search);
    251 	input_len  = u32_strlen(state->input);
    252 	end_row	   = state->rows_count;
    253 
    254 	if (SEL_NOT_EMPTY(state))
    255 		get_selection(&start_row, NULL, &end_row, NULL, state);
    256 
    257 	for (int row = start_row; row < end_row; row++)
    258 		if (state->buffer[row].size > bufsize)
    259 			bufsize = state->buffer[row].size;
    260 
    261 	buf = calloc(bufsize, sizeof(uint32_t));
    262 	if (!buf)
    263 		goto do_search_replace_alloc_error;
    264 
    265 	for (int row = start_row; row < end_row; row++)
    266 	{
    267 		before = state->buffer[row].text;
    268 		found  = (uint32_t*)u32_strstr(before, state->search);
    269 
    270 		if (!found)
    271 			continue;
    272 
    273 		state->dirty = 1;
    274 		*buf	     = 0;
    275 		*found	     = 0;
    276 		buflen	     = u32_strlen(before);
    277 		ENSURE_SIZE(buf, temp, bufsize, buflen + 1, buflen + 1,
    278 			do_search_replace_alloc_error, uint32_t);
    279 		U32_MEMCCPY(buf, before, bufsize, temp);
    280 		after	     = found + search_len;
    281 		*(after - 1) = 0;
    282 		while (found)
    283 		{
    284 			old_buflen = buflen;
    285 			buflen += input_len;
    286 			ENSURE_SIZE(buf, temp, bufsize, buflen + 1, buflen + 1,
    287 				do_search_replace_alloc_error, uint32_t);
    288 			U32_MEMCCPY((buf + old_buflen), state->input,
    289 				input_len + 1, temp);
    290 			before = after;
    291 			found  = (uint32_t*)u32_strstr(before, state->search);
    292 			if (found) /* new found, after before */
    293 				*found = 0;
    294 			after_len  = u32_strlen(after);
    295 			old_buflen = buflen;
    296 			buflen += after_len;
    297 			ENSURE_SIZE(buf, temp, bufsize, buflen + 1, buflen + 1,
    298 				do_search_replace_alloc_error, uint32_t);
    299 			U32_MEMCCPY((buf + old_buflen), after, after_len + 1,
    300 				temp);
    301 			if (found) /* new found, after before */
    302 			{
    303 				after	     = found + search_len;
    304 				*(after - 1) = 0;
    305 			}
    306 		}
    307 		U32_SAFE_COPY(state->buffer[row].text, temp,
    308 			state->buffer[row].size, buf, bufsize,
    309 			do_search_replace_alloc_error);
    310 		state->buffer[row].length = buflen;
    311 	}
    312 	free(buf);
    313 	return 0;
    314 
    315 do_search_replace_alloc_error:
    316 	state->running = 0;
    317 	return 1;
    318 }
    319 
    320 static int
    321 enter_command_mode(struct DrawState* state)
    322 {
    323 	assert(state != NULL);
    324 	SET(state->mode, M_COMMAND);
    325 	return 0;
    326 }
    327 
    328 static int
    329 enter_insert_mode(struct DrawState* state)
    330 {
    331 	assert(state != NULL);
    332 	CLEAR(state->mode, M_COMMAND);
    333 	return 0;
    334 }
    335 
    336 static int
    337 erase_backwards(struct DrawState* state)
    338 {
    339 	struct BufferLine* buffer_line;
    340 
    341 	assert(state != NULL);
    342 	buffer_line = &state->buffer[state->coord.row];
    343 
    344 	if (SEL_NOT_EMPTY(state))
    345 	{
    346 		erase_selection(state);
    347 		position_to_selection_start(state);
    348 		clear_selection(state);
    349 		return 0;
    350 	}
    351 
    352 	if (state->coord.col > 0)
    353 	{
    354 		ssize_t i = state->coord.col;
    355 		while (i <= buffer_line->length)
    356 		{
    357 			buffer_line->text[i - 1] = buffer_line->text[i];
    358 			i++;
    359 		}
    360 		buffer_line->text[i - 1] = 0;
    361 		buffer_line->length--;
    362 		position_to(state, state->coord.row, state->coord.col - 1);
    363 		state->dirty = 1;
    364 	}
    365 	else if (state->coord.row > 0)
    366 	{
    367 		move_up(state);
    368 		move_end(state);
    369 		erase_forward(state);
    370 	}
    371 	return 0;
    372 }
    373 
    374 static int
    375 erase_forward(struct DrawState* state)
    376 {
    377 	struct BufferLine* buffer_line;
    378 
    379 	assert(state != NULL);
    380 	buffer_line = &state->buffer[state->coord.row];
    381 
    382 	if (SEL_NOT_EMPTY(state))
    383 	{
    384 		erase_selection(state);
    385 		position_to_selection_start(state);
    386 		clear_selection(state);
    387 		return 0;
    388 	}
    389 
    390 	if (buffer_line->text[state->coord.col])
    391 	{
    392 		ssize_t i = state->coord.col;
    393 		while (i + 2 < buffer_line->size && buffer_line->text[i + 1])
    394 		{
    395 			buffer_line->text[i] = buffer_line->text[i + 1];
    396 			i++;
    397 		}
    398 		buffer_line->text[i] = 0;
    399 		buffer_line->length--;
    400 		state->dirty = 1;
    401 	}
    402 	else if ((state->coord.row + 1) < state->rows_count)
    403 		simple_join_lines(state);
    404 	return 0;
    405 }
    406 
    407 static int
    408 erase_line(struct DrawState* state)
    409 {
    410 	struct BufferLine* current = NULL;
    411 	struct BufferLine* next	   = NULL;
    412 	uint32_t* ptext		   = NULL;
    413 
    414 	assert(state != NULL);
    415 	if (state->rows_count == 1)
    416 	{
    417 		*state->buffer[0].text	= 0;
    418 		state->buffer[0].length = 0;
    419 		return 0;
    420 	}
    421 	for (ssize_t row = state->coord.row; row < state->rows_count - 1; row++)
    422 	{
    423 		current = state->buffer + row;
    424 		next	= state->buffer + row + 1;
    425 		U32_SAFE_COPY(current->text, ptext, current->size, next->text,
    426 			next->size, erase_line_error);
    427 		current->length = next->length;
    428 	}
    429 	free_bufferline(&state->buffer[state->rows_count - 1]);
    430 	if ((state->coord.row + 1) == state->rows_count)
    431 		state->coord.row--;
    432 	position_to(state, state->coord.row, 0);
    433 	state->rows_count--;
    434 	state->dirty = 1;
    435 	recalculate_line_numbers(state);
    436 	return 0;
    437 
    438 erase_line_error:
    439 	state->running = 0;
    440 	return 1;
    441 }
    442 
    443 static int
    444 erase_prev_word(struct DrawState* state)
    445 {
    446 	uint32_t* pline;
    447 	ssize_t len;
    448 	int i, j, d;
    449 
    450 	assert((state != NULL) && (state->buffer != NULL)
    451 		&& (state->buffer[state->coord.row].text != NULL));
    452 	i     = state->coord.col;
    453 	len   = state->buffer[state->coord.row].length;
    454 	pline = state->buffer[state->coord.row].text;
    455 
    456 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
    457 		i--;
    458 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
    459 		i--;
    460 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
    461 		&& !u32_is_word_boundary(pline[i - 1], 0))
    462 		i--;
    463 
    464 	d = state->coord.col - i;
    465 
    466 	for (j = i; j + d < len; j++)
    467 		pline[j] = pline[j + d];
    468 
    469 	len -= d;
    470 	state->buffer[state->coord.row].length = len;
    471 	pline[len]			       = 0;
    472 	state->coord.col		       = i;
    473 	state->display_column = display_length(pline, state->coord.col);
    474 	if (state->display_column < state->first_shown.col)
    475 		state->first_shown.col = state->display_column;
    476 	state->dirty = 1;
    477 	return 0;
    478 }
    479 
    480 static int
    481 erase_selection(struct DrawState* state)
    482 {
    483 	assert(state != NULL);
    484 	if (SEL_NOT_EMPTY(state))
    485 	{
    486 		uint32_t* buf  = NULL;
    487 		uint32_t* temp = NULL;
    488 		/* Selection start row/col */
    489 		int ssr = 0;
    490 		int ssc = 0;
    491 		/* Selection end row/col */
    492 		int ser	    = 0;
    493 		int sec	    = 0;
    494 		int ser_len = 0;
    495 		ssize_t bufsize;
    496 
    497 		get_selection(&ssr, &ssc, &ser, &sec, state);
    498 
    499 		assert(ser < state->buffer_size);
    500 		ser_len = state->buffer[ser].length;
    501 		bufsize = ssc + ser_len - sec + 1;
    502 		buf	= calloc(bufsize, sizeof(uint32_t));
    503 		if (!buf)
    504 			exit(print_error(ENOMEM, "Memory allocation failed"));
    505 
    506 		U32_MEMCCPY(buf, state->buffer[ser].text + sec, bufsize, temp);
    507 		ENSURE_SIZE(state->buffer[ssr].text, temp,
    508 			state->buffer[ssr].size, bufsize, bufsize,
    509 			erase_selection_alloc_error, uint32_t);
    510 		U32_MEMCCPY((state->buffer[ssr].text + ssc), buf, bufsize, temp);
    511 
    512 		state->buffer[ssr].size = MAX(bufsize, state->buffer[ssr].size);
    513 		state->buffer[ssr].length = ssc + ser_len - sec;
    514 
    515 		if (ssr == ser)
    516 			goto erase_selection_same_line;
    517 		for (ssize_t row = 0; ser + row + 1 < state->rows_count; row++)
    518 		{
    519 			ENSURE_SIZE(state->buffer[ssr + row + 1].text, temp,
    520 				state->buffer[ssr + row + 1].size,
    521 				state->buffer[ser + row + 1].size,
    522 				state->buffer[ser + row + 1].size,
    523 				erase_selection_alloc_error, uint32_t);
    524 			U32_MEMCCPY(state->buffer[ssr + row + 1].text,
    525 				state->buffer[ser + row + 1].text,
    526 				state->buffer[ser + row + 1].size, temp);
    527 			state->buffer[ssr + row + 1].size
    528 				= MAX(state->buffer[ssr + row + 1].size,
    529 					state->buffer[ser + row + 1].size);
    530 			state->buffer[ssr + row + 1].length
    531 				= state->buffer[ser + row + 1].length;
    532 		}
    533 		for (ssize_t row = state->rows_count - (ser - ssr);
    534 			row < state->rows_count; row++)
    535 		{
    536 			*state->buffer[row].text  = 0;
    537 			state->buffer[row].length = 0;
    538 		}
    539 		state->rows_count -= ser - ssr;
    540 		recalculate_line_numbers(state);
    541 
    542 	erase_selection_same_line:
    543 		free(buf);
    544 		state->dirty = 1;
    545 	}
    546 	return 0;
    547 
    548 erase_selection_alloc_error:
    549 	state->running = 0;
    550 	return 1;
    551 }
    552 
    553 static int
    554 erase_to_end(struct DrawState* state)
    555 {
    556 	struct BufferLine* buffer_line;
    557 
    558 	assert(state != NULL);
    559 	buffer_line = &state->buffer[state->coord.row];
    560 	assert((buffer_line != NULL) && (buffer_line->text != NULL));
    561 
    562 	if (SEL_NOT_EMPTY(state))
    563 	{
    564 		erase_selection(state);
    565 		position_to_selection_start(state);
    566 		clear_selection(state);
    567 		return 0;
    568 	}
    569 
    570 	buffer_line->text[state->coord.col] = 0;
    571 	buffer_line->length		    = state->coord.col;
    572 	move_end(state);
    573 	state->dirty = 1;
    574 	return 0;
    575 }
    576 
    577 static int
    578 erase_to_start(struct DrawState* state)
    579 {
    580 	struct BufferLine* buffer_line;
    581 	ssize_t i;
    582 	ssize_t len;
    583 
    584 	assert(state != NULL);
    585 	i	    = state->coord.col;
    586 	buffer_line = &state->buffer[state->coord.row];
    587 	assert((buffer_line != NULL) && (buffer_line->text != NULL));
    588 	len = buffer_line->length;
    589 
    590 	if (SEL_NOT_EMPTY(state))
    591 	{
    592 		erase_selection(state);
    593 		position_to_selection_start(state);
    594 		clear_selection(state);
    595 		return 0;
    596 	}
    597 
    598 	do
    599 	{
    600 		assert(i < buffer_line->length);
    601 		buffer_line->text[i - state->coord.col] = buffer_line->text[i];
    602 		i++;
    603 	} while (buffer_line->text[i]);
    604 	buffer_line->text[len - state->coord.col] = 0;
    605 	buffer_line->length			  = len - state->coord.col;
    606 	move_start(state);
    607 	state->dirty = 1;
    608 	return 0;
    609 }
    610 
    611 static int
    612 exit_program(struct DrawState* state)
    613 {
    614 	char* temp = NULL;
    615 
    616 	assert(state != NULL);
    617 	if (state->dirty)
    618 	{
    619 		SET(state->mode, M_PROMPT);
    620 		MEMCCPY(state->prompt, prompt_dirty, LINE_DEFAULT, temp);
    621 		state->prompt_callback = quit_callback;
    622 	}
    623 	else
    624 		state->running = 0;
    625 	state->error = ERR_NONE;
    626 	return 0;
    627 }
    628 
    629 void
    630 get_selection(int* ssr, int* ssc, int* ser, int* sec,
    631 	const struct DrawState* state)
    632 {
    633 	assert(state != NULL);
    634 	if (state->sel_start.row == state->sel_end.row)
    635 	{
    636 		if (state->sel_start.col <= state->sel_end.col)
    637 		{
    638 			if (ssc)
    639 				*ssc = state->sel_start.col;
    640 			if (sec)
    641 				*sec = state->sel_end.col;
    642 		}
    643 		else /* Swap */
    644 		{
    645 			if (ssc)
    646 				*ssc = state->sel_end.col;
    647 			if (sec)
    648 				*sec = state->sel_start.col;
    649 		}
    650 
    651 		if (ssr)
    652 			*ssr = state->sel_start.row;
    653 		if (ser)
    654 			*ser = state->sel_end.row;
    655 	}
    656 	else if (state->sel_start.row < state->sel_end.row)
    657 	{
    658 		if (ssr)
    659 			*ssr = state->sel_start.row;
    660 		if (ser)
    661 			*ser = state->sel_end.row;
    662 		if (ssc)
    663 			*ssc = state->sel_start.col;
    664 		if (sec)
    665 			*sec = state->sel_end.col;
    666 	}
    667 	else /* Swap */
    668 	{
    669 		if (ssr)
    670 			*ssr = state->sel_end.row;
    671 		if (ser)
    672 			*ser = state->sel_start.row;
    673 		if (ssc)
    674 			*ssc = state->sel_end.col;
    675 		if (sec)
    676 			*sec = state->sel_start.col;
    677 	}
    678 }
    679 
    680 static int
    681 go_to_line_callback(struct DrawState* state)
    682 {
    683 	ssize_t lineno = 0;
    684 	ssize_t len    = 0;
    685 	char* u8_input = NULL;
    686 
    687 	assert(state != NULL);
    688 	if (*state->input)
    689 	{
    690 		len	 = u32_strlen(state->input);
    691 		u8_input = calloc(len + 1, UTF8REPMAX);
    692 		if (!u8_input)
    693 		{
    694 			state->running = 0;
    695 			return 1;
    696 		}
    697 		unicode_string_to_u8(u8_input, state->input, len + 1);
    698 		errno  = 0;
    699 		lineno = strtoul(u8_input, NULL, 10);
    700 		if (errno)
    701 			state->error = ERR_INVALID_NUM;
    702 		if (!state->error)
    703 		{
    704 			if (lineno > state->rows_count - 1)
    705 				lineno = state->rows_count;
    706 			else if (lineno < 1)
    707 				lineno = 1;
    708 			position_to(state, lineno - 1, state->coord.col);
    709 			*state->input = 0;
    710 		}
    711 		free(u8_input);
    712 	}
    713 	if (!state->error)
    714 		state->input_callback = NULL;
    715 	return 0;
    716 }
    717 
    718 static void
    719 handle_key_event(struct tb_event* ev, struct DrawState* state)
    720 {
    721 	const struct Key* current = NULL;
    722 
    723 	assert((state != NULL) && (ev != NULL));
    724 	state->error = ERR_NONE;
    725 	errno	     = 0;
    726 
    727 	if (IN(state->mode, M_PROMPT))
    728 	{
    729 		if (ev->ch && strchr("YyNn", ev->ch))
    730 		{
    731 			if (state->prompt_callback)
    732 				state->prompt_callback(state, ev);
    733 			CLEAR(state->mode, M_PROMPT);
    734 			*state->prompt = 0;
    735 		}
    736 		else if (ev->key == TB_KEY_ESC)
    737 			cancel_close(state);
    738 		return;
    739 	}
    740 
    741 	/* Remember to also edit the following condition if you
    742 	 * reassign motion keys */
    743 	if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP
    744 		&& ev->key != TB_KEY_PGDN && ev->key != TB_KEY_PGUP
    745 		&& ev->key != TB_KEY_CTRL_B && ev->key != TB_KEY_CTRL_F
    746 		&& !(IN(state->mode, M_COMMAND) && strchr("jkgGHLM", ev->ch)))
    747 		state->saved_column = -1;
    748 
    749 	if (IN(state->mode, M_INPUT))
    750 	{
    751 		for (size_t key = 0; key < LEN(input_dialog_keys); key++)
    752 		{
    753 			current = input_dialog_keys + key;
    754 			if (((current->ev.mod == ev->mod)
    755 				    && (current->ev.key == ev->key) && !ev->ch)
    756 				|| (current->ev.ch
    757 					&& (current->ev.ch == ev->ch)))
    758 				if (current->callback)
    759 				{
    760 					current->callback(state);
    761 					return;
    762 				}
    763 		}
    764 		if (ev->ch)
    765 		{
    766 			if ((IN(state->mode, M_INPUT_NUM)
    767 				    && strchr("0123456789", (const char)ev->ch))
    768 				|| !IN(state->mode, M_INPUT_NUM))
    769 				input_insert_char(state, ev->ch);
    770 			else
    771 				state->error = ERR_INPUT_NONNUM;
    772 			return;
    773 		}
    774 	}
    775 
    776 	for (size_t key = 0; key < LEN(top_level_keys); key++)
    777 	{
    778 		current = &top_level_keys[key];
    779 		if (((current->ev.mod == ev->mod)
    780 			    && (current->ev.key == ev->key) && !ev->ch)
    781 			|| (current->ev.ch && (current->ev.ch == ev->ch)))
    782 			if (current->callback)
    783 			{
    784 				current->callback(state);
    785 				return;
    786 			}
    787 	}
    788 
    789 	if (IN(state->mode, M_HELP))
    790 	{
    791 		state->error = ERR_DLG_OPEN;
    792 		return;
    793 	}
    794 
    795 	for (size_t key = 0; key < LEN(global_keys); key++)
    796 	{
    797 		current = &global_keys[key];
    798 		assert(current != NULL);
    799 		if (((current->ev.mod == ev->mod)
    800 			    && (current->ev.key == ev->key) && !ev->ch)
    801 			|| (current->ev.ch && (current->ev.ch == ev->ch)))
    802 			if (current->callback)
    803 			{
    804 				current->callback(state);
    805 				return;
    806 			}
    807 	}
    808 
    809 	if (IN(state->mode, M_COMMAND))
    810 	{
    811 		for (size_t key = 0; key < LEN(command_keys); key++)
    812 		{
    813 			current = &command_keys[key];
    814 			assert(current != NULL);
    815 			if (((current->ev.mod == ev->mod)
    816 				    && (current->ev.key == ev->key) && !ev->ch)
    817 				|| (current->ev.ch
    818 					&& (current->ev.ch == ev->ch)))
    819 				if (current->callback)
    820 				{
    821 					current->callback(state);
    822 					return;
    823 				}
    824 		}
    825 	}
    826 	else
    827 	{
    828 		for (size_t key = 0; key < LEN(insert_keys); key++)
    829 		{
    830 			current = &insert_keys[key];
    831 			if (((current->ev.mod == ev->mod)
    832 				    && (current->ev.key == ev->key) && !ev->ch)
    833 				|| (current->ev.ch
    834 					&& (current->ev.ch == ev->ch)))
    835 				if (current->callback)
    836 				{
    837 					current->callback(state);
    838 					return;
    839 				}
    840 		}
    841 		if (ev->ch)
    842 		{
    843 			insert_char(state, ev->ch);
    844 			return;
    845 		}
    846 	}
    847 
    848 	state->error = ERR_UNKNOWN_KEY;
    849 }
    850 
    851 static int
    852 input_cancel(struct DrawState* state)
    853 {
    854 	assert(state != NULL);
    855 	if (state->error)
    856 	{
    857 		state->error = ERR_NONE;
    858 		return 0;
    859 	}
    860 	*state->input = 0;
    861 	if (state->input_update_callback)
    862 		state->input_update_callback(state);
    863 	CLEAR(state->mode, M_INPUT | M_INPUT_NUM);
    864 	state->input_callback	     = NULL;
    865 	state->input_update_callback = NULL;
    866 	state->chain_dialog	     = 0;
    867 	return 0;
    868 }
    869 
    870 static int
    871 input_enter(struct DrawState* state)
    872 {
    873 	int hide_dialog = 1;
    874 
    875 	assert(state != NULL);
    876 	if (state->input_callback)
    877 	{
    878 		state->input_callback(state);
    879 		hide_dialog = (!state->search || !*state->search
    880 			|| !state->chain_dialog || *state->input != 0);
    881 		if (hide_dialog)
    882 		{
    883 			if (!state->error)
    884 				CLEAR(state->mode, M_INPUT | M_INPUT_NUM);
    885 			state->input_callback = NULL;
    886 		}
    887 	}
    888 	else if (hide_dialog)
    889 		CLEAR(state->mode, M_INPUT | M_INPUT_NUM);
    890 	if (hide_dialog)
    891 		state->input_update_callback = NULL;
    892 	return 0;
    893 }
    894 
    895 static int
    896 input_erase_backwards(struct DrawState* state)
    897 {
    898 	assert(state != NULL);
    899 	if (state->input_column > 0)
    900 	{
    901 		ssize_t i = state->input_column;
    902 		do
    903 		{
    904 			state->input[i - 1] = state->input[i];
    905 			i++;
    906 		} while (state->input[i]);
    907 		state->input[i - 1] = 0;
    908 		input_position_to(state, state->input_column - 1);
    909 		if (state->input_update_callback)
    910 			state->input_update_callback(state);
    911 	}
    912 	return 0;
    913 }
    914 
    915 static int
    916 input_erase_forward(struct DrawState* state)
    917 {
    918 	assert(state != NULL);
    919 	if (state->input)
    920 	{
    921 		ssize_t i = state->input_column;
    922 		while (i + 2 < MAXINPUT && state->input[i + 1])
    923 		{
    924 			state->input[i] = state->input[i + 1];
    925 			i++;
    926 		}
    927 		state->input[i] = 0;
    928 		if (state->input_update_callback)
    929 			state->input_update_callback(state);
    930 	}
    931 	return 0;
    932 }
    933 
    934 static int
    935 input_erase_to_end(struct DrawState* state)
    936 {
    937 	uint32_t* temp = NULL;
    938 	assert(state != NULL);
    939 	state->input[state->input_column] = 0;
    940 	input_move_end(state);
    941 	U32_MEMCCPY(state->search, state->input, MAXINPUT, temp);
    942 	if (state->input_update_callback)
    943 		state->input_update_callback(state);
    944 	return 0;
    945 }
    946 
    947 static int
    948 input_erase_to_start(struct DrawState* state)
    949 {
    950 	ssize_t i;
    951 	ssize_t len;
    952 
    953 	assert(state != NULL);
    954 	i   = state->input_column;
    955 	len = u32_strlen(state->input);
    956 
    957 	do
    958 	{
    959 		assert(i < len);
    960 		state->input[i - state->input_column] = state->input[i];
    961 		i++;
    962 	} while (state->input[i]);
    963 	state->input[len - state->input_column] = 0;
    964 	input_move_start(state);
    965 	if (state->input_update_callback)
    966 		state->input_update_callback(state);
    967 	return 0;
    968 }
    969 
    970 static int
    971 input_insert_char(struct DrawState* state, uint32_t ch)
    972 {
    973 	ssize_t len;
    974 	assert(state != NULL);
    975 	len = u32_strlen(state->input);
    976 	if (state->input_column + 1 < MAXINPUT)
    977 	{
    978 		ssize_t i    = len + 1;
    979 		int add_null = state->input[state->input_column] == 0;
    980 		while (i + 2 < MAXINPUT && i > state->input_column)
    981 		{
    982 			state->input[i] = state->input[i - 1];
    983 			i--;
    984 		}
    985 		state->input[state->input_column] = ch;
    986 		input_position_to(state, state->input_column + 1);
    987 		if (add_null)
    988 			state->input[state->input_column] = 0;
    989 		if (state->input_update_callback)
    990 			state->input_update_callback(state);
    991 	}
    992 	return 0;
    993 }
    994 
    995 static int
    996 input_insert_space(struct DrawState* state)
    997 {
    998 	return input_insert_char(state, ' ');
    999 }
   1000 
   1001 static int
   1002 input_insert_tab(struct DrawState* state)
   1003 {
   1004 	return input_insert_char(state, '\t');
   1005 }
   1006 
   1007 static int
   1008 input_move_end(struct DrawState* state)
   1009 {
   1010 	assert(state != NULL);
   1011 	input_position_to(state, u32_strlen(state->input));
   1012 	return 0;
   1013 }
   1014 
   1015 static int
   1016 input_move_left(struct DrawState* state)
   1017 {
   1018 	assert(state != NULL);
   1019 	if (state->input_column == 0)
   1020 		return 0;
   1021 	input_position_to(state, state->input_column - 1);
   1022 	return 0;
   1023 }
   1024 
   1025 static int
   1026 input_move_next_word(struct DrawState* state)
   1027 {
   1028 	uint32_t* pline;
   1029 	ssize_t len;
   1030 	int i;
   1031 
   1032 	assert((state != NULL) && (state->input != NULL));
   1033 	i     = state->input_column;
   1034 	pline = state->input;
   1035 	len   = u32_strlen(state->input);
   1036 	while (i < len && !u32_is_word_boundary(pline[i], 0))
   1037 		i++;
   1038 	while (i < len && u32_is_word_boundary(pline[i], 0))
   1039 		i++;
   1040 	input_position_to(state, i);
   1041 	return 0;
   1042 }
   1043 
   1044 static int
   1045 input_move_prev_word(struct DrawState* state)
   1046 {
   1047 	int i;
   1048 	uint32_t* pline;
   1049 
   1050 	assert((state != NULL) && (state->input != NULL));
   1051 	i     = state->input_column;
   1052 	pline = state->input;
   1053 	assert(pline != NULL);
   1054 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
   1055 		i--;
   1056 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
   1057 		i--;
   1058 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
   1059 		&& !u32_is_word_boundary(pline[i - 1], 0))
   1060 		i--;
   1061 	input_position_to(state, i);
   1062 	return 0;
   1063 }
   1064 
   1065 static int
   1066 input_move_right(struct DrawState* state)
   1067 {
   1068 	assert(state != NULL);
   1069 	input_position_to(state, state->input_column + 1);
   1070 	return 0;
   1071 }
   1072 
   1073 static int
   1074 input_move_start(struct DrawState* state)
   1075 {
   1076 	input_position_to(state, 0);
   1077 	return 0;
   1078 }
   1079 
   1080 static void
   1081 input_position_to(struct DrawState* state, const int column)
   1082 {
   1083 	ssize_t len;
   1084 
   1085 	assert((state != NULL) && (state->input != NULL));
   1086 	len		    = u32_strlen(state->input);
   1087 	state->input_column = column;
   1088 
   1089 	if (state->input_column > len)
   1090 		state->input_column = len;
   1091 	state->input_display_column
   1092 		= display_length(state->input, state->input_column);
   1093 	if (state->input_first_shown_column > state->input_display_column)
   1094 		state->input_first_shown_column = state->input_display_column;
   1095 	if (state->input_first_shown_column + (state->input_dialog_width - 4)
   1096 		< state->input_column)
   1097 		state->input_first_shown_column = state->input_display_column
   1098 			- (state->input_dialog_width - 4);
   1099 }
   1100 
   1101 static int
   1102 insert_char(struct DrawState* state, uint32_t ch)
   1103 {
   1104 	struct BufferLine* buffer_line = NULL;
   1105 	uint32_t* ptext		       = NULL;
   1106 	ssize_t i, len, size;
   1107 
   1108 	assert((state != NULL) && (state->buffer != NULL));
   1109 	ENSURE_SIZE(state->buffer[state->coord.row].text, ptext,
   1110 		state->buffer[state->coord.row].size,
   1111 		state->buffer[state->coord.row].length + 2,
   1112 		state->buffer[state->coord.row].size + LINE_ALLOC_DELTA,
   1113 		insert_char_error, uint32_t);
   1114 
   1115 	if (SEL_NOT_EMPTY(state))
   1116 	{
   1117 		erase_selection(state);
   1118 		position_to_selection_start(state);
   1119 		clear_selection(state);
   1120 	}
   1121 
   1122 	buffer_line = &state->buffer[state->coord.row];
   1123 	len	    = buffer_line->length;
   1124 	size	    = buffer_line->size;
   1125 	i	    = len;
   1126 
   1127 	assert(buffer_line->text != NULL);
   1128 	if (!IN(state->mode, M_OVERWRITE))
   1129 		while (i + 1 < size && i > state->coord.col)
   1130 		{
   1131 			buffer_line->text[i] = buffer_line->text[i - 1];
   1132 			i--;
   1133 		}
   1134 	buffer_line->text[state->coord.col] = ch;
   1135 	if (state->coord.col == len || !IN(state->mode, M_OVERWRITE))
   1136 		buffer_line->length++;
   1137 	position_to(state, state->coord.row, state->coord.col + 1);
   1138 	buffer_line->text[buffer_line->length] = 0;
   1139 
   1140 	if (!enable_wrap)
   1141 		goto insert_skip_wrap;
   1142 
   1143 	while (state->display_column >= wrap_width)
   1144 	{
   1145 		int saved_column = state->coord.col;
   1146 		buffer_line	 = state->buffer + state->coord.row;
   1147 		assert((buffer_line != NULL) && (buffer_line->text != NULL));
   1148 		state->coord.col = buffer_length(buffer_line->text, wrap_width);
   1149 		/* clang-format off */
   1150 		while (state->coord.col > 0
   1151 			&& !u32_is_word_boundary(
   1152 				buffer_line->text[state->coord.col - 1], 1))
   1153 			state->coord.col--;
   1154 		/* clang-format on */
   1155 		if (state->coord.col == 0)
   1156 		{
   1157 			state->coord.col = saved_column;
   1158 			break;
   1159 		}
   1160 		simple_insert_line(state);
   1161 		state->saved_column = -1;
   1162 		position_to(state, state->coord.row + 1,
   1163 			saved_column - state->coord.col);
   1164 	}
   1165 
   1166 insert_skip_wrap:
   1167 	state->dirty = 1;
   1168 
   1169 	return 0;
   1170 
   1171 insert_char_error:
   1172 	state->running = 0;
   1173 	return 1;
   1174 }
   1175 
   1176 static int
   1177 insert_line(struct DrawState* state)
   1178 {
   1179 	struct BufferLine* buffer_line = NULL;
   1180 
   1181 	assert(state != NULL);
   1182 	if (SEL_NOT_EMPTY(state))
   1183 	{
   1184 		erase_selection(state);
   1185 		position_to_selection_start(state);
   1186 		clear_selection(state);
   1187 	}
   1188 
   1189 	if (!enable_wrap)
   1190 		goto insert_skip_wrap;
   1191 
   1192 	while (state->display_column >= wrap_width)
   1193 	{
   1194 		int saved_column = state->coord.col;
   1195 		buffer_line	 = state->buffer + state->coord.row;
   1196 		assert((buffer_line != NULL) && (buffer_line->text != NULL));
   1197 		state->coord.col = buffer_length(buffer_line->text, wrap_width);
   1198 		/* clang-format off */
   1199 		while (state->coord.col > 0
   1200 			&& !u32_is_word_boundary(
   1201 				buffer_line->text[state->coord.col - 1], 1))
   1202 			state->coord.col--;
   1203 		/* clang-format on */
   1204 		if (state->coord.col == 0)
   1205 		{
   1206 			state->coord.col = saved_column;
   1207 			break;
   1208 		}
   1209 		simple_insert_line(state);
   1210 		state->saved_column = -1;
   1211 		position_to(state, state->coord.row + 1,
   1212 			saved_column - state->coord.col);
   1213 	}
   1214 
   1215 insert_skip_wrap:
   1216 	simple_insert_line(state);
   1217 	state->saved_column = -1;
   1218 	position_to(state, state->coord.row + 1, 0);
   1219 	state->dirty = 1;
   1220 	return 0;
   1221 }
   1222 
   1223 static int
   1224 insert_space(struct DrawState* state)
   1225 {
   1226 	return insert_char(state, ' ');
   1227 }
   1228 
   1229 static int
   1230 insert_tab(struct DrawState* state)
   1231 {
   1232 	return insert_char(state, '\t');
   1233 }
   1234 
   1235 static int
   1236 join_lines(struct DrawState* state)
   1237 {
   1238 	struct BufferLine* current = NULL;
   1239 	struct BufferLine* next	   = NULL;
   1240 	uint32_t* ptext		   = NULL;
   1241 	/* Selection start row/col */
   1242 	int ssr = 0;
   1243 	int ssc = 0;
   1244 	/* Selection end row */
   1245 	int ser	      = 0;
   1246 	int selection = 0;
   1247 	int saved_row;
   1248 
   1249 	assert(state != NULL);
   1250 	saved_row = state->coord.row;
   1251 	if (SEL_NOT_EMPTY(state))
   1252 	{
   1253 		get_selection(&ssr, &ssc, &ser, NULL, state);
   1254 		ser--;
   1255 		selection = 1;
   1256 		clear_selection(state);
   1257 	}
   1258 	else
   1259 	{
   1260 		ssr = state->coord.row;
   1261 		ssc = state->coord.col;
   1262 		ser = state->coord.row;
   1263 	}
   1264 
   1265 	if ((ssr + 1) == state->rows_count)
   1266 		return 0;
   1267 
   1268 	for (int row = ssr; row <= ser; row++)
   1269 	{
   1270 		assert(ssr + 1 < state->buffer_size);
   1271 		current = state->buffer + ssr;
   1272 		next	= state->buffer + ssr + 1;
   1273 		assert((current != NULL) && (next != NULL));
   1274 		ENSURE_SIZE(current->text, ptext, current->size,
   1275 			current->length + 3, current->length + LINE_ALLOC_DELTA,
   1276 			join_lines_error, uint32_t);
   1277 		state->coord.row = ssr;
   1278 		if (current->length > 0
   1279 			&& !u32_is_word_boundary(current->text[current->length
   1280 							 - 1],
   1281 				1)
   1282 			&& !u32_is_word_boundary(next->text[0], 1))
   1283 		{
   1284 			current->text[current->length]	   = ' ';
   1285 			current->text[current->length + 1] = 0;
   1286 			current->length++;
   1287 			state->coord.col = current->length - 1;
   1288 		}
   1289 		else
   1290 			state->coord.col = current->length;
   1291 		state->display_column
   1292 			= display_length(current->text, state->coord.col);
   1293 		simple_join_lines(state);
   1294 	}
   1295 
   1296 	state->coord.row = saved_row;
   1297 	if (selection)
   1298 		position_to(state, ssr, ssc);
   1299 
   1300 	return 0;
   1301 
   1302 join_lines_error:
   1303 	state->running = 0;
   1304 	return 1;
   1305 }
   1306 
   1307 static int
   1308 load_file(struct DrawState* state)
   1309 {
   1310 	struct BufferLine* newchunk = NULL;
   1311 	struct BufferLine* line	    = NULL;
   1312 	FILE* input		    = NULL;
   1313 	uint32_t* newtext	    = NULL;
   1314 	uint32_t* ptext		    = NULL;
   1315 	char* input_line	    = NULL;
   1316 	ssize_t input_size	    = limits[L_LINE_MAX];
   1317 	ssize_t input_len	    = 0;
   1318 	int possible_nulbyte;
   1319 
   1320 	assert(state != NULL);
   1321 	input_line = calloc(input_size, 1);
   1322 	if (!input_line)
   1323 		goto load_file_alloc_error;
   1324 	newchunk = calloc(ALLOC_DELTA, sizeof(struct BufferLine));
   1325 	if (!newchunk)
   1326 		goto load_file_alloc_error;
   1327 	for (ssize_t row = 0; row < ALLOC_DELTA; row++)
   1328 	{
   1329 		init_bufferline(newchunk + row);
   1330 		newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
   1331 		if (!newtext)
   1332 			goto load_file_alloc_error;
   1333 		newchunk[row].size = LINE_DEFAULT;
   1334 		newchunk[row].text = newtext;
   1335 	}
   1336 
   1337 	state->buffer	   = newchunk;
   1338 	state->buffer_size = ALLOC_DELTA;
   1339 	state->rows_count  = 0;
   1340 
   1341 	input = fopen(state->filename, "rt");
   1342 	if (!input)
   1343 		return 1;
   1344 
   1345 	state->action = ACT_READING;
   1346 	redraw_status(state);
   1347 
   1348 	possible_nulbyte = 0;
   1349 	while (!feof(input))
   1350 	{
   1351 		char* eol     = NULL;
   1352 		char* nulbyte = NULL;
   1353 
   1354 		if (!fgets(input_line, input_size, input))
   1355 			continue;
   1356 
   1357 		if (possible_nulbyte)
   1358 		{
   1359 			/* Definitely not EOF, NUL byte confirmed */
   1360 			state->error = ERR_BINARY;
   1361 			fclose(input);
   1362 			free(input_line);
   1363 			return 1;
   1364 		}
   1365 
   1366 		eol = strchr(input_line, '\n');
   1367 		if (eol)
   1368 			*eol = 0;
   1369 		nulbyte = strchr(input_line, 0);
   1370 
   1371 		/* We don't know if the NUL byte not being equal to
   1372 		 * (possibly NULL) eol is the result of EOF or not
   1373 		 */
   1374 		if (!open_binary && nulbyte && (nulbyte != eol))
   1375 			possible_nulbyte = 1;
   1376 
   1377 		if (state->rows_count + 1 == state->buffer_size)
   1378 		{
   1379 			state->buffer_size += ALLOC_DELTA;
   1380 			newchunk = realloc(state->buffer,
   1381 				state->buffer_size * sizeof(struct BufferLine));
   1382 			if (!newchunk)
   1383 				goto load_file_alloc_error;
   1384 			state->buffer = newchunk;
   1385 			for (ssize_t row = state->buffer_size - ALLOC_DELTA;
   1386 				row < state->buffer_size; row++)
   1387 			{
   1388 				init_bufferline(state->buffer + row);
   1389 				newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
   1390 				if (!newtext)
   1391 					goto load_file_alloc_error;
   1392 				state->buffer[row].size = LINE_DEFAULT;
   1393 				state->buffer[row].text = newtext;
   1394 			}
   1395 		}
   1396 		line	  = state->buffer + state->rows_count;
   1397 		input_len = strlen(input_line);
   1398 		ENSURE_SIZE(line->text, ptext, line->size, input_len + 1,
   1399 			input_len + LINE_ALLOC_DELTA, load_file_alloc_error,
   1400 			uint32_t);
   1401 
   1402 		U32_U8_MEMCCPY(line->text, input_line, line->size, ptext);
   1403 		line->length = u32_strlen(line->text);
   1404 		state->rows_count++;
   1405 	}
   1406 	fclose(input);
   1407 	state->action = ACT_IDLE;
   1408 
   1409 	if (state->rows_count == 0)
   1410 		state->rows_count++;
   1411 
   1412 	free(input_line);
   1413 	return 0;
   1414 
   1415 load_file_alloc_error:
   1416 	return 1;
   1417 }
   1418 
   1419 static int
   1420 move_bottom(struct DrawState* state)
   1421 {
   1422 	position_to(state, state->rows_count > 1 ? state->rows_count - 1 : 0,
   1423 		state->coord.col);
   1424 	return 0;
   1425 }
   1426 
   1427 static int
   1428 move_down(struct DrawState* state)
   1429 {
   1430 	position_to(state, state->coord.row + 1, state->coord.col);
   1431 	return 0;
   1432 }
   1433 
   1434 static int
   1435 move_end(struct DrawState* state)
   1436 {
   1437 	position_to(state, state->coord.row,
   1438 		state->buffer[state->coord.row].length);
   1439 	return 0;
   1440 }
   1441 
   1442 static int
   1443 move_left(struct DrawState* state)
   1444 {
   1445 	position_to(state, state->coord.row, state->coord.col - 1);
   1446 	return 0;
   1447 }
   1448 
   1449 static int
   1450 move_next_word(struct DrawState* state)
   1451 {
   1452 	uint32_t* pline;
   1453 	ssize_t len;
   1454 	int i;
   1455 
   1456 	assert((state != NULL) && (state->buffer != NULL));
   1457 	i     = state->coord.col;
   1458 	pline = state->buffer[state->coord.row].text;
   1459 	len   = state->buffer[state->coord.row].length;
   1460 	while (i < len && !u32_is_word_boundary(pline[i], 0))
   1461 		i++;
   1462 	while (i < len && u32_is_word_boundary(pline[i], 0))
   1463 		i++;
   1464 	position_to(state, state->coord.row, i);
   1465 	return 0;
   1466 }
   1467 
   1468 static int
   1469 move_page_down(struct DrawState* state)
   1470 {
   1471 	assert(state != NULL);
   1472 	if ((state->coord.row + state->maxy) < state->rows_count + 2)
   1473 		position_to(state, state->coord.row + state->maxy - 2,
   1474 			state->coord.col);
   1475 	else
   1476 		move_bottom(state);
   1477 	return 0;
   1478 }
   1479 
   1480 static int
   1481 move_page_up(struct DrawState* state)
   1482 {
   1483 	assert(state != NULL);
   1484 	if (state->coord.row > state->maxy - 2)
   1485 		position_to(state, state->coord.row - (state->maxy - 2),
   1486 			state->coord.col);
   1487 	else
   1488 		move_top(state);
   1489 	return 0;
   1490 }
   1491 
   1492 static int
   1493 move_prev_word(struct DrawState* state)
   1494 {
   1495 	uint32_t* pline;
   1496 	int i;
   1497 
   1498 	assert((state != NULL) && (state->buffer != NULL));
   1499 	i     = state->coord.col;
   1500 	pline = state->buffer[state->coord.row].text;
   1501 	assert(pline != NULL);
   1502 	if (i > 0 && u32_is_word_boundary(pline[i - 1], 0))
   1503 		i--;
   1504 	while (i > 0 && u32_is_word_boundary(pline[i], 0))
   1505 		i--;
   1506 	while (i > 0 && !u32_is_word_boundary(pline[i], 0)
   1507 		&& !u32_is_word_boundary(pline[i - 1], 0))
   1508 		i--;
   1509 	position_to(state, state->coord.row, i);
   1510 	return 0;
   1511 }
   1512 
   1513 static int
   1514 move_right(struct DrawState* state)
   1515 {
   1516 	position_to(state, state->coord.row, state->coord.col + 1);
   1517 	return 0;
   1518 }
   1519 
   1520 static int
   1521 move_screen_bottom(struct DrawState* state)
   1522 {
   1523 	assert(state != NULL);
   1524 	if (state->rows_count + 2 < state->maxy)
   1525 		position_to(state, state->rows_count - 1, state->coord.col);
   1526 	else
   1527 	{
   1528 		if ((state->first_shown.row + state->maxy)
   1529 			< state->rows_count + 2)
   1530 			position_to(state,
   1531 				state->first_shown.row + state->maxy - 2,
   1532 				state->coord.col);
   1533 	}
   1534 	return 0;
   1535 }
   1536 
   1537 static int
   1538 move_screen_middle(struct DrawState* state)
   1539 {
   1540 	assert(state != NULL);
   1541 	if (state->rows_count + 2 < state->maxy)
   1542 		position_to(state, state->rows_count / 2, state->coord.col);
   1543 	else
   1544 	{
   1545 		int row = state->first_shown.row + (state->maxy - 2) / 2;
   1546 		if (state->rows_count < (row + (state->maxy - 2) / 2))
   1547 			row = state->rows_count - (state->maxy - 2) / 2;
   1548 		position_to(state, row, state->coord.col);
   1549 	}
   1550 	return 0;
   1551 }
   1552 
   1553 static int
   1554 move_screen_top(struct DrawState* state)
   1555 {
   1556 	assert(state != NULL);
   1557 	position_to(state, state->first_shown.row, state->coord.col);
   1558 	return 0;
   1559 }
   1560 
   1561 static int
   1562 move_start(struct DrawState* state)
   1563 {
   1564 	assert(state != NULL);
   1565 	position_to(state, state->coord.row, 0);
   1566 	return 0;
   1567 }
   1568 
   1569 static int
   1570 move_top(struct DrawState* state)
   1571 {
   1572 	assert(state != NULL);
   1573 	position_to(state, 0, state->coord.col);
   1574 	return 0;
   1575 }
   1576 
   1577 static int
   1578 move_up(struct DrawState* state)
   1579 {
   1580 	assert(state != NULL);
   1581 	position_to(state, state->coord.row > 1 ? state->coord.row - 1 : 0,
   1582 		state->coord.col);
   1583 	return 0;
   1584 }
   1585 
   1586 static int
   1587 next_match(struct DrawState* state)
   1588 {
   1589 	struct BufferLine* current = NULL;
   1590 	const uint32_t* found	   = NULL;
   1591 	ssize_t idelta;
   1592 
   1593 	assert(state != NULL);
   1594 	if (!state->search || !*state->search)
   1595 		return 0;
   1596 
   1597 	state->error = ERR_NONE;
   1598 	for (ssize_t row = state->coord.row; row < state->rows_count; row++)
   1599 	{
   1600 		current = state->buffer + row;
   1601 		found	= current->text;
   1602 		if (row == state->coord.row)
   1603 			found += state->coord.col + 1;
   1604 		found = u32_strstr(found, state->search);
   1605 		if (found)
   1606 		{
   1607 			idelta = found - (const uint32_t*)current->text;
   1608 			state->saved_column
   1609 				= display_length(current->text, idelta);
   1610 			if (*current->text)
   1611 			{
   1612 				position_to(state, row, idelta);
   1613 				return 0;
   1614 			}
   1615 		}
   1616 	}
   1617 	return 0;
   1618 }
   1619 
   1620 static int
   1621 paste_from_paste(struct DrawState* state)
   1622 {
   1623 	struct BufferLine* line	  = NULL;
   1624 	struct BufferLine* newbuf = NULL;
   1625 	struct Coord newpos;
   1626 	uint32_t* newtext = NULL;
   1627 	uint32_t* ptext	  = NULL;
   1628 	uint32_t* rest	  = NULL;
   1629 	ssize_t newsize	  = 0;
   1630 	ssize_t rest_size = 0;
   1631 
   1632 	assert(state != NULL);
   1633 	if (!state->paste_count)
   1634 		return 0;
   1635 
   1636 	if (SEL_NOT_EMPTY(state))
   1637 	{
   1638 		erase_selection(state);
   1639 		position_to_selection_start(state);
   1640 		clear_selection(state);
   1641 	}
   1642 
   1643 	if (state->paste_count > 1)
   1644 	{
   1645 		if (state->rows_count + state->paste_count > state->buffer_size)
   1646 		{
   1647 			ssize_t old_size = state->buffer_size;
   1648 			while (state->rows_count + state->paste_count
   1649 				> state->buffer_size)
   1650 				state->buffer_size += ALLOC_DELTA;
   1651 			newbuf = realloc(state->buffer,
   1652 				state->buffer_size * sizeof(struct BufferLine));
   1653 			for (ssize_t row = old_size; row < state->buffer_size;
   1654 				row++)
   1655 			{
   1656 				init_bufferline(newbuf + row);
   1657 				newtext = calloc(LINE_DEFAULT, sizeof(uint32_t));
   1658 				newbuf[row].size = LINE_DEFAULT;
   1659 				newbuf[row].text = newtext;
   1660 			}
   1661 			state->buffer = newbuf;
   1662 		}
   1663 	}
   1664 
   1665 	line	= state->buffer + state->coord.row;
   1666 	newsize = line->length + state->paste[0].length + 1;
   1667 	newtext = calloc(newsize, sizeof(uint32_t));
   1668 	if (!newtext)
   1669 	{
   1670 		state->running = 0;
   1671 		goto paste_from_paste_cleanup;
   1672 	}
   1673 	U32_MEMCCPY(newtext, line->text, state->coord.col + 1, ptext);
   1674 	U32_MEMCCPY((newtext + state->coord.col), state->paste[0].text, newsize,
   1675 		ptext);
   1676 
   1677 	assign_coord(&newpos, state->coord);
   1678 
   1679 	if (state->paste_count == 1)
   1680 	{
   1681 		if (ptext)
   1682 			U32_MEMCCPY_EXT((ptext - 1), newtext,
   1683 				line->text + state->coord.col,
   1684 				line->length - state->coord.col, newsize,
   1685 				ptext);
   1686 		U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
   1687 			paste_from_paste_cleanup);
   1688 		line->length = u32_strlen(line->text);
   1689 		newpos.col += state->paste[0].length;
   1690 	}
   1691 	else
   1692 	{
   1693 		state->rows_count += state->paste_count - 1;
   1694 		rest_size = line->size - state->coord.col;
   1695 		rest	  = calloc(rest_size, sizeof(uint32_t));
   1696 		if (!rest)
   1697 		{
   1698 			state->running = 0;
   1699 			goto paste_from_paste_cleanup;
   1700 		}
   1701 		U32_MEMCCPY(rest, line->text + state->coord.col, rest_size,
   1702 			ptext);
   1703 		U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
   1704 			paste_from_paste_cleanup);
   1705 		line->length = u32_strlen(line->text);
   1706 		for (ssize_t row = state->rows_count - state->coord.row
   1707 				- (state->paste_count - 2) - 1;
   1708 			row > 0; row--)
   1709 		{
   1710 			struct BufferLine* copied_line
   1711 				= &state->buffer[state->coord.row + row - 1];
   1712 
   1713 			line = state->buffer + state->coord.row
   1714 				+ state->paste_count - 2 + row;
   1715 			U32_SAFE_COPY(line->text, ptext, line->size,
   1716 				copied_line->text, copied_line->size,
   1717 				paste_from_paste_cleanup);
   1718 			line->length = u32_strlen(line->text);
   1719 		}
   1720 		line = state->buffer + state->coord.row;
   1721 		U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
   1722 			paste_from_paste_cleanup);
   1723 		line->length = u32_strlen(line->text);
   1724 		for (ssize_t row = 1; row < state->paste_count - 1; row++)
   1725 		{
   1726 			line = state->buffer + state->coord.row + row;
   1727 			U32_SAFE_COPY(line->text, ptext, line->size,
   1728 				state->paste[row].text, state->paste[row].size,
   1729 				paste_from_paste_cleanup);
   1730 			line->length = u32_strlen(line->text);
   1731 		}
   1732 		line = state->buffer + state->coord.row + state->paste_count
   1733 			- 1;
   1734 		U32_SAFE_COPY(newtext, ptext, newsize,
   1735 			state->paste[state->paste_count - 1].text,
   1736 			state->paste[state->paste_count - 1].size + rest_size,
   1737 			paste_from_paste_cleanup);
   1738 		if (ptext)
   1739 			U32_MEMCCPY_EXT((ptext - 1), newtext, rest,
   1740 				newsize - (ptext - newtext - 1), newsize,
   1741 				ptext);
   1742 		U32_SAFE_COPY(line->text, ptext, line->size, newtext, newsize,
   1743 			paste_from_paste_cleanup);
   1744 		line->length = u32_strlen(line->text);
   1745 		newpos.col   = state->paste[state->paste_count - 1].length;
   1746 		newpos.row += state->paste_count - 1;
   1747 		state->saved_column
   1748 			= display_length(state->paste[state->paste_count - 1]
   1749 						 .text,
   1750 				newpos.col);
   1751 	}
   1752 
   1753 	position_to(state, newpos.row, newpos.col);
   1754 	state->dirty = 1;
   1755 
   1756 paste_from_paste_cleanup:
   1757 	free(rest);
   1758 	free(newtext);
   1759 	return 0;
   1760 }
   1761 
   1762 /*
   1763  * state->saved_column - display coordinate
   1764  * row, column - buffer coordinates (not display)
   1765  */
   1766 static void
   1767 position_to(struct DrawState* state, const int row, const int column)
   1768 {
   1769 	ssize_t dlen;
   1770 	int old_first_shown_row;
   1771 	int maxx;
   1772 
   1773 	assert(state != NULL);
   1774 	maxx = state->maxx;
   1775 	if (state->line_numbers)
   1776 		maxx -= state->line_numbers_width + 2;
   1777 	old_first_shown_row = state->first_shown.row;
   1778 	if (row != state->coord.row)
   1779 	{
   1780 		state->coord.row = row;
   1781 		if ((state->coord.row + 1) > state->rows_count)
   1782 			state->coord.row = state->rows_count - 1;
   1783 		if (state->first_shown.row > state->coord.row)
   1784 			state->first_shown.row = state->coord.row;
   1785 		if (state->first_shown.row + (state->maxy - 2)
   1786 			< state->coord.row)
   1787 			state->first_shown.row
   1788 				= state->coord.row - (state->maxy - 2);
   1789 		if (state->saved_column == -1)
   1790 		{
   1791 			state->saved_column = state->display_column;
   1792 			state->coord.col    = column;
   1793 		}
   1794 		else
   1795 			state->coord.col
   1796 				= buffer_length(state->buffer[state->coord.row]
   1797 							.text,
   1798 					state->saved_column);
   1799 		if (state->coord.col < 0)
   1800 			state->coord.col = 0;
   1801 		else if (state->coord.col
   1802 			> (int)state->buffer[state->coord.row].length)
   1803 			state->coord.col
   1804 				= state->buffer[state->coord.row].length;
   1805 		dlen = display_length(state->buffer[state->coord.row].text,
   1806 			state->coord.col);
   1807 		if (dlen < state->saved_column)
   1808 			state->display_column = dlen;
   1809 		else
   1810 			state->display_column = state->saved_column;
   1811 	}
   1812 	else
   1813 	{
   1814 		state->coord.col = column;
   1815 		if (state->coord.col < 0)
   1816 		{
   1817 			if (state->coord.row > 0)
   1818 			{
   1819 				state->coord.row--;
   1820 				if (state->first_shown.row > 0
   1821 					&& state->first_shown.row
   1822 						> state->coord.row)
   1823 					state->first_shown.row--;
   1824 				state->coord.col
   1825 					= state->buffer[state->coord.row].length;
   1826 			}
   1827 			else
   1828 				state->coord.col = 0;
   1829 		}
   1830 		else if (state->coord.col
   1831 			> state->buffer[state->coord.row].length)
   1832 		{
   1833 			if ((state->coord.row + 1) < state->rows_count)
   1834 			{
   1835 				state->coord.row++;
   1836 				if (state->first_shown.row + (state->maxy - 2)
   1837 						< state->coord.row
   1838 					&& state->first_shown.row + 1
   1839 						< state->rows_count)
   1840 					state->first_shown.row++;
   1841 				state->coord.col = 0;
   1842 			}
   1843 			else
   1844 				state->coord.col
   1845 					= state->buffer[state->coord.row].length;
   1846 		}
   1847 		state->display_column
   1848 			= display_length(state->buffer[state->coord.row].text,
   1849 				state->coord.col);
   1850 		state->saved_column = state->display_column;
   1851 	}
   1852 	if (state->first_shown.col > state->display_column)
   1853 	{
   1854 		state->first_shown.col = state->display_column;
   1855 	}
   1856 	if (state->first_shown.col + maxx < state->display_column + 1)
   1857 	{
   1858 		int index_first;
   1859 		int fsc = state->first_shown.col;
   1860 		index_first
   1861 			= buffer_length(state->buffer[state->coord.row].text,
   1862 				fsc);
   1863 		while (fsc + maxx < state->display_column + 1)
   1864 		{
   1865 			index_first++;
   1866 			fsc = display_length(state->buffer[state->coord.row].text,
   1867 				index_first);
   1868 		}
   1869 		state->first_shown.col = fsc;
   1870 	}
   1871 	if (old_first_shown_row != state->first_shown.row)
   1872 		recalculate_line_numbers(state);
   1873 
   1874 	if (IN(state->mode, M_SELECTION))
   1875 		assign_coord(&state->sel_end, state->coord);
   1876 }
   1877 
   1878 static int
   1879 position_to_selection_end(struct DrawState* state)
   1880 {
   1881 	assert(state != NULL);
   1882 	if (SEL_NOT_EMPTY(state))
   1883 	{
   1884 		int ser = 0;
   1885 		int sec = 0;
   1886 
   1887 		get_selection(NULL, NULL, &ser, &sec, state);
   1888 
   1889 		state->saved_column
   1890 			= display_length(state->buffer[ser].text, sec);
   1891 		position_to(state, ser, sec);
   1892 		state->saved_column = state->display_column;
   1893 	}
   1894 	return 0;
   1895 }
   1896 
   1897 static int
   1898 position_to_selection_start(struct DrawState* state)
   1899 {
   1900 	assert(state != NULL);
   1901 	if (SEL_NOT_EMPTY(state))
   1902 	{
   1903 		int ssr = 0;
   1904 		int ssc = 0;
   1905 
   1906 		get_selection(&ssr, &ssc, NULL, NULL, state);
   1907 
   1908 		state->saved_column
   1909 			= display_length(state->buffer[ssr].text, ssc);
   1910 		position_to(state, ssr, ssc);
   1911 		state->saved_column = state->display_column;
   1912 	}
   1913 	return 0;
   1914 }
   1915 
   1916 static int
   1917 prev_match(struct DrawState* state)
   1918 {
   1919 	struct BufferLine* current = NULL;
   1920 	const uint32_t* found	   = NULL;
   1921 	ssize_t idelta;
   1922 	ssize_t row;
   1923 
   1924 	assert(state != NULL);
   1925 	if (!state->search || !*state->search)
   1926 		return 0;
   1927 
   1928 	state->error = ERR_NONE;
   1929 
   1930 	row = state->coord.row;
   1931 	while (1)
   1932 	{
   1933 		const uint32_t* save_found;
   1934 		current = state->buffer + row;
   1935 		found	= current->text;
   1936 		if (!found)
   1937 			return 0;
   1938 		if (row == state->coord.row)
   1939 			found += state->coord.col;
   1940 		else
   1941 			found += current->length;
   1942 
   1943 	prev_match_loop:
   1944 		save_found = found;
   1945 		found	   = u32_strrstr(current->text, state->search, found);
   1946 		if (found)
   1947 		{
   1948 			idelta = found - current->text;
   1949 			state->saved_column
   1950 				= display_length(current->text, idelta);
   1951 			if (*current->text)
   1952 			{
   1953 				position_to(state, row, idelta);
   1954 				return 0;
   1955 			}
   1956 		}
   1957 		if (save_found == current->text)
   1958 			goto prev_match_end;
   1959 		found = save_found - 1;
   1960 		goto prev_match_loop;
   1961 	prev_match_end:
   1962 		if (!row)
   1963 			break;
   1964 		row--;
   1965 	}
   1966 	return 0;
   1967 }
   1968 
   1969 static int
   1970 print_error(const int code, const char* msg, ...)
   1971 {
   1972 	char buf[LINE_DEFAULT];
   1973 	va_list args;
   1974 	va_start(args, msg);
   1975 	vsnprintf(buf, LINE_DEFAULT, msg, args);
   1976 	va_end(args);
   1977 	fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf);
   1978 	return code;
   1979 }
   1980 
   1981 static int
   1982 quit_callback(struct DrawState* state, struct tb_event* ev)
   1983 {
   1984 	assert(state != NULL);
   1985 	if (ev->ch == 'y')
   1986 		if (write_file(state))
   1987 			return 1;
   1988 	state->prompt_callback = NULL;
   1989 	state->running	       = 0;
   1990 	return 0;
   1991 }
   1992 
   1993 static int
   1994 reread_callback(struct DrawState* state, struct tb_event* ev)
   1995 {
   1996 	assert(state != NULL);
   1997 	if (strchr("Yy", ev->ch))
   1998 		if (reread_file(state))
   1999 			return 1;
   2000 	state->prompt_callback = NULL;
   2001 	return 0;
   2002 }
   2003 
   2004 static int
   2005 reread_file(struct DrawState* state)
   2006 {
   2007 	assert(state != NULL);
   2008 	free_drawstate(state);
   2009 	init_drawstate(state, state->filename, state->prompt, 1, 1);
   2010 
   2011 	redraw_status(state);
   2012 
   2013 	if (load_file(state))
   2014 	{
   2015 		state->running = 0;
   2016 		return state->error;
   2017 	}
   2018 
   2019 	recalculate_line_numbers(state);
   2020 	return 0;
   2021 }
   2022 
   2023 static int
   2024 reread_or_callback(struct DrawState* state)
   2025 {
   2026 	char* temp = NULL;
   2027 
   2028 	assert(state != NULL);
   2029 	if (state->dirty)
   2030 	{
   2031 		SET(state->mode, M_PROMPT);
   2032 		MEMCCPY(state->prompt, prompt_reread_dirty, LINE_DEFAULT, temp);
   2033 		state->prompt_callback = reread_callback;
   2034 	}
   2035 	else
   2036 		reread_file(state);
   2037 	return 0;
   2038 }
   2039 
   2040 static int
   2041 save_file(struct DrawState* state)
   2042 {
   2043 	FILE* output = NULL;
   2044 	struct stat st;
   2045 	char* buf	= NULL;
   2046 	char* pbuf	= NULL;
   2047 	ssize_t bufsize = LINE_DEFAULT;
   2048 	int result	= 0;
   2049 	int saved_errno;
   2050 
   2051 	assert(state != NULL);
   2052 	if (!(buf = calloc(bufsize, 1)))
   2053 		goto save_file_alloc_error;
   2054 
   2055 	state->action = ACT_WRITING;
   2056 	redraw_status(state);
   2057 	errno = 0;
   2058 
   2059 	if (!create_backups || state->new_file)
   2060 		goto save_skip_backup_copy;
   2061 
   2062 	if (!backup_fail && !state->backup_possible)
   2063 		goto save_skip_backup_copy;
   2064 
   2065 	if ((unlink(state->backup_filename) < 0) && (errno != ENOENT))
   2066 	{
   2067 		state->error = ERR_ERRNO_SET;
   2068 		result	     = 1;
   2069 		goto save_file_cleanup;
   2070 	}
   2071 	if (cp(state->rfn_len > 0 ? state->real_filename : state->filename,
   2072 		    state->backup_filename))
   2073 	{
   2074 		if (backup_fail)
   2075 		{
   2076 			state->error = ERR_ERRNO_SET;
   2077 			result	     = 1;
   2078 			goto save_file_cleanup;
   2079 		}
   2080 		else
   2081 			errno = 0; /* ignore errors */
   2082 	}
   2083 
   2084 save_skip_backup_copy:
   2085 	if (!(output = fopen(state->rfn_len > 0 ? state->real_filename
   2086 						: state->filename,
   2087 		      "w")))
   2088 	{
   2089 		state->error = ERR_ERRNO_SET;
   2090 		result	     = 1;
   2091 		goto save_file_cleanup;
   2092 	}
   2093 
   2094 	if (!create_backups || state->new_file)
   2095 		goto save_skip_backup_symlink;
   2096 
   2097 	if (lstat(state->rfn_len > 0 ? state->real_filename : state->filename,
   2098 		    &st)
   2099 		< 0)
   2100 	{
   2101 		state->error = ERR_ERRNO_SET;
   2102 		result	     = 1;
   2103 		goto save_file_cleanup;
   2104 	}
   2105 
   2106 	if (!backup_fail && !state->backup_possible)
   2107 		goto save_skip_backup_symlink;
   2108 
   2109 	if (state->rfn_len > 0)
   2110 	{
   2111 		if (unlink(state->filename) == -1)
   2112 		{
   2113 			state->error = ERR_ERRNO_SET;
   2114 			result	     = 1;
   2115 			goto save_file_cleanup;
   2116 		}
   2117 		if (symlink(state->orig_real_filename, state->filename) == -1)
   2118 		{
   2119 			state->error = ERR_ERRNO_SET;
   2120 			result	     = 1;
   2121 			goto save_file_cleanup;
   2122 		}
   2123 	}
   2124 	chmod(state->rfn_len > 0 ? state->real_filename : state->filename,
   2125 		st.st_mode);
   2126 
   2127 save_skip_backup_symlink:
   2128 	if (state->rows_count == 1 && state->buffer[0].length == 0)
   2129 		goto save_file_cleanup;
   2130 
   2131 	for (ssize_t row = 0; row < state->rows_count; row++)
   2132 	{
   2133 		ENSURE_SIZE(buf, pbuf, bufsize,
   2134 			UTF8REPMAX * state->buffer[row].length + 1,
   2135 			UTF8REPMAX * state->buffer[row].length
   2136 				+ LINE_ALLOC_DELTA,
   2137 			save_file_alloc_error, char);
   2138 		unicode_string_to_u8(buf, state->buffer[row].text, bufsize);
   2139 		fprintf(output, "%s\n", buf);
   2140 	}
   2141 
   2142 	state->new_file = 0;
   2143 	goto save_file_cleanup;
   2144 
   2145 save_file_alloc_error:
   2146 	if (output)
   2147 		fclose(output);
   2148 	return 1;
   2149 
   2150 save_file_cleanup:
   2151 	saved_errno = errno;
   2152 	if (output)
   2153 		fclose(output);
   2154 	free(buf);
   2155 	state->action = ACT_IDLE;
   2156 	errno	      = saved_errno;
   2157 	return result;
   2158 }
   2159 
   2160 static int
   2161 search_dialog_callback(struct DrawState* state)
   2162 {
   2163 	assert(state != NULL);
   2164 	next_match(state);
   2165 	if (!state->error)
   2166 		state->input_callback = NULL;
   2167 	return 0;
   2168 }
   2169 
   2170 static int
   2171 search_replace_replace_dialog_callback(struct DrawState* state)
   2172 {
   2173 	assert(state != NULL);
   2174 	do_search_replace(state);
   2175 	*state->search = 0;
   2176 	/*u32_strcpy(state->search, state->input);
   2177 	next_match(state);*/
   2178 	if (!state->error)
   2179 		state->input_callback = NULL;
   2180 	state->chain_dialog = 0;
   2181 	return 0;
   2182 }
   2183 
   2184 static int
   2185 search_replace_search_dialog_callback(struct DrawState* state)
   2186 {
   2187 	assert(state != NULL);
   2188 	/*next_match(state);*/
   2189 	if (!state->error)
   2190 		state->input_callback = NULL;
   2191 	*state->input = 0;
   2192 	show_replace(state);
   2193 	return 0;
   2194 }
   2195 
   2196 static int
   2197 search_update_callback(struct DrawState* state)
   2198 {
   2199 	uint32_t* temp = NULL;
   2200 	U32_MEMCCPY(state->search, state->input, MAXINPUT, temp);
   2201 	return 0;
   2202 }
   2203 
   2204 static int
   2205 show_go_to_line(struct DrawState* state)
   2206 {
   2207 	assert(state != NULL);
   2208 	state->input_title    = (char*)go_to_dialog_title;
   2209 	state->input_callback = go_to_line_callback;
   2210 	if (!state->input)
   2211 	{
   2212 		if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
   2213 		{
   2214 			state->running = 0;
   2215 			return 1;
   2216 		}
   2217 	}
   2218 	SET(state->mode, M_INPUT | M_INPUT_NUM);
   2219 	*state->input = 0;
   2220 	state->error  = ERR_NONE;
   2221 	input_position_to(state, 0);
   2222 	return 0;
   2223 }
   2224 
   2225 static int
   2226 show_help(struct DrawState* state)
   2227 {
   2228 	assert(state != NULL);
   2229 	SET(state->mode, M_HELP);
   2230 	state->error = ERR_NONE;
   2231 	return 0;
   2232 }
   2233 
   2234 static int
   2235 show_replace(struct DrawState* state)
   2236 {
   2237 	assert(state != NULL);
   2238 	state->input_title	     = (char*)replace_dialog_title;
   2239 	state->input_callback	     = search_replace_replace_dialog_callback;
   2240 	state->input_update_callback = NULL;
   2241 	if (!state->input)
   2242 	{
   2243 		if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
   2244 		{
   2245 			state->running = 0;
   2246 			return 1;
   2247 		}
   2248 	}
   2249 	*state->input = 0;
   2250 	SET(state->mode, M_INPUT);
   2251 	CLEAR(state->mode, M_INPUT_NUM);
   2252 	state->error = ERR_NONE;
   2253 	input_position_to(state, 0);
   2254 	return 0;
   2255 }
   2256 
   2257 static int
   2258 show_search(struct DrawState* state)
   2259 {
   2260 	assert(state != NULL);
   2261 	if (!state->search)
   2262 	{
   2263 		if (!(state->search = calloc(MAXINPUT, sizeof(uint32_t))))
   2264 		{
   2265 			state->running = 0;
   2266 			return 1;
   2267 		}
   2268 	}
   2269 	*state->search		     = 0;
   2270 	state->input_title	     = (char*)search_dialog_title;
   2271 	state->input_callback	     = search_dialog_callback;
   2272 	state->input_update_callback = search_update_callback;
   2273 	if (!state->input)
   2274 	{
   2275 		if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
   2276 		{
   2277 			state->running = 0;
   2278 			return 1;
   2279 		}
   2280 	}
   2281 	*state->input = 0;
   2282 	SET(state->mode, M_INPUT);
   2283 	CLEAR(state->mode, M_INPUT_NUM);
   2284 	state->error = ERR_NONE;
   2285 	input_position_to(state, 0);
   2286 	return 0;
   2287 }
   2288 
   2289 static int
   2290 show_search_replace(struct DrawState* state)
   2291 {
   2292 	assert(state != NULL);
   2293 	if (!state->search)
   2294 	{
   2295 		if (!(state->search = calloc(MAXINPUT, sizeof(uint32_t))))
   2296 		{
   2297 			state->running = 0;
   2298 			return 1;
   2299 		}
   2300 	}
   2301 	*state->search		     = 0;
   2302 	state->input_title	     = (char*)search_dialog_title;
   2303 	state->input_callback	     = search_replace_search_dialog_callback;
   2304 	state->input_update_callback = search_update_callback;
   2305 	if (!state->input)
   2306 	{
   2307 		if (!(state->input = calloc(MAXINPUT, sizeof(uint32_t))))
   2308 		{
   2309 			state->running = 0;
   2310 			return 1;
   2311 		}
   2312 	}
   2313 	*state->input	    = 0;
   2314 	state->chain_dialog = 1;
   2315 	SET(state->mode, M_INPUT);
   2316 	CLEAR(state->mode, M_INPUT_NUM);
   2317 	state->error = ERR_NONE;
   2318 	input_position_to(state, 0);
   2319 	return 0;
   2320 }
   2321 
   2322 static void
   2323 simple_insert_line(struct DrawState* state)
   2324 {
   2325 	struct BufferLine* newchunk = NULL;
   2326 	struct BufferLine* line	    = NULL;
   2327 	uint32_t* ptext		    = NULL;
   2328 
   2329 	assert(state != NULL);
   2330 	if (state->rows_count + 1 == state->buffer_size)
   2331 	{
   2332 		state->buffer_size += ALLOC_DELTA;
   2333 		newchunk = realloc(state->buffer,
   2334 			state->buffer_size * sizeof(struct BufferLine));
   2335 		if (!newchunk)
   2336 		{
   2337 			state->running = 0;
   2338 			return;
   2339 		}
   2340 		state->buffer = newchunk;
   2341 		for (ssize_t row = state->buffer_size - ALLOC_DELTA;
   2342 			row < state->buffer_size; row++)
   2343 		{
   2344 			line = state->buffer + row;
   2345 			init_bufferline(line);
   2346 			ENSURE_SIZE(line->text, ptext, line->size, LINE_DEFAULT,
   2347 				LINE_DEFAULT, simple_insert_line_error,
   2348 				uint32_t);
   2349 		}
   2350 	}
   2351 	state->rows_count++;
   2352 	for (ssize_t i = state->rows_count - 1; i > (state->coord.row + 1); i--)
   2353 	{
   2354 		U32_SAFE_COPY(state->buffer[i].text, ptext,
   2355 			state->buffer[i].size, state->buffer[i - 1].text,
   2356 			state->buffer[i - 1].size, simple_insert_line_error);
   2357 		state->buffer[i].length = state->buffer[i - 1].length;
   2358 	}
   2359 	ENSURE_SIZE(state->buffer[state->coord.row + 1].text, ptext,
   2360 		state->buffer[state->coord.row + 1].size, LINE_DEFAULT,
   2361 		LINE_DEFAULT, simple_insert_line_error, uint32_t);
   2362 	*state->buffer[state->coord.row + 1].text = 0;
   2363 	if (state->coord.col < state->buffer[state->coord.row].length)
   2364 		U32_SAFE_COPY(state->buffer[state->coord.row + 1].text, ptext,
   2365 			state->buffer[state->coord.row + 1].size,
   2366 			state->buffer[state->coord.row].text + state->coord.col,
   2367 			state->buffer[state->coord.row].size,
   2368 			simple_insert_line_error);
   2369 
   2370 	state->buffer[state->coord.row + 1].length
   2371 		= state->buffer[state->coord.row].length - state->coord.col;
   2372 
   2373 	state->buffer[state->coord.row].text[state->coord.col] = 0;
   2374 	state->buffer[state->coord.row].length = state->coord.col;
   2375 
   2376 	state->dirty = 1;
   2377 	recalculate_line_numbers(state);
   2378 	return;
   2379 
   2380 simple_insert_line_error:
   2381 	state->running = 0;
   2382 	return;
   2383 }
   2384 
   2385 static void
   2386 simple_join_lines(struct DrawState* state)
   2387 {
   2388 	struct BufferLine* first_line = NULL;
   2389 	struct BufferLine* next_line  = NULL;
   2390 	uint32_t* ptext		      = NULL;
   2391 
   2392 	assert(state != NULL);
   2393 	if ((state->coord.row + 1) == state->rows_count)
   2394 		return;
   2395 
   2396 	first_line = &state->buffer[state->coord.row];
   2397 	next_line  = &state->buffer[state->coord.row + 1];
   2398 	ENSURE_SIZE(first_line->text, ptext, first_line->size,
   2399 		first_line->length + next_line->length + 1,
   2400 		first_line->length + next_line->length + LINE_ALLOC_DELTA,
   2401 		simple_join_lines_error, uint32_t);
   2402 	U32_MEMCCPY((first_line->text + first_line->length), next_line->text,
   2403 		next_line->length + 1, ptext);
   2404 	first_line->length = u32_strlen(first_line->text);
   2405 	/* i <- i+1 */
   2406 	for (ssize_t i = state->coord.row + 1; i + 1 < state->rows_count; i++)
   2407 	{
   2408 		U32_SAFE_COPY(state->buffer[i].text, ptext,
   2409 			state->buffer[i].size, state->buffer[i + 1].text,
   2410 			state->buffer[i + 1].size, simple_join_lines_error);
   2411 		state->buffer[i].length = state->buffer[i + 1].length;
   2412 	}
   2413 	free_bufferline(&state->buffer[state->rows_count - 1]);
   2414 	state->rows_count--;
   2415 	state->dirty = 1;
   2416 	recalculate_line_numbers(state);
   2417 	return;
   2418 
   2419 simple_join_lines_error:
   2420 	state->running = 0;
   2421 	return;
   2422 }
   2423 
   2424 static void
   2425 simple_yank(struct DrawState* state)
   2426 {
   2427 	struct BufferLine* newbuf = NULL;
   2428 	uint32_t* ptext		  = NULL;
   2429 	ssize_t initialize_start;
   2430 	/* Selection start row/col */
   2431 	int ssr = 0;
   2432 	int ssc = 0;
   2433 	/* Selection end row/col */
   2434 	int ser = 0;
   2435 	int sec = 0;
   2436 
   2437 	assert(state != NULL);
   2438 	if (!SEL_NOT_EMPTY(state))
   2439 	{
   2440 		if (state->paste_count > 0)
   2441 		{
   2442 			state->paste_count     = 0;
   2443 			*state->paste[0].text  = 0;
   2444 			state->paste[0].length = 0;
   2445 		}
   2446 		clear_selection(state);
   2447 		return;
   2448 	}
   2449 
   2450 	get_selection(&ssr, &ssc, &ser, &sec, state);
   2451 
   2452 	state->paste_count = ser - ssr + 1;
   2453 	initialize_start   = state->paste_size;
   2454 	if (!state->paste_size)
   2455 	{
   2456 		while (state->paste_count > state->paste_size)
   2457 			state->paste_size += ALLOC_DELTA;
   2458 		newbuf = calloc(state->paste_size, sizeof(struct BufferLine));
   2459 	}
   2460 	else if (state->paste_count > state->paste_size)
   2461 	{
   2462 		while (state->paste_count > state->paste_size)
   2463 			state->paste_size += ALLOC_DELTA;
   2464 		newbuf = realloc(state->paste,
   2465 			state->paste_size * sizeof(struct BufferLine));
   2466 	}
   2467 	else
   2468 		newbuf = state->paste;
   2469 	for (ssize_t row = initialize_start; row < state->paste_size; row++)
   2470 	{
   2471 		init_bufferline(newbuf + row);
   2472 		ENSURE_SIZE(newbuf[row].text, ptext, newbuf[row].size,
   2473 			LINE_DEFAULT, LINE_DEFAULT, simple_yank_alloc_error,
   2474 			uint32_t);
   2475 	}
   2476 	state->paste = newbuf;
   2477 	if (state->paste_count == 1)
   2478 	{
   2479 		U32_SAFE_COPY(state->paste[0].text, ptext, state->paste[0].size,
   2480 			state->buffer[ssr].text + ssc, (sec - ssc + 1),
   2481 			simple_yank_alloc_error);
   2482 		state->paste[0].length = sec - ssc;
   2483 	}
   2484 	else
   2485 	{
   2486 		ssize_t first_line_len = state->buffer[ssr].length - ssc;
   2487 
   2488 		U32_SAFE_COPY(state->paste[0].text, ptext, state->paste[0].size,
   2489 			state->buffer[ssr].text + ssc, first_line_len + 1,
   2490 			simple_yank_alloc_error);
   2491 		state->paste[0].length = first_line_len;
   2492 
   2493 		for (ssize_t row = 1; row < state->paste_count - 1; row++)
   2494 		{
   2495 			U32_SAFE_COPY(state->paste[row].text, ptext,
   2496 				state->paste[row].size,
   2497 				state->buffer[ssr + row].text,
   2498 				state->buffer[ssr + row].size,
   2499 				simple_yank_alloc_error);
   2500 			state->paste[row].length
   2501 				= u32_strlen(state->paste[row].text);
   2502 		}
   2503 
   2504 		U32_SAFE_COPY(state->paste[state->paste_count - 1].text, ptext,
   2505 			state->paste[state->paste_count - 1].size,
   2506 			state->buffer[ser].text, (sec + 1),
   2507 			simple_yank_alloc_error);
   2508 		state->paste[state->paste_count - 1].length = sec;
   2509 	}
   2510 	return;
   2511 
   2512 simple_yank_alloc_error:
   2513 	state->running = 0;
   2514 	return;
   2515 }
   2516 
   2517 static int
   2518 suspend_program(struct DrawState* state)
   2519 {
   2520 	tb_set_clear_attrs(TB_WHITE, TB_DEFAULT);
   2521 	tb_clear();
   2522 	tb_set_cursor(0, 0);
   2523 	tb_present();
   2524 
   2525 	kill(getpid(), SIGTSTP);
   2526 
   2527 	/* --- back from suspend --- */
   2528 
   2529 	tb_clear();
   2530 	if (draw(state))
   2531 		return 1;
   2532 	tb_present();
   2533 	return 0;
   2534 }
   2535 
   2536 static int
   2537 toggle_line_numbers(struct DrawState* state)
   2538 {
   2539 	assert(state != NULL);
   2540 	state->line_numbers = !state->line_numbers;
   2541 	position_to(state, state->coord.row, state->coord.col);
   2542 	return 0;
   2543 }
   2544 
   2545 static int
   2546 toggle_overwrite(struct DrawState* state)
   2547 {
   2548 	assert(state != NULL);
   2549 	TOGGLE(state->mode, M_OVERWRITE);
   2550 	CLEAR(state->mode, M_COMMAND);
   2551 	return 0;
   2552 }
   2553 
   2554 static int
   2555 toggle_selection(struct DrawState* state)
   2556 {
   2557 	assert(state != NULL);
   2558 	TOGGLE(state->mode, M_SELECTION);
   2559 	if (IN(state->mode, M_SELECTION))
   2560 		assign_coord(&state->sel_start, state->coord);
   2561 	assign_coord(&state->sel_end, state->coord);
   2562 	return 0;
   2563 }
   2564 
   2565 /*
   2566  * Not ideal (todo). In extreme cases (files generated by the testsuite, with a
   2567  * huge block of full lines with no spaces), becomes unresponsive and has to be
   2568  * killed. But in most "normal" cases, behaves acceptably.
   2569  */
   2570 static int
   2571 wrap_para(struct DrawState* state)
   2572 {
   2573 	struct BufferLine* current = NULL;
   2574 	struct BufferLine* next	   = NULL;
   2575 	uint32_t* ptext		   = NULL;
   2576 	int sel;
   2577 	int start_row;
   2578 	int end_row;
   2579 	int cur_row;
   2580 	int bcol;
   2581 	int dlen;
   2582 	int col;
   2583 	int added_blank = 0;
   2584 
   2585 	assert(state != NULL);
   2586 	if (!enable_wrap)
   2587 		goto wrap_not_enabled;
   2588 
   2589 	sel = SEL_NOT_EMPTY(state);
   2590 
   2591 	state->action = ACT_WRAPPING;
   2592 	redraw_status(state);
   2593 
   2594 	if (sel)
   2595 		get_selection(&start_row, NULL, &end_row, NULL, state);
   2596 	else
   2597 	{
   2598 		start_row = state->coord.row;
   2599 		end_row	  = start_row;
   2600 		for (cur_row = start_row; cur_row > 0; cur_row--)
   2601 			if (state->buffer[cur_row - 1].length == 0
   2602 				|| !u32_has_nonblanks(
   2603 					state->buffer[cur_row - 1].text))
   2604 			{
   2605 				start_row = cur_row;
   2606 				break;
   2607 			}
   2608 		if (cur_row == 0)
   2609 			start_row = cur_row;
   2610 		for (cur_row = state->coord.row;
   2611 			cur_row + 1 < state->rows_count; cur_row++)
   2612 			if (state->buffer[cur_row].length == 0
   2613 				|| !u32_has_nonblanks(
   2614 					state->buffer[cur_row].text))
   2615 			{
   2616 				end_row = cur_row - 1;
   2617 				break;
   2618 			}
   2619 		if (cur_row + 1 == state->rows_count)
   2620 			end_row = cur_row - 1;
   2621 	}
   2622 
   2623 	cur_row = start_row;
   2624 wrap_next_line:
   2625 	state->coord.col = 0;
   2626 	current		 = &state->buffer[cur_row];
   2627 	next		 = &state->buffer[cur_row + 1];
   2628 	state->coord.row = cur_row;
   2629 
   2630 	if (sel && (cur_row == state->sel_end.row)
   2631 		&& (cur_row != state->sel_start.row)
   2632 		&& (cur_row + 1 != state->rows_count))
   2633 		goto wrap_finish;
   2634 
   2635 	/* If the line has less than wrap_width characters, join with next */
   2636 	dlen = display_length(current->text, current->length);
   2637 	while ((dlen < wrap_width) && (cur_row + 1 < state->rows_count)
   2638 		&& (current->length > 0) && u32_has_nonblanks(next->text))
   2639 	{
   2640 		/* Insert space to avoid joining ending and beginning words */
   2641 		if (current->length > 0
   2642 			&& !u32_is_word_boundary(current->text[current->length
   2643 							 - 1],
   2644 				1))
   2645 		{
   2646 			ENSURE_SIZE(current->text, ptext, current->size,
   2647 				current->length + 2, current->length + 2,
   2648 				wrap_alloc_error, uint32_t);
   2649 			current->length++;
   2650 			current->text[current->length - 1] = (uint32_t)' ';
   2651 			current->text[current->length]	   = 0;
   2652 		}
   2653 
   2654 		if (sel)
   2655 		{
   2656 			if (cur_row + 1 == state->sel_end.row)
   2657 				state->sel_end.col
   2658 					+= state->buffer[cur_row].length;
   2659 			if (cur_row + 1 == state->sel_start.row)
   2660 				state->sel_end.col -= state->sel_start.col;
   2661 			state->sel_end.row--;
   2662 		}
   2663 		simple_join_lines(state);
   2664 		end_row--;
   2665 
   2666 		current = &state->buffer[cur_row];
   2667 		next	= &state->buffer[cur_row + 1];
   2668 
   2669 		dlen = display_length(current->text, current->length);
   2670 	}
   2671 
   2672 	/* If the line has wrap_width or more characters, wrap it */
   2673 	while (dlen >= wrap_width)
   2674 	{
   2675 		int tdlen;
   2676 		bcol = buffer_length(current->text, wrap_width);
   2677 		while (bcol > 0
   2678 			&& !u32_is_word_boundary(current->text[bcol - 1], 1))
   2679 			bcol--;
   2680 		if (bcol == 0)
   2681 			break;
   2682 		ENSURE_SIZE(current->text, ptext, current->size,
   2683 			current->length + 1, current->length + LINE_ALLOC_DELTA,
   2684 			wrap_alloc_error, uint32_t);
   2685 		state->coord.col = bcol - 1;
   2686 
   2687 		if (sel)
   2688 		{
   2689 			if (cur_row == state->sel_end.row)
   2690 				state->sel_end.col -= bcol;
   2691 			/* TODO: Add startcol parameter to buffer_length? */
   2692 			/*if (cur_row == state->sel_start.row)
   2693 				state->sel_end.col += state->sel_start.col;*/
   2694 			state->sel_end.row++;
   2695 		}
   2696 		simple_insert_line(state);
   2697 		end_row++;
   2698 
   2699 		cur_row++;
   2700 		state->coord.row++;
   2701 		current = &state->buffer[cur_row];
   2702 		next	= &state->buffer[cur_row + 1];
   2703 
   2704 		/* - Remove spaces between the two words at split
   2705 		 * - current is the new current (the rest of the line
   2706 		 *   after split) */
   2707 		while (u32_is_word_boundary(current->text[0], 1))
   2708 		{
   2709 			for (col = 0; col < current->length; col++)
   2710 				current->text[col] = current->text[col + 1];
   2711 			assert(current->length > 0);
   2712 			current->length--;
   2713 			if (current->length == 0)
   2714 				break;
   2715 		}
   2716 
   2717 		if (cur_row + 1 == state->rows_count)
   2718 			goto wrap_next_line;
   2719 
   2720 		/* If the rest of the line is still in block and is
   2721 		 * < wrap_width, join it with next (the new next) */
   2722 		tdlen = display_length(current->text, current->length);
   2723 		if ((current->length > 0) && u32_has_nonblanks(next->text)
   2724 			&& (tdlen < wrap_width)
   2725 			&& !(sel && (cur_row == state->sel_end.row)))
   2726 		{
   2727 			/* Add space if needed to avoid joining words */
   2728 			/* clang-format off */
   2729 			if (!u32_is_word_boundary(
   2730 				current->text[current->length - 1], 1))
   2731 			{
   2732 				/* clang-format on */
   2733 				ENSURE_SIZE(current->text, ptext, current->size,
   2734 					current->length + 2,
   2735 					current->length + 2, wrap_alloc_error,
   2736 					uint32_t);
   2737 				current->length++;
   2738 				current->text[current->length - 1]
   2739 					= (uint32_t)' ';
   2740 				current->text[current->length] = 0;
   2741 			}
   2742 
   2743 			if (sel)
   2744 			{
   2745 				if (cur_row + 1 == state->sel_end.row)
   2746 					state->sel_end.col
   2747 						+= state->buffer[cur_row].length;
   2748 				if (cur_row + 1 == state->sel_start.row)
   2749 					state->sel_end.col
   2750 						-= state->sel_start.col;
   2751 				state->sel_end.row--;
   2752 			}
   2753 			simple_join_lines(state);
   2754 			end_row--;
   2755 
   2756 			dlen = display_length(current->text,
   2757 				sel && (cur_row == state->sel_end.row)
   2758 					? state->sel_end.col
   2759 					: current->length);
   2760 			// dlen = display_length(current->text, current->length);
   2761 			if (dlen < wrap_width)
   2762 				goto wrap_next_line;
   2763 
   2764 			/* One more split needed for the rest of the line (new
   2765 			 * current) */
   2766 			bcol = buffer_length(current->text, wrap_width);
   2767 			while (bcol > 0
   2768 				&& !u32_is_word_boundary(current->text[bcol - 1],
   2769 					1))
   2770 				bcol--;
   2771 			if (bcol == 0)
   2772 				break;
   2773 			ENSURE_SIZE(current->text, ptext, current->size,
   2774 				current->length + 1,
   2775 				current->length + LINE_ALLOC_DELTA,
   2776 				wrap_alloc_error, uint32_t);
   2777 			state->coord.col = bcol - 1;
   2778 
   2779 			if (sel)
   2780 			{
   2781 				if (cur_row == state->sel_end.row)
   2782 					state->sel_end.col -= bcol;
   2783 				/* TODO: Add startcol parameter to
   2784 				 *	 buffer_length? */
   2785 				/*if (cur_row == state->sel_start.row)
   2786 					state->sel_end.col +=
   2787 					state->sel_start.col;*/
   2788 				state->sel_end.row++;
   2789 			}
   2790 			simple_insert_line(state);
   2791 			end_row++;
   2792 
   2793 			/* Remove spaces between the two words at split */
   2794 			while (u32_is_word_boundary(next->text[0], 1))
   2795 			{
   2796 				for (col = 0; col < next->length; col++)
   2797 					next->text[col] = next->text[col + 1];
   2798 				assert(next->length > 0);
   2799 				next->length--;
   2800 				if (next->length == 0)
   2801 					break;
   2802 			}
   2803 		}
   2804 
   2805 		if (cur_row + 1 == state->rows_count)
   2806 			goto wrap_next_line;
   2807 
   2808 		if (cur_row == MIN((end_row + 1), state->rows_count))
   2809 			break;
   2810 
   2811 		dlen = display_length(current->text, current->length);
   2812 	}
   2813 	cur_row++;
   2814 	if (added_blank)
   2815 		goto wrap_finish;
   2816 	if (cur_row >= state->rows_count)
   2817 	{
   2818 		added_blank = 1;
   2819 		move_end(state);
   2820 		simple_insert_line(state);
   2821 		move_up(state);
   2822 		goto wrap_next_line;
   2823 	}
   2824 	else if (cur_row < (end_row + 2 >= state->rows_count ? state->rows_count
   2825 							     : end_row + 1))
   2826 		goto wrap_next_line;
   2827 
   2828 wrap_finish:
   2829 	if (added_blank)
   2830 		erase_line(state);
   2831 
   2832 	if (sel)
   2833 		position_to_selection_end(state);
   2834 	else
   2835 		position_to(state, cur_row, 0);
   2836 
   2837 	state->action = ACT_IDLE;
   2838 	recalculate_line_numbers(state);
   2839 	return 0;
   2840 
   2841 wrap_not_enabled:
   2842 	state->error = ERR_NOT_IMP;
   2843 	return 1;
   2844 
   2845 wrap_alloc_error:
   2846 	state->running = 0;
   2847 	return 1;
   2848 }
   2849 
   2850 static int
   2851 write_file(struct DrawState* state)
   2852 {
   2853 	assert(state != NULL);
   2854 	if (save_file(state))
   2855 	{
   2856 		state->running = 0;
   2857 		return 1;
   2858 	}
   2859 
   2860 	state->error = ERR_NONE;
   2861 	state->dirty = 0;
   2862 	return 0;
   2863 }
   2864 
   2865 static int
   2866 yank_to_paste(struct DrawState* state)
   2867 {
   2868 	simple_yank(state);
   2869 	clear_selection(state);
   2870 	return 0;
   2871 }
   2872 
   2873 int
   2874 main(int argc, char** argv)
   2875 {
   2876 	struct tb_event ev;
   2877 	struct DrawState state;
   2878 	struct stat st;
   2879 	char prompt[LINE_DEFAULT];
   2880 	char* filename		   = NULL;
   2881 	char* wd		   = NULL;
   2882 	char* dir		   = NULL;
   2883 	char* pdir		   = NULL;
   2884 	char* temp		   = NULL;
   2885 	char* rfn		   = NULL;
   2886 	const char* saved_tb_error = NULL;
   2887 	ssize_t filename_size	   = 0;
   2888 	int result		   = 0;
   2889 	int dir_access;
   2890 	int dir_access_errno;
   2891 	int saved_errno;
   2892 	int tbires;
   2893 	int optind = 1;
   2894 
   2895 	*prompt = 0;
   2896 
   2897 #ifdef __OpenBSD__
   2898 	if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0)
   2899 	{
   2900 		perror(PROGRAM_NAME ": pledge");
   2901 		exit(errno);
   2902 	}
   2903 #endif
   2904 
   2905 	setlocale(LC_ALL, "");
   2906 
   2907 	if (argc == 1)
   2908 		return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG,
   2909 			"No filename given");
   2910 
   2911 main_options_loop:
   2912 	if (optind == argc)
   2913 		return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG,
   2914 			"No filename given");
   2915 
   2916 	if (!strcmp(argv[optind], "-h") || !strcmp(argv[optind], "--help"))
   2917 	{
   2918 		printf("Usage:\t%s -h | --help | -V | --full-version | "
   2919 		       "-v | --version\n"
   2920 		       "\t%s [-B | --no-backup] [-b | --backup] "
   2921 		       "[-O | --no-open-binary] [-o | --open-binary] "
   2922 		       "filename\n",
   2923 			PROGRAM_NAME, PROGRAM_NAME);
   2924 		return 0;
   2925 	}
   2926 	else if (!strcmp(argv[optind], "-v")
   2927 		|| !strcmp(argv[optind], "--version"))
   2928 	{
   2929 		printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE);
   2930 		return 0;
   2931 	}
   2932 	else if (!strcmp(argv[optind], "-V")
   2933 		|| !strcmp(argv[optind], "--full-version"))
   2934 	{
   2935 		printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE);
   2936 		puts(COPYRIGHT);
   2937 		printf("Features set in config.h and on command line:\n");
   2938 		printf("  backup_fail:\t\t%s\n", backup_fail ? "set" : "unset");
   2939 		printf("  create_backups:\t%s\n",
   2940 			create_backups ? "set" : "unset");
   2941 		printf("  enable_wrap:\t\t%s\n", enable_wrap ? "set" : "unset");
   2942 		printf("  line_numbers:\t\t%s\n",
   2943 			line_numbers ? "set" : "unset");
   2944 		printf("  open_binary:\t\t%s\n", open_binary ? "set" : "unset");
   2945 		printf("  wrap_width:\t\t%d\n", wrap_width);
   2946 		return 0;
   2947 	}
   2948 	else if (!strcmp(argv[optind], "-B")
   2949 		|| !strcmp(argv[optind], "--no-backup"))
   2950 	{
   2951 		create_backups = 0;
   2952 		optind++;
   2953 		goto main_options_loop;
   2954 	}
   2955 	else if (!strcmp(argv[optind], "-b")
   2956 		|| !strcmp(argv[optind], "--backup"))
   2957 	{
   2958 		create_backups = 1;
   2959 		optind++;
   2960 		goto main_options_loop;
   2961 	}
   2962 	else if (!strcmp(argv[optind], "-O")
   2963 		|| !strcmp(argv[optind], "--no-open-binary"))
   2964 	{
   2965 		open_binary = 0;
   2966 		optind++;
   2967 		goto main_options_loop;
   2968 	}
   2969 	else if (!strcmp(argv[optind], "-o")
   2970 		|| !strcmp(argv[optind], "--open-binary"))
   2971 	{
   2972 		open_binary = 1;
   2973 		optind++;
   2974 		goto main_options_loop;
   2975 	}
   2976 
   2977 	/*
   2978 	 * Chicken and egg problem: to get fpathconf(dirfd, _PC_PATH_MAX)
   2979 	 * we need a dir, which needs to be allocated, and to allocate it we
   2980 	 * need to know the value returned by fpathconf(dirfd, _PC_PATH_MAX)...
   2981 	 */
   2982 	/* argv[1] is the filename; BTW, "filename" is a misnomer */
   2983 	filename_size = strlen(argv[optind]) + 1;
   2984 	if (!(dir = malloc(filename_size)))
   2985 	{
   2986 		perror(PROGRAM_NAME ": malloc");
   2987 		exit(errno);
   2988 	}
   2989 
   2990 	(void)dirname(dir, argv[optind], filename_size);
   2991 	if (init_limits(dir) < 0)
   2992 	{
   2993 		perror(PROGRAM_NAME ": init_limits");
   2994 		free(dir);
   2995 		exit(errno);
   2996 	}
   2997 
   2998 	filename = calloc(limits[L_PATH_MAX], 1);
   2999 	if (!filename)
   3000 	{
   3001 		perror(PROGRAM_NAME ": calloc");
   3002 		exit(errno);
   3003 	}
   3004 
   3005 	*filename = 0;
   3006 	MEMCCPY(filename, argv[optind], limits[L_PATH_MAX], temp);
   3007 	if (!*filename)
   3008 	{
   3009 		free(filename);
   3010 		free(dir);
   3011 		return print_error(EINVAL, "No filename given");
   3012 	}
   3013 
   3014 	init_drawstate(&state, filename, prompt, 0, 0);
   3015 
   3016 #ifdef __OpenBSD__
   3017 	if (unveil(state.filename, "rwc") < 0)
   3018 	{
   3019 		perror(PROGRAM_NAME ": unveil");
   3020 		goto main_done;
   3021 	}
   3022 	if (unveil(dir, "rwc") < 0)
   3023 	{
   3024 		perror(PROGRAM_NAME ": unveil");
   3025 		goto main_done;
   3026 	}
   3027 #endif
   3028 
   3029 	if (!create_backups)
   3030 		goto main_skip_backups;
   3031 
   3032 	if (!(wd = malloc(limits[L_PATH_MAX])))
   3033 	{
   3034 		perror(PROGRAM_NAME ": malloc");
   3035 		goto main_done;
   3036 	}
   3037 	if (!getcwd(wd, limits[L_PATH_MAX]))
   3038 	{
   3039 		perror(PROGRAM_NAME ": getcwd");
   3040 		goto main_done;
   3041 	}
   3042 #ifdef __OpenBSD__
   3043 	if (unveil(wd, "rwc") < 0)
   3044 	{
   3045 		perror(PROGRAM_NAME ": unveil");
   3046 		goto main_done;
   3047 	}
   3048 #endif
   3049 
   3050 	errno = 0;
   3051 	if (lstat(state.filename, &st) != -1)
   3052 	{
   3053 		if (S_ISLNK(st.st_mode))
   3054 		{
   3055 			if (!(rfn = malloc(limits[L_PATH_MAX])))
   3056 			{
   3057 				perror(PROGRAM_NAME ": malloc");
   3058 				goto main_done;
   3059 			}
   3060 
   3061 			if (!(state.real_filename = malloc(limits[L_PATH_MAX])))
   3062 			{
   3063 				perror(PROGRAM_NAME ": malloc");
   3064 				goto main_done;
   3065 			}
   3066 
   3067 			state.rfn_len = readlink(state.filename, rfn,
   3068 				limits[L_PATH_MAX]);
   3069 			if (state.rfn_len == -1)
   3070 				goto main_done;
   3071 
   3072 			rfn[state.rfn_len] = 0;
   3073 
   3074 			if (*rfn == '/') /* rfn is absolute */
   3075 			{
   3076 				MEMCCPY(state.real_filename, rfn,
   3077 					limits[L_PATH_MAX], temp);
   3078 				state.rfn_len = strlen(rfn);
   3079 			}
   3080 			else /* rfn is relative */
   3081 			{
   3082 				size_t rfn_dir_len;
   3083 
   3084 				state.orig_real_filename
   3085 					= strndup(rfn, state.rfn_len);
   3086 				if (!state.orig_real_filename)
   3087 				{
   3088 					perror(PROGRAM_NAME ": strndup");
   3089 					goto main_done;
   3090 				}
   3091 
   3092 				MEMCCPY(state.real_filename, dir,
   3093 					limits[L_PATH_MAX], temp);
   3094 				if (!temp)
   3095 				{
   3096 					errno = ENAMETOOLONG;
   3097 					goto main_done;
   3098 				}
   3099 				*(temp - 1) = '/'; /* overwrite NUL */
   3100 				rfn_dir_len = temp - state.real_filename - 1;
   3101 				MEMCCPY_EXT(temp, state.real_filename, rfn,
   3102 					limits[L_PATH_MAX] - rfn_dir_len, temp);
   3103 				if (temp)
   3104 					state.rfn_len = temp
   3105 						- state.real_filename - 1;
   3106 			}
   3107 			free(rfn);
   3108 			rfn = NULL;
   3109 #ifdef __OpenBSD__
   3110 			if (unveil(state.real_filename, "rwc") < 0)
   3111 			{
   3112 				perror(PROGRAM_NAME ": unveil");
   3113 				goto main_done;
   3114 			}
   3115 #endif
   3116 			if (stat(state.real_filename, &st) == -1)
   3117 				goto main_done;
   3118 		}
   3119 	} /* We don't error out here on ENOENT to account for new files */
   3120 	else if (errno == ENOENT)
   3121 		state.new_file = 1;
   3122 	else
   3123 		goto main_done;
   3124 
   3125 	if (!(state.backup_filename = malloc(limits[L_PATH_MAX])))
   3126 	{
   3127 		perror(PROGRAM_NAME ": malloc");
   3128 		goto main_done;
   3129 	}
   3130 
   3131 	/* Backup filename is the target file + suffix if the file to
   3132 	 * write is not a new file and is a symlink, otherwise file +
   3133 	 * suffix */
   3134 	MEMCCPY(state.backup_filename,
   3135 		state.new_file ? state.filename
   3136 			       : (state.rfn_len > 0 ? state.real_filename
   3137 						    : state.filename),
   3138 		limits[L_PATH_MAX], temp);
   3139 	if (!temp) /* filename is full, turning off backups */
   3140 		state.backup_possible = 0;
   3141 	else
   3142 		MEMCCPY_EXT((temp - 1), state.backup_filename, backup_suffix,
   3143 			limits[L_PATH_MAX] + temp - state.backup_filename,
   3144 			temp);
   3145 #ifdef __OpenBSD__
   3146 	if (unveil(state.backup_filename, "rwc") < 0)
   3147 	{
   3148 		perror(PROGRAM_NAME ": unveil");
   3149 		goto main_done;
   3150 	}
   3151 #endif
   3152 
   3153 main_skip_backups:
   3154 	if (state.rfn_len > 0)
   3155 	{
   3156 		if (!(pdir = realloc(dir, limits[L_PATH_MAX])))
   3157 		{
   3158 			perror(PROGRAM_NAME ": realloc");
   3159 			goto main_done;
   3160 		}
   3161 		dir  = pdir;
   3162 		*dir = 0;
   3163 		(void)dirname(dir, state.real_filename, limits[L_PATH_MAX]);
   3164 #ifdef __OpenBSD__
   3165 		if (unveil(dir, "rwc") < 0)
   3166 		{
   3167 			perror(PROGRAM_NAME ": unveil");
   3168 			goto main_done;
   3169 		}
   3170 #endif
   3171 	}
   3172 
   3173 #ifdef __OpenBSD__
   3174 	if (unveil_terminfo_path() < 0)
   3175 	{
   3176 		perror(PROGRAM_NAME ": unveil");
   3177 		goto main_done;
   3178 	}
   3179 	/* Have to explicitly unveil /dev/tty before tb_init despite pledge(tty)
   3180 	 * in OpenBSD 7.9+
   3181 	 */
   3182 	if (unveil("/dev/tty", "rw"))
   3183 	{
   3184 		perror(PROGRAM_NAME ": unveil");
   3185 		goto main_done;
   3186 	}
   3187 	if (unveil(NULL, NULL) < 0)
   3188 	{
   3189 		perror(PROGRAM_NAME ": unveil");
   3190 		goto main_done;
   3191 	}
   3192 #endif
   3193 
   3194 	dir_access	 = access(dir, W_OK | X_OK);
   3195 	dir_access_errno = errno;
   3196 
   3197 	if (!backup_fail && state.backup_possible)
   3198 		state.backup_possible = dir_access != -1;
   3199 
   3200 	result = stat(state.filename, &st);
   3201 	if (result == -1)
   3202 	{
   3203 		if (errno != ENOENT || dir_access == -1)
   3204 		{
   3205 			errno = dir_access_errno;
   3206 			goto main_done;
   3207 		}
   3208 		if (create_file(&state))
   3209 			goto main_done;
   3210 
   3211 		goto init_termbox;
   3212 	}
   3213 
   3214 	errno = 0;
   3215 
   3216 	if (!S_ISREG(st.st_mode))
   3217 	{
   3218 		state.error = ERR_NOTREG_FILE;
   3219 		goto main_done;
   3220 	}
   3221 
   3222 	if (access(state.filename, F_OK | R_OK | W_OK) == -1)
   3223 		goto main_done;
   3224 
   3225 	if (state.rfn_len > 0
   3226 		&& access(state.real_filename, F_OK | R_OK | W_OK) == -1)
   3227 		goto main_done;
   3228 
   3229 	if (load_file(&state))
   3230 		goto main_done;
   3231 
   3232 	goto init_termbox;
   3233 
   3234 init_termbox:
   3235 	tbires = 0;
   3236 	if ((tbires = tb_init()))
   3237 	{
   3238 		saved_tb_error = tb_strerror(tbires);
   3239 		errno	       = ERR_TB_ERROR;
   3240 		goto main_done;
   3241 	}
   3242 
   3243 	errno		       = 0;
   3244 	state.maxx	       = 0;
   3245 	state.recalculate_size = 1;
   3246 
   3247 	while (state.running)
   3248 	{
   3249 		tb_set_clear_attrs(colors[C_TEXT].fg, colors[C_TEXT].bg);
   3250 		tb_clear();
   3251 
   3252 		if (state.recalculate_size)
   3253 		{
   3254 			recalculate_size(&state);
   3255 			state.recalculate_size = 0;
   3256 		}
   3257 		if (draw(&state))
   3258 		{
   3259 			state.running = 0;
   3260 			goto main_done;
   3261 		}
   3262 		tb_present();
   3263 		result = tb_poll_event(&ev);
   3264 		if (result == TB_OK && ev.type == TB_EVENT_RESIZE)
   3265 			state.recalculate_size = 1;
   3266 		else if (result == TB_OK && ev.type == TB_EVENT_KEY)
   3267 			handle_key_event(&ev, &state);
   3268 	}
   3269 
   3270 main_done:
   3271 	saved_errno = errno;
   3272 
   3273 	if (saved_tb_error)
   3274 	{
   3275 		tb_shutdown(1);
   3276 		print_error(1, saved_tb_error);
   3277 		exit(CUSTOM_ERROR_START + saved_errno);
   3278 	}
   3279 
   3280 	tb_shutdown(saved_errno != ENOMEM);
   3281 
   3282 	if (saved_errno)
   3283 		perror(PROGRAM_NAME);
   3284 
   3285 	if (state.error == ERR_NOTREG_FILE)
   3286 		print_error(1, errors[state.error], state.filename);
   3287 	else if (state.error)
   3288 		print_error(1, errors[state.error]);
   3289 	/*free_drawstate(&state);
   3290 	free(wd);
   3291 	free(dir);
   3292 	free(rfn);*/
   3293 	return saved_errno
   3294 		? saved_errno
   3295 		: (state.error ? CUSTOM_ERROR_START + state.error : 0);
   3296 }