nano

nano with my custom patches
git clone git://bsandro.tech/nano
Log | Files | Refs | README | LICENSE

text.c (95319B)


      1 /**************************************************************************
      2  *   text.c  --  This file is part of GNU nano.                           *
      3  *                                                                        *
      4  *   Copyright (C) 1999-2011, 2013-2025 Free Software Foundation, Inc.    *
      5  *   Copyright (C) 2014-2015 Mark Majeres                                 *
      6  *   Copyright (C) 2016 Mike Scalora                                      *
      7  *   Copyright (C) 2016 Sumedh Pendurkar                                  *
      8  *   Copyright (C) 2018 Marco Diego Aurélio Mesquita                      *
      9  *   Copyright (C) 2015-2022 Benno Schulenberg                            *
     10  *                                                                        *
     11  *   GNU nano is free software: you can redistribute it and/or modify     *
     12  *   it under the terms of the GNU General Public License as published    *
     13  *   by the Free Software Foundation, either version 3 of the License,    *
     14  *   or (at your option) any later version.                               *
     15  *                                                                        *
     16  *   GNU nano is distributed in the hope that it will be useful,          *
     17  *   but WITHOUT ANY WARRANTY; without even the implied warranty          *
     18  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              *
     19  *   See the GNU General Public License for more details.                 *
     20  *                                                                        *
     21  *   You should have received a copy of the GNU General Public License    *
     22  *   along with this program.  If not, see https://gnu.org/licenses/.     *
     23  *                                                                        *
     24  **************************************************************************/
     25 
     26 #include "prototypes.h"
     27 
     28 #include <errno.h>
     29 #include <fcntl.h>
     30 #include <string.h>
     31 #include <time.h>
     32 #include <unistd.h>
     33 #include <sys/wait.h>
     34 
     35 #if defined(__APPLE__) && !defined(st_mtim)
     36 #define st_mtim  st_mtimespec
     37 #endif
     38 
     39 #ifndef NANO_TINY
     40 /* Toggle the mark. */
     41 void do_mark(void)
     42 {
     43 	if (!openfile->mark) {
     44 		openfile->mark = openfile->current;
     45 		openfile->mark_x = openfile->current_x;
     46 		openfile->softmark = FALSE;
     47 		statusbar(_("Mark Set"));
     48 	} else {
     49 		openfile->mark = NULL;
     50 		statusbar(_("Mark Unset"));
     51 		refresh_needed = TRUE;
     52 	}
     53 }
     54 #endif
     55 
     56 /* Insert a tab.  Or, if --tabstospaces is in effect, insert the number
     57  * of spaces that a tab would normally take up at this position. */
     58 void do_tab(void)
     59 {
     60 #ifndef NANO_TINY
     61 	/* When <Tab> is pressed while a region is marked, indent the region. */
     62 	if (openfile->mark && openfile->mark != openfile->current)
     63 		do_indent();
     64 	else
     65 #endif
     66 #ifdef ENABLE_COLOR
     67 	if (openfile->syntax && openfile->syntax->tabstring)
     68 		inject(openfile->syntax->tabstring, strlen(openfile->syntax->tabstring));
     69 	else
     70 #endif
     71 #ifndef NANO_TINY
     72 	if (ISSET(TABS_TO_SPACES)) {
     73 		char *spaces = nmalloc(tabsize + 1);
     74 		size_t length = tabsize - (xplustabs() % tabsize);
     75 
     76 		memset(spaces, ' ', length);
     77 		spaces[length] = '\0';
     78 
     79 		inject(spaces, length);
     80 
     81 		free(spaces);
     82 	} else
     83 #endif
     84 		inject((char *)"\t", 1);
     85 }
     86 
     87 #ifndef NANO_TINY
     88 /* Add an indent to the given line. */
     89 void indent_a_line(linestruct *line, char *indentation)
     90 {
     91 	size_t length = strlen(line->data);
     92 	size_t indent_len = strlen(indentation);
     93 
     94 	/* If the requested indentation is empty, don't change the line. */
     95 	if (indent_len == 0)
     96 		return;
     97 
     98 	/* Add the fabricated indentation to the beginning of the line. */
     99 	line->data = nrealloc(line->data, length + indent_len + 1);
    100 	memmove(line->data + indent_len, line->data, length + 1);
    101 	memcpy(line->data, indentation, indent_len);
    102 
    103 	openfile->totsize += indent_len;
    104 
    105 	/* Compensate for the change in the current line. */
    106 	if (line == openfile->mark && openfile->mark_x > 0)
    107 		openfile->mark_x += indent_len;
    108 	if (line == openfile->current && openfile->current_x > 0) {
    109 		openfile->current_x += indent_len;
    110 		openfile->placewewant = xplustabs();
    111 	}
    112 }
    113 
    114 /* Indent the current line (or the marked lines) by tabsize columns.
    115  * This inserts either a tab character or a tab's worth of spaces,
    116  * depending on whether --tabstospaces is in effect. */
    117 void do_indent(void)
    118 {
    119 	linestruct *top, *bot, *line;
    120 	char *indentation;
    121 
    122 	/* Use either all the marked lines or just the current line. */
    123 	get_range(&top, &bot);
    124 
    125 	/* Skip any leading empty lines. */
    126 	while (top != bot->next && top->data[0] == '\0')
    127 		top = top->next;
    128 
    129 	/* If all lines are empty, there is nothing to do. */
    130 	if (top == bot->next)
    131 		return;
    132 
    133 	indentation = nmalloc(tabsize + 1);
    134 
    135 #ifdef ENABLE_COLOR
    136 	if (openfile->syntax && openfile->syntax->tabstring)
    137 		indentation = mallocstrcpy(indentation, openfile->syntax->tabstring);
    138 	else
    139 #endif
    140 	/* Set the indentation to either a bunch of spaces or a single tab. */
    141 	if (ISSET(TABS_TO_SPACES)) {
    142 		memset(indentation, ' ', tabsize);
    143 		indentation[tabsize] = '\0';
    144 	} else {
    145 		indentation[0] = '\t';
    146 		indentation[1] = '\0';
    147 	}
    148 
    149 	add_undo(INDENT, NULL);
    150 
    151 	/* Go through each of the lines, adding an indent to the non-empty ones,
    152 	 * and recording whatever was added in the undo item. */
    153 	for (line = top; line != bot->next; line = line->next) {
    154 		char *real_indent = (line->data[0] == '\0') ? "" : indentation;
    155 
    156 		indent_a_line(line, real_indent);
    157 		update_multiline_undo(line->lineno, real_indent);
    158 	}
    159 
    160 	free(indentation);
    161 
    162 	set_modified();
    163 	ensure_firstcolumn_is_aligned();
    164 	refresh_needed = TRUE;
    165 	shift_held = TRUE;
    166 }
    167 
    168 /* Return the number of bytes of whitespace at the start of the given text,
    169  * but at most a tab's worth. */
    170 size_t length_of_white(const char *text)
    171 {
    172 	size_t white_count = 0;
    173 
    174 #ifdef ENABLE_COLOR
    175 	if (openfile->syntax && openfile->syntax->tabstring) {
    176 		size_t thelength = strlen(openfile->syntax->tabstring);
    177 
    178 		while (text[white_count] == openfile->syntax->tabstring[white_count])
    179 			if (++white_count == thelength)
    180 				return thelength;
    181 
    182 		white_count = 0;
    183 	}
    184 #endif
    185 
    186 	while (TRUE) {
    187 		if (*text == '\t')
    188 			return white_count + 1;
    189 
    190 		if (*text != ' ')
    191 			return white_count;
    192 
    193 		if (++white_count == tabsize)
    194 			return tabsize;
    195 
    196 		text++;
    197 	}
    198 }
    199 
    200 /* Adjust the positions of mark and cursor when they are on the given line. */
    201 void compensate_leftward(linestruct *line, size_t leftshift)
    202 {
    203 	if (line == openfile->mark) {
    204 		if (openfile->mark_x < leftshift)
    205 			openfile->mark_x = 0;
    206 		else
    207 			openfile->mark_x -= leftshift;
    208 	}
    209 
    210 	if (line == openfile->current) {
    211 		if (openfile->current_x < leftshift)
    212 			openfile->current_x = 0;
    213 		else
    214 			openfile->current_x -= leftshift;
    215 		openfile->placewewant = xplustabs();
    216 	}
    217 }
    218 
    219 /* Remove an indent from the given line. */
    220 void unindent_a_line(linestruct *line, size_t indent_len)
    221 {
    222 	size_t length = strlen(line->data);
    223 
    224 	/* If the indent is empty, don't change the line. */
    225 	if (indent_len == 0)
    226 		return;
    227 
    228 	/* Remove the first tab's worth of whitespace from this line. */
    229 	memmove(line->data, line->data + indent_len, length - indent_len + 1);
    230 
    231 	openfile->totsize -= indent_len;
    232 
    233 	/* Adjust the positions of mark and cursor, when they are affected. */
    234 	compensate_leftward(line, indent_len);
    235 }
    236 
    237 /* Unindent the current line (or the marked lines) by tabsize columns.
    238  * The removed indent can be a mixture of spaces plus at most one tab. */
    239 void do_unindent(void)
    240 {
    241 	linestruct *top, *bot, *line;
    242 
    243 	/* Use either all the marked lines or just the current line. */
    244 	get_range(&top, &bot);
    245 
    246 	/* Skip any leading lines that cannot be unindented. */
    247 	while (top != bot->next && length_of_white(top->data) == 0)
    248 		top = top->next;
    249 
    250 	/* If none of the lines can be unindented, there is nothing to do. */
    251 	if (top == bot->next)
    252 		return;
    253 
    254 	add_undo(UNINDENT, NULL);
    255 
    256 	/* Go through each of the lines, removing their leading indent where
    257 	 * possible, and saving the removed whitespace in the undo item. */
    258 	for (line = top; line != bot->next; line = line->next) {
    259 		size_t indent_len = length_of_white(line->data);
    260 		char *indentation = measured_copy(line->data, indent_len);
    261 
    262 		unindent_a_line(line, indent_len);
    263 		update_multiline_undo(line->lineno, indentation);
    264 
    265 		free(indentation);
    266 	}
    267 
    268 	set_modified();
    269 	ensure_firstcolumn_is_aligned();
    270 	refresh_needed = TRUE;
    271 	shift_held = TRUE;
    272 }
    273 
    274 /* Perform an undo or redo for an indent or unindent action. */
    275 void handle_indent_action(undostruct *u, bool undoing, bool add_indent)
    276 {
    277 	groupstruct *group = u->grouping;
    278 	linestruct *line = line_from_number(group->top_line);
    279 
    280 	/* When redoing, reposition the cursor and let the indenter adjust it. */
    281 	if (!undoing)
    282 		goto_line_posx(u->head_lineno, u->head_x);
    283 
    284 	/* For each line in the group, add or remove the individual indent. */
    285 	while (line != NULL && line->lineno <= group->bottom_line) {
    286 		char *blanks = group->indentations[line->lineno - group->top_line];
    287 
    288 		if (undoing ^ add_indent)
    289 			indent_a_line(line, blanks);
    290 		else
    291 			unindent_a_line(line, strlen(blanks));
    292 
    293 		line = line->next;
    294 	}
    295 
    296 	/* When undoing, reposition the cursor to the recorded location. */
    297 	if (undoing)
    298 		goto_line_posx(u->head_lineno, u->head_x);
    299 
    300 	refresh_needed = TRUE;
    301 }
    302 #endif /* !NANO_TINY */
    303 
    304 #ifdef ENABLE_COMMENT
    305 /* Test whether the given line can be uncommented, or add or remove a comment,
    306  * depending on action.  Return TRUE if the line is uncommentable, or when
    307  * anything was added or removed; FALSE otherwise. */
    308 bool comment_line(undo_type action, linestruct *line, const char *comment_seq)
    309 {
    310 	size_t comment_seq_len = strlen(comment_seq);
    311 	const char *post_seq = strchr(comment_seq, '|');
    312 		/* The postfix, if this is a bracketing type comment sequence. */
    313 	size_t pre_len = post_seq ? post_seq++ - comment_seq : comment_seq_len;
    314 		/* Length of prefix. */
    315 	size_t post_len = post_seq ? comment_seq_len - pre_len - 1 : 0;
    316 		/* Length of postfix. */
    317 	size_t line_len = strlen(line->data);
    318 
    319 	if (!ISSET(NO_NEWLINES) && line == openfile->filebot)
    320 		return FALSE;
    321 
    322 	if (action == COMMENT) {
    323 		/* Make room for the comment sequence(s), move the text right and
    324 		 * copy them in. */
    325 		line->data = nrealloc(line->data, line_len + pre_len + post_len + 1);
    326 		memmove(line->data + pre_len, line->data, line_len + 1);
    327 		memmove(line->data, comment_seq, pre_len);
    328 		if (post_len > 0)
    329 			memmove(line->data + pre_len + line_len, post_seq, post_len + 1);
    330 
    331 		openfile->totsize += pre_len + post_len;
    332 
    333 		/* If needed, adjust the position of the mark and of the cursor. */
    334 		if (line == openfile->mark && openfile->mark_x > 0)
    335 			openfile->mark_x += pre_len;
    336 		if (line == openfile->current && openfile->current_x > 0) {
    337 			openfile->current_x += pre_len;
    338 			openfile->placewewant = xplustabs();
    339 		}
    340 
    341 		return TRUE;
    342 	}
    343 
    344 	/* If the line is commented, report it as uncommentable, or uncomment it. */
    345 	if (strncmp(line->data, comment_seq, pre_len) == 0 && (post_len == 0 ||
    346 				strcmp(line->data + line_len - post_len, post_seq) == 0)) {
    347 
    348 		if (action == PREFLIGHT)
    349 			return TRUE;
    350 
    351 		/* Erase the comment prefix by moving the non-comment part. */
    352 		memmove(line->data, line->data + pre_len, line_len - pre_len);
    353 		/* Truncate the postfix if there was one. */
    354 		line->data[line_len - pre_len - post_len] = '\0';
    355 
    356 		openfile->totsize -= pre_len + post_len;
    357 
    358 		/* Adjust the positions of mark and cursor, when needed. */
    359 		compensate_leftward(line, pre_len);
    360 
    361 		return TRUE;
    362 	}
    363 
    364 	return FALSE;
    365 }
    366 
    367 /* Comment or uncomment the current line or the marked lines. */
    368 void do_comment(void)
    369 {
    370 	const char *comment_seq = GENERAL_COMMENT_CHARACTER;
    371 	undo_type action = UNCOMMENT;
    372 	linestruct *top, *bot, *line;
    373 	bool empty, all_empty = TRUE;
    374 
    375 #ifdef ENABLE_COLOR
    376 	if (openfile->syntax)
    377 		comment_seq = openfile->syntax->comment;
    378 
    379 	if (*comment_seq == '\0') {
    380 		statusline(AHEM, _("Commenting is not supported for this file type"));
    381 		return;
    382 	}
    383 #endif
    384 
    385 	/* Determine which lines to work on. */
    386 	get_range(&top, &bot);
    387 
    388 	/* If only the magic line is selected, don't do anything. */
    389 	if (top == bot && bot == openfile->filebot && !ISSET(NO_NEWLINES)) {
    390 		statusline(AHEM, _("Cannot comment past end of file"));
    391 		return;
    392 	}
    393 
    394 	/* Figure out whether to comment or uncomment the selected line or lines. */
    395 	for (line = top; line != bot->next; line = line->next) {
    396 		empty = white_string(line->data);
    397 
    398 		/* If this line is not blank and not commented, we comment all. */
    399 		if (!empty && !comment_line(PREFLIGHT, line, comment_seq)) {
    400 			action = COMMENT;
    401 			break;
    402 		}
    403 		all_empty = all_empty && empty;
    404 	}
    405 
    406 	/* If all selected lines are blank, we comment them. */
    407 	action = all_empty ? COMMENT : action;
    408 
    409 	add_undo(action, NULL);
    410 
    411 	/* Store the comment sequence used for the operation, because it could
    412 	 * change when the file name changes; we need to know what it was. */
    413 	openfile->current_undo->strdata = copy_of(comment_seq);
    414 
    415 	/* Comment/uncomment each of the selected lines when possible, and
    416 	 * store undo data when a line changed. */
    417 	for (line = top; line != bot->next; line = line->next)
    418 		if (comment_line(action, line, comment_seq))
    419 			update_multiline_undo(line->lineno, "");
    420 
    421 	set_modified();
    422 	ensure_firstcolumn_is_aligned();
    423 	refresh_needed = TRUE;
    424 	shift_held = TRUE;
    425 }
    426 
    427 /* Perform an undo or redo for a comment or uncomment action. */
    428 void handle_comment_action(undostruct *u, bool undoing, bool add_comment)
    429 {
    430 	groupstruct *group = u->grouping;
    431 
    432 	/* When redoing, reposition the cursor and let the commenter adjust it. */
    433 	if (!undoing)
    434 		goto_line_posx(u->head_lineno, u->head_x);
    435 
    436 	while (group) {
    437 		linestruct *line = line_from_number(group->top_line);
    438 
    439 		while (line != NULL && line->lineno <= group->bottom_line) {
    440 			comment_line(undoing ^ add_comment ?
    441 								COMMENT : UNCOMMENT, line, u->strdata);
    442 			line = line->next;
    443 		}
    444 
    445 		group = group->next;
    446 	}
    447 
    448 	/* When undoing, reposition the cursor to the recorded location. */
    449 	if (undoing)
    450 		goto_line_posx(u->head_lineno, u->head_x);
    451 
    452 	refresh_needed = TRUE;
    453 }
    454 #endif /* ENABLE_COMMENT */
    455 
    456 #ifndef NANO_TINY
    457 #define redo_paste  undo_cut
    458 #define undo_paste  redo_cut
    459 
    460 /* Undo a cut, or redo a paste. */
    461 void undo_cut(undostruct *u)
    462 {
    463 	goto_line_posx(u->head_lineno, (u->xflags & WAS_WHOLE_LINE) ? 0 : u->head_x);
    464 
    465 	/* Clear an inherited anchor but not a user-placed one. */
    466 	if (!(u->xflags & HAD_ANCHOR_AT_START))
    467 		openfile->current->has_anchor = FALSE;
    468 
    469 	if (u->cutbuffer)
    470 		copy_from_buffer(u->cutbuffer);
    471 
    472 	/* If originally the last line was cut too, remove an extra magic line. */
    473 	if ((u->xflags & INCLUDED_LAST_LINE) && !ISSET(NO_NEWLINES) &&
    474 						openfile->filebot != openfile->current &&
    475 						openfile->filebot->prev->data[0] == '\0')
    476 		remove_magicline();
    477 
    478 	if (u->xflags & CURSOR_WAS_AT_HEAD)
    479 		goto_line_posx(u->head_lineno, u->head_x);
    480 }
    481 
    482 /* Redo a cut, or undo a paste. */
    483 void redo_cut(undostruct *u)
    484 {
    485 	linestruct *oldcutbuffer = cutbuffer;
    486 
    487 	cutbuffer = NULL;
    488 
    489 	openfile->mark = line_from_number(u->head_lineno);
    490 	openfile->mark_x = (u->xflags & WAS_WHOLE_LINE) ? 0 : u->head_x;
    491 
    492 	goto_line_posx(u->tail_lineno, u->tail_x);
    493 
    494 	do_snip(TRUE, FALSE, u->type == ZAP);
    495 
    496 	free_lines(cutbuffer);
    497 	cutbuffer = oldcutbuffer;
    498 }
    499 
    500 /* Undo the last thing(s) we did. */
    501 void do_undo(void)
    502 {
    503 	undostruct *u = openfile->current_undo;
    504 	linestruct *oldcutbuffer, *intruder;
    505 	linestruct *line = NULL;
    506 	size_t original_x, regain_from_x;
    507 	char *undidmsg = NULL;
    508 	char *data;
    509 
    510 	if (u == NULL) {
    511 		statusline(AHEM, _("Nothing to undo"));
    512 		return;
    513 	}
    514 
    515 	if (u->type <= REPLACE)
    516 		line = line_from_number(u->tail_lineno);
    517 
    518 	switch (u->type) {
    519 	case ADD:
    520 		/* TRANSLATORS: The next thirteen strings describe actions
    521 		 * that are undone or redone.  They are all nouns, not verbs. */
    522 		undidmsg = _("addition");
    523 		if ((u->xflags & INCLUDED_LAST_LINE) && !ISSET(NO_NEWLINES))
    524 			remove_magicline();
    525 		memmove(line->data + u->head_x, line->data + u->head_x + strlen(u->strdata),
    526 						strlen(line->data + u->head_x) - strlen(u->strdata) + 1);
    527 		goto_line_posx(u->head_lineno, u->head_x);
    528 		break;
    529 	case ENTER:
    530 		undidmsg = _("line break");
    531 		/* An <Enter> at the end of leading whitespace while autoindenting has
    532 		 * deleted the whitespace, and stored an x position of zero.  In that
    533 		 * case, adjust the positions to return to and to scoop data from. */
    534 		original_x = (u->head_x == 0) ? u->tail_x : u->head_x;
    535 		regain_from_x = (u->head_x == 0) ? 0 : u->tail_x;
    536 		line->data = nrealloc(line->data, strlen(line->data) +
    537 								strlen(&u->strdata[regain_from_x]) + 1);
    538 		strcat(line->data, &u->strdata[regain_from_x]);
    539 		line->has_anchor |= line->next->has_anchor;
    540 		unlink_node(line->next);
    541 		renumber_from(line);
    542 		openfile->current = line;
    543 		goto_line_posx(u->head_lineno, original_x);
    544 		break;
    545 	case BACK:
    546 	case DEL:
    547 		undidmsg = _("deletion");
    548 		data = nmalloc(strlen(line->data) + strlen(u->strdata) + 1);
    549 		strncpy(data, line->data, u->head_x);
    550 		strcpy(&data[u->head_x], u->strdata);
    551 		strcpy(&data[u->head_x + strlen(u->strdata)], &line->data[u->head_x]);
    552 		free(line->data);
    553 		line->data = data;
    554 		goto_line_posx(u->tail_lineno, u->tail_x);
    555 		break;
    556 	case JOIN:
    557 		undidmsg = _("line join");
    558 		/* When the join was done by a Backspace at the tail of the file,
    559 		 * and the nonewlines flag isn't set, do not re-add a newline that
    560 		 * wasn't actually deleted; just position the cursor. */
    561 		if ((u->xflags & WAS_BACKSPACE_AT_EOF) && !ISSET(NO_NEWLINES)) {
    562 	        goto_line_posx(openfile->filebot->lineno, 0);
    563 			focusing = FALSE;
    564 			break;
    565 		}
    566 		line->data[u->tail_x] = '\0';
    567 		intruder = make_new_node(line);
    568 		intruder->data = copy_of(u->strdata);
    569 		splice_node(line, intruder);
    570 		renumber_from(intruder);
    571 		goto_line_posx(u->head_lineno, u->head_x);
    572 		break;
    573 	case REPLACE:
    574 		undidmsg = _("replacement");
    575 		data = u->strdata;
    576 		u->strdata = line->data;
    577 		line->data = data;
    578 		goto_line_posx(u->head_lineno, u->head_x);
    579 		break;
    580 #ifdef ENABLE_WRAPPING
    581 	case SPLIT_BEGIN:
    582 		undidmsg = _("addition");
    583 		break;
    584 	case SPLIT_END:
    585 		openfile->current_undo = openfile->current_undo->next;
    586 		while (openfile->current_undo->type != SPLIT_BEGIN)
    587 			do_undo();
    588 		u = openfile->current_undo;
    589 		break;
    590 #endif
    591 	case ZAP:
    592 		undidmsg = _("erasure");
    593 		undo_cut(u);
    594 		break;
    595 	case CUT_TO_EOF:
    596 	case CUT:
    597 		/* TRANSLATORS: Remember: these are nouns, NOT verbs. */
    598 		undidmsg = _("cut");
    599 		undo_cut(u);
    600 		break;
    601 	case PASTE:
    602 		undidmsg = _("paste");
    603 		undo_paste(u);
    604 		if ((u->xflags & INCLUDED_LAST_LINE) && !ISSET(NO_NEWLINES) &&
    605 							openfile->filebot != openfile->current)
    606 			remove_magicline();
    607 		break;
    608 	case INSERT:
    609 		undidmsg = _("insertion");
    610 		oldcutbuffer = cutbuffer;
    611 		cutbuffer = NULL;
    612 		goto_line_posx(u->head_lineno, u->head_x);
    613 		openfile->mark = line_from_number(u->tail_lineno);
    614 		openfile->mark_x = u->tail_x;
    615 		cut_marked_region();
    616 		u->cutbuffer = cutbuffer;
    617 		cutbuffer = oldcutbuffer;
    618 		if ((u->xflags & INCLUDED_LAST_LINE) && !ISSET(NO_NEWLINES) &&
    619 							openfile->filebot != openfile->current)
    620 			remove_magicline();
    621 		break;
    622 	case COUPLE_BEGIN:
    623 		undidmsg = u->strdata;
    624 		goto_line_posx(u->head_lineno, u->head_x);
    625 		openfile->cursor_row = u->tail_lineno;
    626 		adjust_viewport(STATIONARY);
    627 		break;
    628 	case COUPLE_END:
    629 		/* Remember the row of the cursor for a possible redo. */
    630 		openfile->current_undo->head_lineno = openfile->cursor_row;
    631 		openfile->current_undo = openfile->current_undo->next;
    632 		do_undo();
    633 		do_undo();
    634 		do_undo();
    635 		return;
    636 	case INDENT:
    637 		handle_indent_action(u, TRUE, TRUE);
    638 		undidmsg = _("indent");
    639 		break;
    640 	case UNINDENT:
    641 		handle_indent_action(u, TRUE, FALSE);
    642 		undidmsg = _("unindent");
    643 		break;
    644 #ifdef ENABLE_COMMENT
    645 	case COMMENT:
    646 		handle_comment_action(u, TRUE, TRUE);
    647 		undidmsg = _("comment");
    648 		break;
    649 	case UNCOMMENT:
    650 		handle_comment_action(u, TRUE, FALSE);
    651 		undidmsg = _("uncomment");
    652 		break;
    653 #endif
    654 	default:
    655 		break;
    656 	}
    657 
    658 	if (undidmsg && !ISSET(ZERO) && !pletion_line)
    659 		statusline(HUSH, _("Undid %s"), undidmsg);
    660 
    661 	openfile->current_undo = openfile->current_undo->next;
    662 	openfile->last_action = OTHER;
    663 	openfile->mark = NULL;
    664 	openfile->placewewant = xplustabs();
    665 
    666 	openfile->totsize = u->wassize;
    667 
    668 #ifdef ENABLE_COLOR
    669 	if (u->type <= REPLACE)
    670 		check_the_multis(openfile->current);
    671 	else if (u->type == INSERT || u->type == COUPLE_BEGIN)
    672 		recook = TRUE;
    673 #endif
    674 
    675 	/* When at the point where the buffer was last saved, unset "Modified". */
    676 	if (openfile->current_undo == openfile->last_saved) {
    677 		openfile->modified = FALSE;
    678 		titlebar(NULL);
    679 	} else
    680 		set_modified();
    681 }
    682 
    683 /* Redo the last thing(s) we undid. */
    684 void do_redo(void)
    685 {
    686 	undostruct *u = openfile->undotop;
    687 	bool suppress_modification = FALSE;
    688 	linestruct *line = NULL;
    689 	linestruct *intruder;
    690 	char *redidmsg = NULL;
    691 	char *data;
    692 
    693 	if (u == NULL || u == openfile->current_undo) {
    694 		statusline(AHEM, _("Nothing to redo"));
    695 		return;
    696 	}
    697 
    698 	/* Find the item before the current one in the undo stack. */
    699 	while (u->next != openfile->current_undo)
    700 		u = u->next;
    701 
    702 	if (u->type <= REPLACE)
    703 		line = line_from_number(u->tail_lineno);
    704 
    705 	switch (u->type) {
    706 	case ADD:
    707 		redidmsg = _("addition");
    708 		if ((u->xflags & INCLUDED_LAST_LINE) && !ISSET(NO_NEWLINES))
    709 			new_magicline();
    710 		data = nmalloc(strlen(line->data) + strlen(u->strdata) + 1);
    711 		strncpy(data, line->data, u->head_x);
    712 		strcpy(&data[u->head_x], u->strdata);
    713 		strcpy(&data[u->head_x + strlen(u->strdata)], &line->data[u->head_x]);
    714 		free(line->data);
    715 		line->data = data;
    716 		goto_line_posx(u->tail_lineno, u->tail_x);
    717 		break;
    718 	case ENTER:
    719 		redidmsg = _("line break");
    720 		line->data[u->head_x] = '\0';
    721 		intruder = make_new_node(line);
    722 		intruder->data = copy_of(u->strdata);
    723 		splice_node(line, intruder);
    724 		renumber_from(intruder);
    725 		goto_line_posx(u->head_lineno + 1, u->tail_x);
    726 		break;
    727 	case BACK:
    728 	case DEL:
    729 		redidmsg = _("deletion");
    730 		memmove(line->data + u->head_x, line->data + u->head_x + strlen(u->strdata),
    731 						strlen(line->data + u->head_x) - strlen(u->strdata) + 1);
    732 		goto_line_posx(u->head_lineno, u->head_x);
    733 		break;
    734 	case JOIN:
    735 		redidmsg = _("line join");
    736 		/* When the join was done by a Backspace at the tail of the file,
    737 		 * and the nonewlines flag isn't set, do not join anything, as
    738 		 * nothing was actually deleted; just position the cursor. */
    739 		if ((u->xflags & WAS_BACKSPACE_AT_EOF) && !ISSET(NO_NEWLINES)) {
    740 			goto_line_posx(u->tail_lineno, u->tail_x);
    741 			break;
    742 		}
    743 		line->data = nrealloc(line->data, strlen(line->data) + strlen(u->strdata) + 1);
    744 		strcat(line->data, u->strdata);
    745 		unlink_node(line->next);
    746 		renumber_from(line);
    747 		openfile->current = line;
    748 		goto_line_posx(u->tail_lineno, u->tail_x);
    749 		break;
    750 	case REPLACE:
    751 		redidmsg = _("replacement");
    752 		data = u->strdata;
    753 		u->strdata = line->data;
    754 		line->data = data;
    755 		goto_line_posx(u->head_lineno, u->head_x);
    756 		break;
    757 #ifdef ENABLE_WRAPPING
    758 	case SPLIT_BEGIN:
    759 		openfile->current_undo = u;
    760 		while (openfile->current_undo->type != SPLIT_END)
    761 			do_redo();
    762 		u = openfile->current_undo;
    763 		goto_line_posx(u->head_lineno, u->head_x);
    764 		ensure_firstcolumn_is_aligned();
    765 		break;
    766 	case SPLIT_END:
    767 		redidmsg = _("addition");
    768 		break;
    769 #endif
    770 	case ZAP:
    771 		redidmsg = _("erasure");
    772 		redo_cut(u);
    773 		break;
    774 	case CUT_TO_EOF:
    775 	case CUT:
    776 		redidmsg = _("cut");
    777 		redo_cut(u);
    778 		break;
    779 	case PASTE:
    780 		redidmsg = _("paste");
    781 		redo_paste(u);
    782 		break;
    783 	case INSERT:
    784 		redidmsg = _("insertion");
    785 		goto_line_posx(u->head_lineno, u->head_x);
    786 		if (u->cutbuffer)
    787 			copy_from_buffer(u->cutbuffer);
    788 		else
    789 			suppress_modification = TRUE;
    790 		free_lines(u->cutbuffer);
    791 		u->cutbuffer = NULL;
    792 		break;
    793 	case COUPLE_BEGIN:
    794 		openfile->current_undo = u;
    795 		do_redo();
    796 		do_redo();
    797 		do_redo();
    798 		return;
    799 	case COUPLE_END:
    800 		redidmsg = u->strdata;
    801 		goto_line_posx(u->tail_lineno, u->tail_x);
    802 		openfile->cursor_row = u->head_lineno;
    803 		adjust_viewport(STATIONARY);
    804 		break;
    805 	case INDENT:
    806 		handle_indent_action(u, FALSE, TRUE);
    807 		redidmsg = _("indent");
    808 		break;
    809 	case UNINDENT:
    810 		handle_indent_action(u, FALSE, FALSE);
    811 		redidmsg = _("unindent");
    812 		break;
    813 #ifdef ENABLE_COMMENT
    814 	case COMMENT:
    815 		handle_comment_action(u, FALSE, TRUE);
    816 		redidmsg = _("comment");
    817 		break;
    818 	case UNCOMMENT:
    819 		handle_comment_action(u, FALSE, FALSE);
    820 		redidmsg = _("uncomment");
    821 		break;
    822 #endif
    823 	default:
    824 		break;
    825 	}
    826 
    827 	if (redidmsg && !ISSET(ZERO))
    828 		statusline(HUSH, _("Redid %s"), redidmsg);
    829 
    830 	openfile->current_undo = u;
    831 	openfile->last_action = OTHER;
    832 	openfile->mark = NULL;
    833 	openfile->placewewant = xplustabs();
    834 
    835 	openfile->totsize = u->newsize;
    836 
    837 #ifdef ENABLE_COLOR
    838 	if (u->type <= REPLACE)
    839 		check_the_multis(openfile->current);
    840 	else if (u->type == INSERT || u->type == COUPLE_END)
    841 		recook = TRUE;
    842 #endif
    843 
    844 	/* When at the point where the buffer was last saved, unset "Modified". */
    845 	if (openfile->current_undo == openfile->last_saved) {
    846 		openfile->modified = FALSE;
    847 		titlebar(NULL);
    848 	} else if (!suppress_modification)
    849 		set_modified();
    850 }
    851 #endif /* !NANO_TINY */
    852 
    853 /* Break the current line at the cursor position. */
    854 void do_enter(void)
    855 {
    856 	linestruct *newnode = make_new_node(openfile->current);
    857 	size_t extra = 0;
    858 #ifndef NANO_TINY
    859 	linestruct *sampleline = openfile->current;
    860 	bool allblanks = FALSE;
    861 
    862 	if (ISSET(AUTOINDENT)) {
    863 #ifdef ENABLE_JUSTIFY
    864 		/* When doing automatic long-line wrapping and the next line is
    865 		 * in this same paragraph, use its indentation as the model. */
    866 		if (ISSET(BREAK_LONG_LINES) && sampleline->next != NULL &&
    867 					inpar(sampleline->next) && !begpar(sampleline->next, 0))
    868 			sampleline = sampleline->next;
    869 #endif
    870 		extra = indent_length(sampleline->data);
    871 
    872 		/* When breaking in the indentation, limit the automatic one. */
    873 		if (extra > openfile->current_x)
    874 			extra = openfile->current_x;
    875 		else if (extra == openfile->current_x)
    876 			allblanks = (indent_length(openfile->current->data) == extra);
    877 	}
    878 #endif /* NANO_TINY */
    879 	newnode->data = nmalloc(strlen(openfile->current->data +
    880 										openfile->current_x) + extra + 1);
    881 	strcpy(&newnode->data[extra], openfile->current->data +
    882 										openfile->current_x);
    883 #ifndef NANO_TINY
    884 	/* Adjust the mark if it is on the current line after the cursor. */
    885 	if (openfile->mark == openfile->current &&
    886 				openfile->mark_x > openfile->current_x) {
    887 		openfile->mark = newnode;
    888 		openfile->mark_x += extra - openfile->current_x;
    889 	}
    890 
    891 	if (ISSET(AUTOINDENT)) {
    892 		/* Copy the whitespace from the sample line to the new one. */
    893 		strncpy(newnode->data, sampleline->data, extra);
    894 		/* If there were only blanks before the cursor, trim them. */
    895 		if (allblanks)
    896 			openfile->current_x = 0;
    897 	}
    898 #endif
    899 
    900 	/* Make the current line end at the cursor position. */
    901 	openfile->current->data[openfile->current_x] = '\0';
    902 
    903 #ifndef NANO_TINY
    904 	add_undo(ENTER, NULL);
    905 #endif
    906 
    907 	/* Insert the newly created line after the current one and renumber. */
    908 	splice_node(openfile->current, newnode);
    909 	renumber_from(newnode);
    910 
    911 	/* Put the cursor on the new line, after any automatic whitespace. */
    912 	openfile->current = newnode;
    913 	openfile->current_x = extra;
    914 	openfile->placewewant = xplustabs();
    915 
    916 	openfile->totsize++;
    917 	set_modified();
    918 
    919 #ifndef NANO_TINY
    920 	if (ISSET(AUTOINDENT) && !allblanks)
    921 		openfile->totsize += extra;
    922 	update_undo(ENTER);
    923 #endif
    924 
    925 	refresh_needed = TRUE;
    926 	focusing = FALSE;
    927 }
    928 
    929 #ifndef NANO_TINY
    930 /* Discard undo items that are newer than the given one, or all if NULL. */
    931 void discard_until(const undostruct *thisitem)
    932 {
    933 	undostruct *dropit = openfile->undotop;
    934 	groupstruct *group;
    935 
    936 	while (dropit != NULL && dropit != thisitem) {
    937 		openfile->undotop = dropit->next;
    938 		free(dropit->strdata);
    939 		free_lines(dropit->cutbuffer);
    940 		group = dropit->grouping;
    941 		while (group != NULL) {
    942 			groupstruct *next = group->next;
    943 			free_chararray(group->indentations,
    944 								group->bottom_line - group->top_line + 1);
    945 			free(group);
    946 			group = next;
    947 		}
    948 		free(dropit);
    949 		dropit = openfile->undotop;
    950 	}
    951 
    952 	/* Adjust the pointer to the top of the undo stack. */
    953 	openfile->current_undo = (undostruct *)thisitem;
    954 
    955 	/* Prevent a chain of editing actions from continuing. */
    956 	openfile->last_action = OTHER;
    957 }
    958 
    959 /* Add a new undo item of the given type to the top of the current pile. */
    960 void add_undo(undo_type action, const char *message)
    961 {
    962 	undostruct *u = nmalloc(sizeof(undostruct));
    963 	linestruct *thisline = openfile->current;
    964 
    965 	/* Initialize the newly allocated undo item. */
    966 	u->type = action;
    967 	u->strdata = NULL;
    968 	u->cutbuffer = NULL;
    969 	u->head_lineno = thisline->lineno;
    970 	u->head_x = openfile->current_x;
    971 	u->tail_lineno = thisline->lineno;
    972 	u->tail_x = openfile->current_x;
    973 	u->wassize = openfile->totsize;
    974 	u->newsize = openfile->totsize;
    975 	u->grouping = NULL;
    976 	u->xflags = 0;
    977 
    978 	/* Blow away any undone items. */
    979 	discard_until(openfile->current_undo);
    980 
    981 #ifdef ENABLE_WRAPPING
    982 	/* If some action caused automatic long-line wrapping, insert the
    983 	 * SPLIT_BEGIN item underneath that action's undo item.  Otherwise,
    984 	 * just add the new item to the top of the undo stack. */
    985 	if (u->type == SPLIT_BEGIN) {
    986 		action = openfile->undotop->type;
    987 		u->wassize = openfile->undotop->wassize;
    988 		u->next = openfile->undotop->next;
    989 		openfile->undotop->next = u;
    990 	} else
    991 #endif
    992 	{
    993 		u->next = openfile->undotop;
    994 		openfile->undotop = u;
    995 		openfile->current_undo = u;
    996 	}
    997 
    998 	/* Record the info needed to be able to undo each possible action. */
    999 	switch (u->type) {
   1000 	case ADD:
   1001 		/* If a new magic line will be added, an undo should remove it. */
   1002 		if (thisline == openfile->filebot)
   1003 			u->xflags |= INCLUDED_LAST_LINE;
   1004 		break;
   1005 	case ENTER:
   1006 		break;
   1007 	case BACK:
   1008 		/* If the next line is the magic line, don't ever undo this
   1009 		 * backspace, as it won't actually have deleted anything. */
   1010 		if (thisline->next == openfile->filebot && thisline->data[0] != '\0')
   1011 			u->xflags |= WAS_BACKSPACE_AT_EOF;
   1012 		/* Fall-through. */
   1013 	case DEL:
   1014 		/* When not at the end of a line, store the deleted character;
   1015 		 * otherwise, morph the undo item into a line join. */
   1016 		if (thisline->data[openfile->current_x] != '\0') {
   1017 			int charlen = char_length(thisline->data + u->head_x);
   1018 
   1019 			u->strdata = measured_copy(thisline->data + u->head_x, charlen);
   1020 			if (u->type == BACK)
   1021 				u->tail_x += charlen;
   1022 			break;
   1023 		}
   1024 		action = JOIN;
   1025 		if (thisline->next != NULL) {
   1026 			if (u->type == BACK) {
   1027 				u->head_lineno = thisline->next->lineno;
   1028 				u->head_x = 0;
   1029 			}
   1030 			u->strdata = copy_of(thisline->next->data);
   1031 		}
   1032 		u->type = JOIN;
   1033 		break;
   1034 	case REPLACE:
   1035 		u->strdata = copy_of(thisline->data);
   1036 		break;
   1037 #ifdef ENABLE_WRAPPING
   1038 	case SPLIT_BEGIN:
   1039 	case SPLIT_END:
   1040 		break;
   1041 #endif
   1042 	case CUT_TO_EOF:
   1043 		u->xflags |= (INCLUDED_LAST_LINE | CURSOR_WAS_AT_HEAD);
   1044 		if (openfile->current->has_anchor)
   1045 			u->xflags |= HAD_ANCHOR_AT_START;
   1046 		break;
   1047 	case ZAP:
   1048 	case CUT:
   1049 		if (openfile->mark) {
   1050 			if (mark_is_before_cursor()){
   1051 				u->head_lineno = openfile->mark->lineno;
   1052 				u->head_x = openfile->mark_x;
   1053 				u->xflags |= MARK_WAS_SET;
   1054 			} else {
   1055 				u->tail_lineno = openfile->mark->lineno;
   1056 				u->tail_x = openfile->mark_x;
   1057 				u->xflags |= (MARK_WAS_SET | CURSOR_WAS_AT_HEAD);
   1058 			}
   1059 			if (u->tail_lineno == openfile->filebot->lineno)
   1060 				u->xflags |= INCLUDED_LAST_LINE;
   1061 		} else if (!ISSET(CUT_FROM_CURSOR)) {
   1062 			/* The entire line is being cut regardless of the cursor position. */
   1063 			u->xflags |= (WAS_WHOLE_LINE | CURSOR_WAS_AT_HEAD);
   1064 			u->tail_x = 0;
   1065 		} else
   1066 			u->xflags |= CURSOR_WAS_AT_HEAD;
   1067 		if ((openfile->mark && mark_is_before_cursor() && openfile->mark->has_anchor) ||
   1068 				((!openfile->mark || !mark_is_before_cursor()) && openfile->current->has_anchor))
   1069 			u->xflags |= HAD_ANCHOR_AT_START;
   1070 		break;
   1071 	case PASTE:
   1072 		u->cutbuffer = copy_buffer(cutbuffer);
   1073 		/* Fall-through. */
   1074 	case INSERT:
   1075 		if (thisline == openfile->filebot)
   1076 			u->xflags |= INCLUDED_LAST_LINE;
   1077 		break;
   1078 	case COUPLE_BEGIN:
   1079 		u->tail_lineno = openfile->cursor_row;
   1080 		/* Fall-through. */
   1081 	case COUPLE_END:
   1082 		u->strdata = copy_of(_(message));
   1083 		break;
   1084 	case INDENT:
   1085 	case UNINDENT:
   1086 #ifdef ENABLE_COMMENT
   1087 	case COMMENT:
   1088 	case UNCOMMENT:
   1089 #endif
   1090 		break;
   1091 	default:
   1092 		die("Bad undo type -- please report a bug\n");
   1093 	}
   1094 
   1095 	openfile->last_action = action;
   1096 }
   1097 
   1098 /* Update a multiline undo item.  This should be called once for each line
   1099  * affected by a multiple-line-altering feature.  The indentation that is
   1100  * added or removed is saved separately for each line in the undo item. */
   1101 void update_multiline_undo(ssize_t lineno, char *indentation)
   1102 {
   1103 	undostruct *u = openfile->current_undo;
   1104 
   1105 	/* If there already is a group and the current line is contiguous with it,
   1106 	 * extend the group; otherwise, create a new group. */
   1107 	if (u->grouping && u->grouping->bottom_line + 1 == lineno) {
   1108 		size_t number_of_lines = lineno - u->grouping->top_line + 1;
   1109 
   1110 		u->grouping->bottom_line = lineno;
   1111 
   1112 		u->grouping->indentations = nrealloc(u->grouping->indentations,
   1113 										number_of_lines * sizeof(char *));
   1114 		u->grouping->indentations[number_of_lines - 1] = copy_of(indentation);
   1115 	} else {
   1116 		groupstruct *born = nmalloc(sizeof(groupstruct));
   1117 
   1118 		born->top_line = lineno;
   1119 		born->bottom_line = lineno;
   1120 
   1121 		born->indentations = nmalloc(sizeof(char *));
   1122 		born->indentations[0] = copy_of(indentation);
   1123 
   1124 		born->next = u->grouping;
   1125 		u->grouping = born;
   1126 	}
   1127 
   1128 	/* Store the file size after the change, to be used when redoing. */
   1129 	u->newsize = openfile->totsize;
   1130 }
   1131 
   1132 /* Update an undo item with (among other things) the file size and
   1133  * cursor position after the given action. */
   1134 void update_undo(undo_type action)
   1135 {
   1136 	undostruct *u = openfile->undotop;
   1137 	size_t datalen, newlen;
   1138 	char *textposition;
   1139 	int charlen;
   1140 
   1141 	if (u->type != action)
   1142 		die("Mismatching undo type -- please report a bug\n");
   1143 
   1144 	u->newsize = openfile->totsize;
   1145 
   1146 	switch (u->type) {
   1147 	case ADD:
   1148 		newlen = openfile->current_x - u->head_x;
   1149 		u->strdata = nrealloc(u->strdata, newlen + 1);
   1150 		strncpy(u->strdata, openfile->current->data + u->head_x, newlen);
   1151 		u->strdata[newlen] = '\0';
   1152 		u->tail_x = openfile->current_x;
   1153 		break;
   1154 	case ENTER:
   1155 		u->strdata = copy_of(openfile->current->data);
   1156 		u->tail_x = openfile->current_x;
   1157 		break;
   1158 	case BACK:
   1159 	case DEL:
   1160 		textposition = openfile->current->data + openfile->current_x;
   1161 		charlen = char_length(textposition);
   1162 		datalen = strlen(u->strdata);
   1163 		if (openfile->current_x == u->head_x) {
   1164 			/* They deleted more: add removed character after earlier stuff. */
   1165 			u->strdata = nrealloc(u->strdata, datalen + charlen + 1);
   1166 			strncpy(u->strdata + datalen, textposition, charlen);
   1167 			u->strdata[datalen + charlen] = '\0';
   1168 			u->tail_x = openfile->current_x;
   1169 		} else if (openfile->current_x == u->head_x - charlen) {
   1170 			/* They backspaced further: add removed character before earlier. */
   1171 			u->strdata = nrealloc(u->strdata, datalen + charlen + 1);
   1172 			memmove(u->strdata + charlen, u->strdata, datalen + 1);
   1173 			strncpy(u->strdata, textposition, charlen);
   1174 			u->head_x = openfile->current_x;
   1175 		} else
   1176 			/* They deleted *elsewhere* on the line: start a new undo item. */
   1177 			add_undo(u->type, NULL);
   1178 		break;
   1179 	case REPLACE:
   1180 		break;
   1181 #ifdef ENABLE_WRAPPING
   1182 	case SPLIT_BEGIN:
   1183 	case SPLIT_END:
   1184 		break;
   1185 #endif
   1186 	case ZAP:
   1187 	case CUT_TO_EOF:
   1188 	case CUT:
   1189 		if (u->type == ZAP)
   1190 			u->cutbuffer = cutbuffer;
   1191 		else if (cutbuffer != NULL) {
   1192 			free_lines(u->cutbuffer);
   1193 			u->cutbuffer = copy_buffer(cutbuffer);
   1194 		} else
   1195 			break;
   1196 		if (!(u->xflags & MARK_WAS_SET)) {
   1197 			linestruct *bottomline = u->cutbuffer;
   1198 			size_t count = 0;
   1199 
   1200 			/* Find the end of the cut for the undo/redo, using our copy. */
   1201 			while (bottomline->next != NULL) {
   1202 				bottomline = bottomline->next;
   1203 				count++;
   1204 			}
   1205 			u->tail_lineno = u->head_lineno + count;
   1206 			if (ISSET(CUT_FROM_CURSOR) || u->type == CUT_TO_EOF) {
   1207 				u->tail_x = strlen(bottomline->data);
   1208 				if (count == 0)
   1209 					u->tail_x += u->head_x;
   1210 			} else if (openfile->current == openfile->filebot && ISSET(NO_NEWLINES))
   1211 				u->tail_x = strlen(bottomline->data);
   1212 		}
   1213 		break;
   1214 	case COUPLE_BEGIN:
   1215 		break;
   1216 	case COUPLE_END:
   1217 	case PASTE:
   1218 	case INSERT:
   1219 		u->tail_lineno = openfile->current->lineno;
   1220 		u->tail_x = openfile->current_x;
   1221 		break;
   1222 	default:
   1223 		die("Bad undo type -- please report a bug\n");
   1224 	}
   1225 }
   1226 #endif /* !NANO_TINY */
   1227 
   1228 #ifdef ENABLE_WRAPPING
   1229 /* When the current line is overlong, hard-wrap it at the furthest possible
   1230  * whitespace character, and prepend the excess part to an "overflow" line
   1231  * (when it already exists, otherwise create one). */
   1232 void do_wrap(void)
   1233 {
   1234 	linestruct *line = openfile->current;
   1235 		/* The line to be wrapped, if needed and possible. */
   1236 	size_t line_len = strlen(line->data);
   1237 		/* The length of this line. */
   1238 #ifdef ENABLE_JUSTIFY
   1239 	size_t quot_len = quote_length(line->data);
   1240 		/* The length of the quoting part of this line. */
   1241 	size_t lead_len = quot_len + indent_length(line->data + quot_len);
   1242 		/* The length of the quoting part plus subsequent whitespace. */
   1243 #else
   1244 	size_t lead_len = indent_length(line->data);
   1245 #endif
   1246 	size_t cursor_x = openfile->current_x;
   1247 		/* The current cursor position, for comparison with the wrap point. */
   1248 	ssize_t wrap_loc;
   1249 		/* The position in the line's text where we wrap. */
   1250 	const char *remainder;
   1251 		/* The text after the wrap point. */
   1252 	size_t rest_length;
   1253 		/* The length of the remainder. */
   1254 
   1255 	/* First find the last blank character where we can break the line. */
   1256 	wrap_loc = break_line(line->data + lead_len,
   1257 							wrap_at - wideness(line->data, lead_len), FALSE);
   1258 
   1259 	/* If no wrapping point was found before end-of-line, we don't wrap. */
   1260 	if (wrap_loc < 0 || lead_len + wrap_loc == line_len)
   1261 		return;
   1262 
   1263 	/* Adjust the wrap location to its position in the full line,
   1264 	 * and step forward to the character just after the blank. */
   1265 	wrap_loc = lead_len + step_right(line->data + lead_len, wrap_loc);
   1266 
   1267 	/* When now at end-of-line, no need to wrap. */
   1268 	if (line->data[wrap_loc] == '\0')
   1269 		return;
   1270 
   1271 #ifndef NANO_TINY
   1272 	add_undo(SPLIT_BEGIN, NULL);
   1273 #endif
   1274 #ifdef ENABLE_JUSTIFY
   1275 	bool autowhite = ISSET(AUTOINDENT);
   1276 
   1277 	if (quot_len > 0)
   1278 		UNSET(AUTOINDENT);
   1279 #endif
   1280 
   1281 	/* The remainder is the text that will be wrapped to the next line. */
   1282 	remainder = line->data + wrap_loc;
   1283 	rest_length = line_len - wrap_loc;
   1284 
   1285 	/* When prepending and the remainder of this line will not make the next
   1286 	 * line too long, then join the two lines, so that, after the line wrap,
   1287 	 * the remainder will effectively have been prefixed to the next line. */
   1288 	if (openfile->spillage_line && openfile->spillage_line == line->next &&
   1289 				rest_length + breadth(line->next->data) <= wrap_at) {
   1290 		/* Go to the end of this line. */
   1291 		openfile->current_x = line_len;
   1292 
   1293 		/* If the remainder doesn't end in a blank, add a space. */
   1294 		if (!is_blank_char(remainder + step_left(remainder, rest_length))) {
   1295 #ifndef NANO_TINY
   1296 			add_undo(ADD, NULL);
   1297 #endif
   1298 			line->data = nrealloc(line->data, line_len + 2);
   1299 			line->data[line_len] = ' ';
   1300 			line->data[line_len + 1] = '\0';
   1301 			rest_length++;
   1302 			openfile->totsize++;
   1303 			openfile->current_x++;
   1304 #ifndef NANO_TINY
   1305 			update_undo(ADD);
   1306 #endif
   1307 		}
   1308 
   1309 		/* Join the next line to this one. */
   1310 		expunge(DEL);
   1311 
   1312 #ifdef ENABLE_JUSTIFY
   1313 		/* If the leading part of the current line equals the leading part of
   1314 		 * what was the next line, then strip this second leading part. */
   1315 		if (strncmp(line->data, line->data + openfile->current_x, lead_len) == 0)
   1316 			for (size_t i = lead_len; i > 0; i--)
   1317 				expunge(DEL);
   1318 #endif
   1319 		/* Remove any extra blanks. */
   1320 		while (is_blank_char(&line->data[openfile->current_x]))
   1321 			expunge(DEL);
   1322 	}
   1323 
   1324 	/* Go to the wrap location. */
   1325 	openfile->current_x = wrap_loc;
   1326 
   1327 	/* When requested, snip trailing blanks off the wrapped line. */
   1328 	if (ISSET(TRIM_BLANKS)) {
   1329 		size_t rear_x = step_left(line->data, wrap_loc);
   1330 		size_t typed_x = step_left(line->data, cursor_x);
   1331 
   1332 		while ((rear_x != typed_x || cursor_x >= wrap_loc) &&
   1333 						is_blank_char(line->data + rear_x)) {
   1334 			openfile->current_x = rear_x;
   1335 			expunge(DEL);
   1336 			rear_x = step_left(line->data, rear_x);
   1337 		}
   1338 	}
   1339 
   1340 	/* Now split the line. */
   1341 	do_enter();
   1342 
   1343 #ifndef NANO_TINY
   1344 	/* When wrapping a partially visible line, adjust start-of-screen. */
   1345 	if (openfile->edittop == line && openfile->firstcolumn > 0 && cursor_x >= wrap_loc)
   1346 		go_forward_chunks(1, &openfile->edittop, &openfile->firstcolumn);
   1347 #endif
   1348 
   1349 #ifdef ENABLE_JUSTIFY
   1350 	/* If the original line has quoting, copy it to the spillage line. */
   1351 	if (quot_len > 0) {
   1352 		line = line->next;
   1353 		line_len = strlen(line->data);
   1354 		line->data = nrealloc(line->data, lead_len + line_len + 1);
   1355 
   1356 		memmove(line->data + lead_len, line->data, line_len + 1);
   1357 		strncpy(line->data, line->prev->data, lead_len);
   1358 
   1359 		openfile->current_x += lead_len;
   1360 		openfile->totsize += lead_len;
   1361 #ifndef NANO_TINY
   1362 		free(openfile->undotop->strdata);
   1363 		update_undo(ENTER);
   1364 #endif
   1365 		if (autowhite)
   1366 			SET(AUTOINDENT);
   1367 	}
   1368 #endif
   1369 
   1370 	openfile->spillage_line = openfile->current;
   1371 
   1372 	if (cursor_x < wrap_loc) {
   1373 		openfile->current = openfile->current->prev;
   1374 		openfile->current_x = cursor_x;
   1375 	} else
   1376 		openfile->current_x += (cursor_x - wrap_loc);
   1377 
   1378 	openfile->placewewant = xplustabs();
   1379 
   1380 #ifndef NANO_TINY
   1381 	add_undo(SPLIT_END, NULL);
   1382 #endif
   1383 
   1384 	refresh_needed = TRUE;
   1385 }
   1386 #endif /* ENABLE_WRAPPING */
   1387 
   1388 #if defined(ENABLE_HELP) || defined(ENABLED_WRAPORJUSTIFY)
   1389 /* Find the last blank in the given piece of text such that the display width
   1390  * to that point is at most (goal + 1).  When there is no such blank, then find
   1391  * the first blank.  Return the index of the last blank in that group of blanks.
   1392  * When snap_at_nl is TRUE, a newline character counts as a blank too. */
   1393 ssize_t break_line(const char *textstart, ssize_t goal, bool snap_at_nl)
   1394 {
   1395 	const char *lastblank = NULL;
   1396 		/* The point where the last blank was found, if any. */
   1397 	const char *pointer = textstart;
   1398 		/* An iterator through the given line of text. */
   1399 	size_t column = 0;
   1400 		/* The column number that corresponds to the position of the pointer. */
   1401 
   1402 	/* Skip over leading whitespace, where a line should never be broken. */
   1403 	while (*pointer != '\0' && is_blank_char(pointer))
   1404 		pointer += advance_over(pointer, &column);
   1405 
   1406 	/* Find the last blank that does not overshoot the target column.
   1407 	 * When treating a help text, do not break in the keystrokes area. */
   1408 	while (*pointer != '\0' && ((ssize_t)column <= goal)) {
   1409 		if (is_blank_char(pointer) && (!inhelp || column > 17 || goal < 40))
   1410 			lastblank = pointer;
   1411 #ifdef ENABLE_HELP
   1412 		else if (snap_at_nl && *pointer == '\n') {
   1413 			lastblank = pointer;
   1414 			break;
   1415 		}
   1416 #endif
   1417 		pointer += advance_over(pointer, &column);
   1418 	}
   1419 
   1420 	/* If the whole line displays shorter than goal, we're done. */
   1421 	if ((ssize_t)column <= goal)
   1422 		return (pointer - textstart);
   1423 
   1424 #ifdef ENABLE_HELP
   1425 	/* When wrapping a help text and no blank was found, force a line break. */
   1426 	if (snap_at_nl && lastblank == NULL)
   1427 		return step_left(textstart, pointer - textstart);
   1428 #endif
   1429 
   1430 	/* If no blank was found within the goal width, seek one after it. */
   1431 	while (lastblank == NULL) {
   1432 		if (*pointer == '\0')
   1433 			return -1;
   1434 
   1435 		if (is_blank_char(pointer))
   1436 			lastblank = pointer;
   1437 		else
   1438 			pointer += char_length(pointer);
   1439 	}
   1440 
   1441 	pointer = lastblank + char_length(lastblank);
   1442 
   1443 	/* Skip any consecutive blanks after the last blank. */
   1444 	while (*pointer != '\0' && is_blank_char(pointer)) {
   1445 		lastblank = pointer;
   1446 		pointer += char_length(pointer);
   1447 	}
   1448 
   1449 	return (lastblank - textstart);
   1450 }
   1451 #endif /* ENABLE_HELP || ENABLED_WRAPORJUSTIFY */
   1452 
   1453 #if !defined(NANO_TINY) || defined(ENABLED_WRAPORJUSTIFY)
   1454 /* Return the length of the indentation part of the given line.  The
   1455  * "indentation" of a line is the leading consecutive whitespace. */
   1456 size_t indent_length(const char *line)
   1457 {
   1458 	const char *start = line;
   1459 
   1460 	while (*line != '\0' && is_blank_char(line))
   1461 		line += char_length(line);
   1462 
   1463 	return (line - start);
   1464 }
   1465 #endif
   1466 
   1467 #ifdef ENABLE_JUSTIFY
   1468 /* Return the length of the quote part of the given line.  The "quote part"
   1469  * of a line is the largest initial substring matching the quoting regex. */
   1470 size_t quote_length(const char *line)
   1471 {
   1472 	regmatch_t matches;
   1473 	int rc = regexec(&quotereg, line, 1, &matches, 0);
   1474 
   1475 	if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
   1476 		return 0;
   1477 
   1478 	return matches.rm_eo;
   1479 }
   1480 
   1481 /* The maximum depth of recursion.  This must be an even number. */
   1482 #define RECURSION_LIMIT  222
   1483 
   1484 /* Return TRUE when the given line is the beginning of a paragraph (BOP). */
   1485 bool begpar(const linestruct *const line, int depth)
   1486 {
   1487 	size_t quot_len, indent_len, prev_dent_len;
   1488 
   1489 	/* The very first line counts as a BOP, even when it contains no text. */
   1490 	if (line->prev == NULL)
   1491 		return TRUE;
   1492 
   1493 	/* If recursion is going too deep, just say it's not a BOP. */
   1494 	if (depth > RECURSION_LIMIT)
   1495 		return FALSE;
   1496 
   1497 	quot_len = quote_length(line->data);
   1498 	indent_len = indent_length(line->data + quot_len);
   1499 
   1500 	/* If this line contains no text, it is not a BOP. */
   1501 	if (line->data[quot_len + indent_len] == '\0')
   1502 		return FALSE;
   1503 
   1504 	/* When requested, treat a line that starts with whitespace as a BOP. */
   1505 	if (ISSET(BOOKSTYLE) && !ISSET(AUTOINDENT) && is_blank_char(line->data))
   1506 		return TRUE;
   1507 
   1508 	/* If the quote part of the preceding line differs, this is a BOP. */
   1509 	if (quot_len != quote_length(line->prev->data) ||
   1510 					strncmp(line->data, line->prev->data, quot_len) != 0)
   1511 		return TRUE;
   1512 
   1513 	prev_dent_len = indent_length(line->prev->data + quot_len);
   1514 
   1515 	/* If the preceding line contains no text, this is a BOP. */
   1516 	if (line->prev->data[quot_len + prev_dent_len] == '\0')
   1517 		return TRUE;
   1518 
   1519 	/* If indentation of this and preceding line are equal, this is not a BOP. */
   1520 	if (wideness(line->prev->data, quot_len + prev_dent_len) ==
   1521 						wideness(line->data, quot_len + indent_len))
   1522 		return FALSE;
   1523 
   1524 	/* Otherwise, this is a BOP if the preceding line is not. */
   1525 	return !begpar(line->prev, depth + 1);
   1526 }
   1527 
   1528 /* Return TRUE when the given line is part of a paragraph: when it
   1529  * contains something more than quoting and leading whitespace. */
   1530 bool inpar(const linestruct *const line)
   1531 {
   1532 	size_t quot_len = quote_length(line->data);
   1533 	size_t indent_len = indent_length(line->data + quot_len);
   1534 
   1535 	return (line->data[quot_len + indent_len] != '\0');
   1536 }
   1537 
   1538 /* Find the first occurring paragraph in the forward direction.  Return TRUE
   1539  * when a paragraph was found, and FALSE otherwise.  Furthermore, return the
   1540  * first line and the number of lines of the paragraph. */
   1541 bool find_paragraph(linestruct **firstline, size_t *const linecount)
   1542 {
   1543 	linestruct *line = *firstline;
   1544 
   1545 	/* When not currently in a paragraph, move forward to a line that is. */
   1546 	while (!inpar(line) && line->next != NULL)
   1547 		line = line->next;
   1548 
   1549 	*firstline = line;
   1550 
   1551 	/* Move down to the last line of the paragraph (if any). */
   1552 	do_para_end(&line);
   1553 
   1554 	/* When not in a paragraph now, there aren't any paragraphs left. */
   1555 	if (!inpar(line))
   1556 		return FALSE;
   1557 
   1558 	/* We found a paragraph; determine its number of lines. */
   1559 	*linecount = line->lineno - (*firstline)->lineno + 1;
   1560 
   1561 	return TRUE;
   1562 }
   1563 
   1564 /* Concatenate into a single line all the lines of the paragraph that starts at
   1565  * *line and consists of 'count' lines, skipping the quoting and indentation on
   1566  * all lines after the first. */
   1567 void concat_paragraph(linestruct *line, size_t count)
   1568 {
   1569 	while (count > 1) {
   1570 		linestruct *next_line = line->next;
   1571 		size_t next_line_len = strlen(next_line->data);
   1572 		size_t next_quot_len = quote_length(next_line->data);
   1573 		size_t next_lead_len = next_quot_len +
   1574 							indent_length(next_line->data + next_quot_len);
   1575 		size_t line_len = strlen(line->data);
   1576 
   1577 		/* We're just about to tack the next line onto this one.  If
   1578 		 * this line isn't empty, make sure it ends in a space. */
   1579 		if (line_len > 0 && line->data[line_len - 1] != ' ') {
   1580 			line->data = nrealloc(line->data, line_len + 2);
   1581 			line->data[line_len++] = ' ';
   1582 			line->data[line_len] = '\0';
   1583 		}
   1584 
   1585 		line->data = nrealloc(line->data, line_len + next_line_len - next_lead_len + 1);
   1586 		strcat(line->data, next_line->data + next_lead_len);
   1587 #ifndef NANO_TINY
   1588 		line->has_anchor |= next_line->has_anchor;
   1589 #endif
   1590 		unlink_node(next_line);
   1591 		count--;
   1592 	}
   1593 }
   1594 
   1595 /* Copy a character from one place to another. */
   1596 void copy_character(char **from, char **to)
   1597 {
   1598 	int charlen = char_length(*from);
   1599 
   1600 	if (*from == *to) {
   1601 		*from += charlen;
   1602 		*to += charlen;
   1603 	} else
   1604 		while (--charlen >= 0)
   1605 			*((*to)++) = *((*from)++);
   1606 }
   1607 
   1608 /* In the given line, replace any series of blanks with a single space,
   1609  * but keep two spaces (if there are two) after any closing punctuation,
   1610  * and remove all blanks from the end of the line.  Leave the first skip
   1611  * number of characters untreated. */
   1612 void squeeze(linestruct *line, size_t skip)
   1613 {
   1614 	char *start = line->data + skip;
   1615 	char *from = start, *to = start;
   1616 
   1617 	/* For each character, 1) when a blank, change it to a space, and pass over
   1618 	 * all blanks after it; 2) if it is punctuation, copy it plus a possible
   1619 	 * tailing bracket, and change at most two subsequent blanks to spaces, and
   1620 	 * pass over all blanks after these; 3) leave anything else unchanged. */
   1621 	while (*from != '\0') {
   1622 		if (is_blank_char(from)) {
   1623 			from += char_length(from);
   1624 			*(to++) = ' ';
   1625 
   1626 			while (*from != '\0' && is_blank_char(from))
   1627 				from += char_length(from);
   1628 		} else if (mbstrchr(punct, from) != NULL) {
   1629 			copy_character(&from, &to);
   1630 
   1631 			if (*from != '\0' && mbstrchr(brackets, from) != NULL)
   1632 				copy_character(&from, &to);
   1633 
   1634 			if (*from != '\0' && is_blank_char(from)) {
   1635 				from += char_length(from);
   1636 				*(to++) = ' ';
   1637 			}
   1638 			if (*from != '\0' && is_blank_char(from)) {
   1639 				from += char_length(from);
   1640 				*(to++) = ' ';
   1641 			}
   1642 
   1643 			while (*from != '\0' && is_blank_char(from))
   1644 				from += char_length(from);
   1645 		} else
   1646 			copy_character(&from, &to);
   1647 	}
   1648 
   1649 	/* If there are spaces at the end of the line, remove them. */
   1650 	while (to > start && *(to - 1) == ' ')
   1651 		to--;
   1652 
   1653 	*to = '\0';
   1654 }
   1655 
   1656 /* Rewrap the given line (that starts with the given lead string which is of
   1657  * the given length), into lines that fit within the target width (wrap_at). */
   1658 void rewrap_paragraph(linestruct **line, char *lead_string, size_t lead_len)
   1659 {
   1660 	ssize_t break_pos;
   1661 		/* The x-coordinate where the current line is to be broken. */
   1662 
   1663 	while (breadth((*line)->data) > wrap_at) {
   1664 		size_t line_len = strlen((*line)->data);
   1665 
   1666 		/* Find a point in the line where it can be broken. */
   1667 		break_pos = break_line((*line)->data + lead_len,
   1668 						wrap_at - wideness((*line)->data, lead_len), FALSE);
   1669 
   1670 		/* If we can't break the line, or don't need to, we're done. */
   1671 		if (break_pos < 0 || lead_len + break_pos == line_len)
   1672 			break;
   1673 
   1674 		/* Adjust the breaking position for the leading part and
   1675 		 * move it beyond the found whitespace character. */
   1676 		break_pos += lead_len + 1;
   1677 
   1678 		/* Insert a new line after the current one, and copy the leading part
   1679 		 * plus the text after the breaking point into it. */
   1680 		splice_node(*line, make_new_node(*line));
   1681 		(*line)->next->data = nmalloc(lead_len + line_len - break_pos + 1);
   1682 		strncpy((*line)->next->data, lead_string, lead_len);
   1683 		strcpy((*line)->next->data + lead_len, (*line)->data + break_pos);
   1684 
   1685 		/* When requested, snip the one or two trailing spaces. */
   1686 		if (ISSET(TRIM_BLANKS)) {
   1687 			while (break_pos > 0 && (*line)->data[break_pos - 1] == ' ')
   1688 				break_pos--;
   1689 		}
   1690 
   1691 		/* Now actually break the current line, and go to the next. */
   1692 		(*line)->data[break_pos] = '\0';
   1693 		*line = (*line)->next;
   1694 	}
   1695 
   1696 #ifdef ENABLE_COLOR
   1697 	/* If the new paragraph exceeds the viewport, recalculate the multidata. */
   1698 	if ((*line)->lineno >= editwinrows)
   1699 		recook = TRUE;
   1700 #endif
   1701 
   1702 	/* When possible, go to the line after the rewrapped paragraph. */
   1703 	if ((*line)->next != NULL)
   1704 		*line = (*line)->next;
   1705 }
   1706 
   1707 /* Justify the lines of the given paragraph (that starts at *line, and consists
   1708  * of 'count' lines) so they all fit within the target width (wrap_at) and have
   1709  * their whitespace normalized. */
   1710 void justify_paragraph(linestruct **line, size_t count)
   1711 {
   1712 	linestruct *sampleline;
   1713 		/* The line from which the indentation is copied. */
   1714 	size_t quot_len;
   1715 		/* Length of the quote part. */
   1716 	size_t lead_len;
   1717 		/* Length of the quote part plus the indentation part. */
   1718 	char *lead_string;
   1719 		/* The quote+indent stuff that is copied from the sample line. */
   1720 
   1721 	/* The sample line is either the only line or the second line. */
   1722 	sampleline = (count == 1 ? *line : (*line)->next);
   1723 
   1724 	/* Copy the leading part (quoting + indentation) of the sample line. */
   1725 	quot_len = quote_length(sampleline->data);
   1726 	lead_len = quot_len + indent_length(sampleline->data + quot_len);
   1727 	lead_string = measured_copy(sampleline->data, lead_len);
   1728 
   1729 	/* Concatenate all lines of the paragraph into a single line. */
   1730 	concat_paragraph(*line, count);
   1731 
   1732 	/* Change all blank characters to spaces and remove excess spaces. */
   1733 	squeeze(*line, quot_len + indent_length((*line)->data + quot_len));
   1734 
   1735 	/* Rewrap the line into multiple lines, accounting for the leading part. */
   1736 	rewrap_paragraph(line, lead_string, lead_len);
   1737 
   1738 	free(lead_string);
   1739 }
   1740 
   1741 #define ONE_PARAGRAPH  FALSE
   1742 #define WHOLE_BUFFER  TRUE
   1743 
   1744 /* Justify the current paragraph, or the entire buffer when whole_buffer is
   1745  * TRUE.  But if the mark is on, justify only the marked text instead. */
   1746 void justify_text(bool whole_buffer)
   1747 {
   1748 	size_t linecount;
   1749 		/* The number of lines in the original paragraph. */
   1750 	linestruct *startline;
   1751 		/* The line where the paragraph or region starts. */
   1752 	linestruct *endline;
   1753 		/* The line where the paragraph or region ends. */
   1754 	size_t start_x;
   1755 		/* The x position where the paragraph or region starts. */
   1756 	size_t end_x;
   1757 		/* The x position where the paragraph or region ends. */
   1758 	linestruct *was_cutbuffer = cutbuffer;
   1759 		/* The old cutbuffer, so we can justify in the current cutbuffer. */
   1760 	linestruct *jusline;
   1761 		/* The line that we're justifying in the current cutbuffer. */
   1762 #ifndef NANO_TINY
   1763 	bool before_eol = FALSE;
   1764 		/* Whether the end of a marked region is before the end of its line. */
   1765 	char *primary_lead = NULL;
   1766 		/* The leading part (quoting + indentation) of the first line
   1767 		 * of the paragraph where the marked region begins. */
   1768 	size_t primary_len = 0;
   1769 		/* The length (in bytes) of the above first-line leading part. */
   1770 	char *secondary_lead = NULL;
   1771 		/* The leading part for lines after the first one. */
   1772 	size_t secondary_len = 0;
   1773 		/* The length of that later lead. */
   1774 	ssize_t was_the_linenumber = openfile->current->lineno;
   1775 		/* The line to return to after a full justification. */
   1776 	bool marked_backward = (openfile->mark && !mark_is_before_cursor());
   1777 
   1778 	/* TRANSLATORS: This one goes with Undid/Redid messages. */
   1779 	add_undo(COUPLE_BEGIN, N_("justification"));
   1780 
   1781 	/* If the mark is on, do as Pico: treat all marked text as one paragraph. */
   1782 	if (openfile->mark) {
   1783 		size_t quot_len, fore_len, other_quot_len, other_white_len;
   1784 		linestruct *sampleline;
   1785 
   1786 		get_region(&startline, &start_x, &endline, &end_x);
   1787 
   1788 		/* When the marked region is empty, do nothing. */
   1789 		if (startline == endline && start_x == end_x) {
   1790 			statusline(AHEM, _("Selection is empty"));
   1791 			discard_until(openfile->undotop->next);
   1792 			return;
   1793 		}
   1794 
   1795 		quot_len = quote_length(startline->data);
   1796 		fore_len = quot_len + indent_length(startline->data + quot_len);
   1797 
   1798 		/* When the region starts IN the lead, take the whole lead. */
   1799 		if (start_x <= fore_len)
   1800 			start_x = 0;
   1801 
   1802 		/* Recede over blanks before the region.  This effectively snips
   1803 		 * trailing blanks from what will become the preceding paragraph. */
   1804 		while (start_x > 0 && is_blank_char(&startline->data[start_x - 1]))
   1805 			start_x = step_left(startline->data, start_x);
   1806 
   1807 		quot_len = quote_length(endline->data);
   1808 		fore_len = quot_len + indent_length(endline->data + quot_len);
   1809 
   1810 		/* When the region ends IN the lead, take the whole lead. */
   1811 		if (0 < end_x && end_x < fore_len)
   1812 			end_x = fore_len;
   1813 
   1814 		/* When not at the left edge, advance over blanks after the region. */
   1815 		while (end_x > 0 && is_blank_char(&endline->data[end_x]))
   1816 			end_x = step_right(endline->data, end_x);
   1817 
   1818 		sampleline = startline;
   1819 
   1820 		/* Find the first line of the paragraph in which the region starts. */
   1821 		while (sampleline->prev && inpar(sampleline) && !begpar(sampleline, 0))
   1822 			sampleline = sampleline->prev;
   1823 
   1824 		/* Ignore lines that contain no text. */
   1825 		while (sampleline->next && !inpar(sampleline))
   1826 			sampleline = sampleline->next;
   1827 
   1828 		/* Store the leading part that is to be used for the new paragraph. */
   1829 		quot_len = quote_length(sampleline->data);
   1830 		primary_len = quot_len + indent_length(sampleline->data + quot_len);
   1831 		primary_lead = measured_copy(sampleline->data, primary_len);
   1832 
   1833 		if (sampleline->next && startline != endline)
   1834 			sampleline = sampleline->next;
   1835 
   1836 		/* Copy the leading part that is to be used for the new paragraph after
   1837 		 * its first line (if any): the quoting of the first line, plus the
   1838 		 * indentation of the second line. */
   1839 		other_quot_len = quote_length(sampleline->data);
   1840 		other_white_len = indent_length(sampleline->data + other_quot_len);
   1841 
   1842 		secondary_len = quot_len + other_white_len;
   1843 		secondary_lead = nmalloc(secondary_len + 1);
   1844 
   1845 		strncpy(secondary_lead, startline->data, quot_len);
   1846 		strncpy(secondary_lead + quot_len, sampleline->data + other_quot_len,
   1847 													other_white_len);
   1848 		secondary_lead[secondary_len] = '\0';
   1849 
   1850 		/* Include preceding and succeeding leads into the marked region. */
   1851 		openfile->mark = startline;
   1852 		openfile->mark_x = start_x;
   1853 		openfile->current = endline;
   1854 		openfile->current_x = end_x;
   1855 
   1856 		linecount = endline->lineno - startline->lineno + (end_x > 0 ? 1 : 0);
   1857 
   1858 		/* Remember whether the end of the region was before the end-of-line. */
   1859 		before_eol = endline->data[end_x] != '\0';
   1860 	} else
   1861 #endif /* NANO_TINY */
   1862 	{
   1863 		/* When justifying the entire buffer, start at the top.  Otherwise, when
   1864 		 * in a paragraph but not at its beginning, move back to its first line. */
   1865 		if (whole_buffer)
   1866 			openfile->current = openfile->filetop;
   1867 		else if (inpar(openfile->current) && !begpar(openfile->current, 0))
   1868 			do_para_begin(&openfile->current);
   1869 
   1870 		/* Find the first line of the paragraph(s) to be justified.  If the
   1871 		 * search fails, there is nothing to justify, and we will be on the
   1872 		 * last line of the file, so put the cursor at the end of it. */
   1873 		if (!find_paragraph(&openfile->current, &linecount)) {
   1874 			openfile->current_x = strlen(openfile->filebot->data);
   1875 #ifndef NANO_TINY
   1876 			discard_until(openfile->undotop->next);
   1877 #endif
   1878 			refresh_needed = TRUE;
   1879 			return;
   1880 		} else
   1881 			openfile->current_x = 0;
   1882 
   1883 		/* Set the starting point of the paragraph. */
   1884 		startline = openfile->current;
   1885 		start_x = 0;
   1886 
   1887 		/* Set the end point of the paragraph. */
   1888 		if (whole_buffer)
   1889 			endline = openfile->filebot;
   1890 		else {
   1891 			endline = startline;
   1892 			for (size_t count = linecount; count > 1; count--)
   1893 				endline = endline->next;
   1894 		}
   1895 
   1896 		/* When possible, step one line further; otherwise, to line's end. */
   1897 		if (endline->next != NULL) {
   1898 			endline = endline->next;
   1899 			end_x = 0;
   1900 		} else
   1901 			end_x = strlen(endline->data);
   1902 	}
   1903 
   1904 #ifndef NANO_TINY
   1905 	add_undo(CUT, NULL);
   1906 #endif
   1907 	/* Do the equivalent of a marked cut into an empty cutbuffer. */
   1908 	cutbuffer = NULL;
   1909 	extract_segment(startline, start_x, endline, end_x);
   1910 #ifndef NANO_TINY
   1911 	update_undo(CUT);
   1912 
   1913 	if (openfile->mark) {
   1914 		linestruct *line = cutbuffer;
   1915 		size_t quot_len = quote_length(line->data);
   1916 		size_t fore_len = quot_len + indent_length(line->data + quot_len);
   1917 		size_t text_len = strlen(line->data) - fore_len;
   1918 
   1919 		/* If the extracted region begins with any leading part, trim it. */
   1920 		if (fore_len > 0)
   1921 			memmove(line->data, line->data + fore_len, text_len + 1);
   1922 
   1923 		/* Then copy back in the leading part that it should have. */
   1924 		if (primary_len > 0) {
   1925 			line->data = nrealloc(line->data, primary_len + text_len + 1);
   1926 			memmove(line->data + primary_len, line->data, text_len + 1);
   1927 			strncpy(line->data, primary_lead, primary_len);
   1928 		}
   1929 
   1930 		/* Now justify the extracted region. */
   1931 		concat_paragraph(cutbuffer, linecount);
   1932 		squeeze(cutbuffer, primary_len);
   1933 		rewrap_paragraph(&line, secondary_lead, secondary_len);
   1934 
   1935 		/* If the marked region started in the middle of a line,
   1936 		 * insert a newline before the new paragraph. */
   1937 		if (start_x > 0) {
   1938 			cutbuffer->prev = make_new_node(NULL);
   1939 			cutbuffer->prev->data = copy_of("");
   1940 			cutbuffer->prev->next = cutbuffer;
   1941 			cutbuffer = cutbuffer->prev;
   1942 		}
   1943 
   1944 		/* If the marked region ended in the middle of a line,
   1945 		 * insert a newline after the new paragraph. */
   1946 		if (end_x > 0 && before_eol) {
   1947 			line->next = make_new_node(line);
   1948 			line->next->data = copy_of(primary_lead);
   1949 		}
   1950 
   1951 		free(secondary_lead);
   1952 		free(primary_lead);
   1953 
   1954 		/* Keep as much of the marked region onscreen as possible. */
   1955 		focusing = FALSE;
   1956 	} else
   1957 #endif
   1958 	{
   1959 		/* Prepare to justify the text we just put in the cutbuffer. */
   1960 		jusline = cutbuffer;
   1961 
   1962 		/* Justify the current paragraph. */
   1963 		justify_paragraph(&jusline, linecount);
   1964 
   1965 		/* When justifying the entire buffer, find and justify all paragraphs. */
   1966 		if (whole_buffer) {
   1967 			while (find_paragraph(&jusline, &linecount)) {
   1968 				justify_paragraph(&jusline, linecount);
   1969 
   1970 				if (jusline->next == NULL)
   1971 					break;
   1972 			}
   1973 		}
   1974 	}
   1975 
   1976 #ifndef NANO_TINY
   1977 	/* Wipe an anchor on the first paragraph if it was only inherited. */
   1978 	if (whole_buffer && !openfile->mark && !cutbuffer->has_anchor)
   1979 		openfile->current->has_anchor = FALSE;
   1980 
   1981 	add_undo(PASTE, NULL);
   1982 #endif
   1983 	/* Do the equivalent of a paste of the justified text. */
   1984 	ingraft_buffer(cutbuffer);
   1985 #ifndef NANO_TINY
   1986 	update_undo(PASTE);
   1987 
   1988 	/* After justifying a backward-marked text, swap mark and cursor. */
   1989 	if (marked_backward) {
   1990 		linestruct *bottom = openfile->current;
   1991 		size_t bottom_x = openfile->current_x;
   1992 
   1993 		openfile->current = openfile->mark;
   1994 		openfile->current_x = openfile->mark_x;
   1995 		openfile->mark = bottom;
   1996 		openfile->mark_x = bottom_x;
   1997 	} else if (whole_buffer && !openfile->mark)
   1998 		goto_line_posx(was_the_linenumber, 0);
   1999 
   2000 	add_undo(COUPLE_END, N_("justification"));
   2001 
   2002 	/* Report on the status bar what we justified. */
   2003 	if (openfile->mark)
   2004 		statusline(REMARK, _("Justified selection"));
   2005 	else
   2006 #endif
   2007 	if (whole_buffer)
   2008 		statusline(REMARK, _("Justified file"));
   2009 	else
   2010 		statusbar(_("Justified paragraph"));
   2011 
   2012 	/* We're done justifying.  Restore the cutbuffer. */
   2013 	cutbuffer = was_cutbuffer;
   2014 
   2015 	/* Set the desired screen column (always zero, except at EOF). */
   2016 	openfile->placewewant = xplustabs();
   2017 
   2018 	set_modified();
   2019 	refresh_needed = TRUE;
   2020 	shift_held = TRUE;
   2021 }
   2022 
   2023 /* Justify the current paragraph. */
   2024 void do_justify(void)
   2025 {
   2026 	justify_text(ONE_PARAGRAPH);
   2027 }
   2028 
   2029 /* Justify the entire file. */
   2030 void do_full_justify(void)
   2031 {
   2032 	justify_text(WHOLE_BUFFER);
   2033 	ran_a_tool = TRUE;
   2034 #ifdef ENABLE_COLOR
   2035 	recook = TRUE;
   2036 #endif
   2037 }
   2038 #endif /* ENABLE_JUSTIFY */
   2039 
   2040 #if defined(ENABLE_SPELLER) || defined (ENABLE_LINTER) || defined (ENABLE_FORMATTER)
   2041 /* Set up an argument list for executing the given command. */
   2042 void construct_argument_list(char ***arguments, char *command, char *filename)
   2043 {
   2044 	char *copy_of_command = copy_of(command);
   2045 	char *element = strtok(copy_of_command, " ");
   2046 	int count = 2;
   2047 
   2048 	while (element != NULL) {
   2049 		*arguments = nrealloc(*arguments, ++count * sizeof(char *));
   2050 		(*arguments)[count - 3] = element;
   2051 		element = strtok(NULL, " ");
   2052 	}
   2053 
   2054 	(*arguments)[count - 2] = filename;
   2055 	(*arguments)[count - 1] = NULL;
   2056 }
   2057 #endif
   2058 
   2059 #if defined(ENABLE_SPELLER) || defined (ENABLE_FORMATTER)
   2060 /* Open the specified file, and if that succeeds, remove the text of the marked
   2061  * region or of the entire buffer and read the file contents into its place. */
   2062 bool replace_buffer(const char *filename, undo_type action, const char *operation)
   2063 {
   2064 	linestruct *was_cutbuffer = cutbuffer;
   2065 	int descriptor;
   2066 	FILE *stream;
   2067 
   2068 	descriptor = open_file(filename, FALSE, &stream);
   2069 
   2070 	if (descriptor < 0)
   2071 		return FALSE;
   2072 
   2073 #ifndef NANO_TINY
   2074 	add_undo(COUPLE_BEGIN, operation);
   2075 #endif
   2076 
   2077 	/* When replacing the whole buffer, start cutting at the top. */
   2078 	if (action == CUT_TO_EOF) {
   2079 		openfile->current = openfile->filetop;
   2080 		openfile->current_x = 0;
   2081 	}
   2082 
   2083 	cutbuffer = NULL;
   2084 
   2085 #ifndef NANO_TINY
   2086 	/* Cut either the marked region or the whole buffer. */
   2087 	add_undo(action, NULL);
   2088 	do_snip(openfile->mark != NULL, openfile->mark == NULL, FALSE);
   2089 	update_undo(action);
   2090 #else
   2091 	do_snip(FALSE, TRUE, FALSE);
   2092 #endif
   2093 
   2094 	/* Discard what was cut. */
   2095 	free_lines(cutbuffer);
   2096 	cutbuffer = was_cutbuffer;
   2097 
   2098 	/* Insert the spell-checked file into the cleared area. */
   2099 	read_file(stream, descriptor, filename, TRUE);
   2100 
   2101 #ifndef NANO_TINY
   2102 	add_undo(COUPLE_END, operation);
   2103 #endif
   2104 	return TRUE;
   2105 }
   2106 
   2107 /* Execute the given program, with the given temp file as last argument. */
   2108 void treat(char *tempfile_name, char *theprogram, bool spelling)
   2109 {
   2110 #if defined(HAVE_FORK) && defined(HAVE_WAIT)
   2111 	ssize_t was_lineno = openfile->current->lineno;
   2112 	size_t was_pww = openfile->placewewant;
   2113 	size_t was_x = openfile->current_x;
   2114 	bool was_at_eol = (openfile->current->data[openfile->current_x] == '\0');
   2115 	struct stat fileinfo;
   2116 	long timestamp_sec = 0;
   2117 	long timestamp_nsec = 0;
   2118 	static char **arguments = NULL;
   2119 	pid_t thepid;
   2120 	int program_status, errornumber;
   2121 	bool replaced = FALSE;
   2122 
   2123 	/* Stat the temporary file.  If that succeeds and its size is zero,
   2124 	 * there is nothing to do; otherwise, store its time of modification. */
   2125 	if (stat(tempfile_name, &fileinfo) == 0) {
   2126 		if (fileinfo.st_size == 0) {
   2127 #ifndef NANO_TINY
   2128 			if (spelling && openfile->mark)
   2129 				statusline(AHEM, _("Selection is empty"));
   2130 			else
   2131 #endif
   2132 				statusline(AHEM, _("Buffer is empty"));
   2133 			return;
   2134 		}
   2135 
   2136 		timestamp_sec = (long)fileinfo.st_mtim.tv_sec;
   2137 		timestamp_nsec = (long)fileinfo.st_mtim.tv_nsec;
   2138 	}
   2139 
   2140 	/* The spell checker needs the screen, so exit from curses mode. */
   2141 	if (spelling)
   2142 		endwin();
   2143 	else
   2144 		statusbar(_("Invoking formatter..."));
   2145 
   2146 	construct_argument_list(&arguments, theprogram, tempfile_name);
   2147 
   2148 	/* Fork a child process and run the given program in it. */
   2149 	if ((thepid = fork()) == 0) {
   2150 		execvp(arguments[0], arguments);
   2151 
   2152 		/* Terminate the child if the program is not found. */
   2153 		exit(9);
   2154 	} else if (thepid > 0) {
   2155 		/* Block SIGWINCHes while waiting for the forked program to end,
   2156 		 * so nano doesn't get pushed past the wait(). */
   2157 		block_sigwinch(TRUE);
   2158 		wait(&program_status);
   2159 		block_sigwinch(FALSE);
   2160 	}
   2161 
   2162 	errornumber = errno;
   2163 
   2164 	/* After spell checking, restore terminal state and reenter curses mode;
   2165 	 * after formatting, make sure that any formatter output is wiped. */
   2166 	if (spelling) {
   2167 		terminal_init();
   2168 		doupdate();
   2169 	} else
   2170 		full_refresh();
   2171 
   2172 	if (thepid < 0) {
   2173 		statusline(ALERT, _("Could not fork: %s"), strerror(errornumber));
   2174 		free(arguments[0]);
   2175 		return;
   2176 	} else if (!WIFEXITED(program_status) || WEXITSTATUS(program_status) > 2) {
   2177 		statusline(ALERT, _("Error invoking '%s'"), arguments[0]);
   2178 		free(arguments[0]);
   2179 		return;
   2180 	} else if (WEXITSTATUS(program_status) != 0)
   2181 		statusline(ALERT, _("Program '%s' complained"), arguments[0]);
   2182 
   2183 	free(arguments[0]);
   2184 
   2185 	/* When the temporary file wasn't touched, say so and leave. */
   2186 	if (timestamp_sec > 0 && stat(tempfile_name, &fileinfo) == 0 &&
   2187 					(long)fileinfo.st_mtim.tv_sec == timestamp_sec &&
   2188 					(long)fileinfo.st_mtim.tv_nsec == timestamp_nsec) {
   2189 		statusline(REMARK, _("Nothing changed"));
   2190 		return;
   2191 	}
   2192 
   2193 #ifndef NANO_TINY
   2194 	/* Replace the marked text (or entire text) with the corrected text. */
   2195 	if (spelling && openfile->mark) {
   2196 		ssize_t was_mark_lineno = openfile->mark->lineno;
   2197 		bool upright = mark_is_before_cursor();
   2198 
   2199 		replaced = replace_buffer(tempfile_name, CUT, "spelling correction");
   2200 
   2201 		/* Adjust the end point of the marked region for any change in
   2202 		 * length of the region's last line. */
   2203 		if (upright)
   2204 			was_x = openfile->current_x;
   2205 		else
   2206 			openfile->mark_x = openfile->current_x;
   2207 
   2208 		/* Restore the mark. */
   2209 		openfile->mark = line_from_number(was_mark_lineno);
   2210 	} else
   2211 #endif
   2212 		replaced = replace_buffer(tempfile_name, CUT_TO_EOF,
   2213 					/* TRANSLATORS: The next two go with Undid/Redid messages. */
   2214 					(spelling ? N_("spelling correction") : N_("formatting")));
   2215 
   2216 	/* Go back to the old position. */
   2217 	goto_line_posx(was_lineno, was_x);
   2218 	if (was_at_eol || openfile->current_x > strlen(openfile->current->data))
   2219 		openfile->current_x = strlen(openfile->current->data);
   2220 
   2221 	if (replaced) {
   2222 #ifndef NANO_TINY
   2223 		openfile->filetop->has_anchor = FALSE;
   2224 		update_undo(COUPLE_END);
   2225 #endif
   2226 	}
   2227 
   2228 	openfile->placewewant = was_pww;
   2229 	adjust_viewport(STATIONARY);
   2230 
   2231 	if (spelling)
   2232 		statusline(REMARK, _("Finished checking spelling"));
   2233 	else
   2234 		statusline(REMARK, _("Buffer has been processed"));
   2235 #endif
   2236 }
   2237 #endif /* ENABLE_SPELLER || ENABLE_FORMATTER */
   2238 
   2239 #ifdef ENABLE_SPELLER
   2240 /* Let the user edit the misspelled word.  Return FALSE if the user cancels. */
   2241 bool fix_spello(const char *word)
   2242 {
   2243 	linestruct *was_edittop = openfile->edittop;
   2244 	linestruct *was_current = openfile->current;
   2245 	size_t was_firstcolumn = openfile->firstcolumn;
   2246 	size_t was_x = openfile->current_x;
   2247 	bool proceed = FALSE;
   2248 	int result;
   2249 #ifndef NANO_TINY
   2250 	bool right_side_up = (openfile->mark && mark_is_before_cursor());
   2251 	linestruct *top, *bot;
   2252 	size_t top_x, bot_x;
   2253 
   2254 	/* If the mark is on, start at the beginning of the marked region. */
   2255 	if (openfile->mark) {
   2256 		get_region(&top, &top_x, &bot, &bot_x);
   2257 		/* If the region is marked normally, swap the end points, so that
   2258 		 * (current, current_x) (where searching starts) is at the top. */
   2259 		if (right_side_up) {
   2260 			openfile->current = top;
   2261 			openfile->current_x = top_x;
   2262 			openfile->mark = bot;
   2263 			openfile->mark_x = bot_x;
   2264 		}
   2265 	} else
   2266 #endif
   2267 	/* Otherwise, start from the top of the file. */
   2268 	{
   2269 		openfile->current = openfile->filetop;
   2270 		openfile->current_x = 0;
   2271 	}
   2272 
   2273 	/* Find the first whole occurrence of word. */
   2274 	result = findnextstr(word, TRUE, INREGION, NULL, FALSE, NULL, 0);
   2275 
   2276 	/* If the word isn't found, alert the user; if it is, allow correction. */
   2277 	if (result == 0) {
   2278 		statusline(ALERT, _("Unfindable word: %s"), word);
   2279 		lastmessage = VACUUM;
   2280 		proceed = TRUE;
   2281 		napms(2800);
   2282 	} else if (result == 1) {
   2283 		spotlighted = TRUE;
   2284 		light_from_col = xplustabs();
   2285 		light_to_col = light_from_col + breadth(word);
   2286 #ifndef NANO_TINY
   2287 		linestruct *saved_mark = openfile->mark;
   2288 		openfile->mark = NULL;
   2289 #endif
   2290 		edit_refresh();
   2291 
   2292 		put_cursor_at_end_of_answer();
   2293 
   2294 		/* Let the user supply a correctly spelled alternative. */
   2295 		proceed = (do_prompt(MSPELL, word, NULL, edit_refresh,
   2296 								/* TRANSLATORS: This is a prompt. */
   2297 								_("Edit a replacement")) != -1);
   2298 
   2299 		spotlighted = FALSE;
   2300 
   2301 #ifndef NANO_TINY
   2302 		openfile->mark = saved_mark;
   2303 #endif
   2304 
   2305 		/* If a replacement was given, go through all occurrences. */
   2306 		if (proceed && strcmp(word, answer) != 0) {
   2307 			do_replace_loop(word, TRUE, was_current, &was_x);
   2308 
   2309 			/* TRANSLATORS: Shown after fixing misspellings in one word. */
   2310 			statusbar(_("Next word..."));
   2311 			napms(400);
   2312 		}
   2313 	}
   2314 
   2315 #ifndef NANO_TINY
   2316 	if (openfile->mark) {
   2317 		/* Restore the (compensated) end points of the marked region. */
   2318 		if (right_side_up) {
   2319 			openfile->current = openfile->mark;
   2320 			openfile->current_x = openfile->mark_x;
   2321 			openfile->mark = top;
   2322 			openfile->mark_x = top_x;
   2323 		} else {
   2324 			openfile->current = top;
   2325 			openfile->current_x = top_x;
   2326 		}
   2327 	} else
   2328 #endif
   2329 	{
   2330 		/* Restore the (compensated) cursor position. */
   2331 		openfile->current = was_current;
   2332 		openfile->current_x = was_x;
   2333 	}
   2334 
   2335 	/* Restore the viewport to where it was. */
   2336 	openfile->edittop = was_edittop;
   2337 	openfile->firstcolumn = was_firstcolumn;
   2338 
   2339 	return proceed;
   2340 }
   2341 
   2342 /* Run a spell-check on the given file, using 'spell' to produce a list of all
   2343  * misspelled words, then feeding those through 'sort' and 'uniq' to obtain an
   2344  * alphabetical list, which words are then offered one by one to the user for
   2345  * correction. */
   2346 void do_int_speller(const char *tempfile_name)
   2347 {
   2348 #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
   2349 	char *misspellings, *pointer, *oneword;
   2350 	long pipesize;
   2351 	size_t buffersize, bytesread, totalread;
   2352 	int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
   2353 	pid_t pid_spell, pid_sort, pid_uniq;
   2354 	int spell_status, sort_status, uniq_status;
   2355 	unsigned stash[sizeof(flags) / sizeof(flags[0])];
   2356 
   2357 	/* Create all three pipes up front. */
   2358 	if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1) {
   2359 		statusline(ALERT, _("Could not create pipe: %s"), strerror(errno));
   2360 		return;
   2361 	}
   2362 
   2363 	statusbar(_("Invoking spell checker..."));
   2364 
   2365 	/* Fork a process to run spell in. */
   2366 	if ((pid_spell = fork()) == 0) {
   2367 		/* Child: open the temporary file that holds the text to be checked. */
   2368 		if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
   2369 			exit(6);
   2370 
   2371 		/* Connect standard input to the temporary file. */
   2372 		if (dup2(tempfile_fd, STDIN_FILENO) < 0)
   2373 			exit(7);
   2374 
   2375 		/* Connect standard output to the write end of the first pipe. */
   2376 		if (dup2(spell_fd[1], STDOUT_FILENO) < 0)
   2377 			exit(8);
   2378 
   2379 		close(tempfile_fd);
   2380 		close(spell_fd[0]);
   2381 		close(spell_fd[1]);
   2382 
   2383 		/* Try to run 'hunspell'; if that fails, fall back to 'spell'. */
   2384 		execlp("hunspell", "hunspell", "-l", NULL);
   2385 		execlp("spell", "spell", NULL);
   2386 
   2387 		/* Indicate failure when neither speller was found. */
   2388 		exit(9);
   2389 	}
   2390 
   2391 	/* Parent: close the unused write end of the first pipe. */
   2392 	close(spell_fd[1]);
   2393 
   2394 	/* Fork a process to run sort in. */
   2395 	if ((pid_sort = fork()) == 0) {
   2396 		/* Connect standard input to the read end of the first pipe. */
   2397 		if (dup2(spell_fd[0], STDIN_FILENO) < 0)
   2398 			exit(7);
   2399 
   2400 		/* Connect standard output to the write end of the second pipe. */
   2401 		if (dup2(sort_fd[1], STDOUT_FILENO) < 0)
   2402 			exit(8);
   2403 
   2404 		close(spell_fd[0]);
   2405 		close(sort_fd[0]);
   2406 		close(sort_fd[1]);
   2407 
   2408 		/* Now run the sort program.  Use -f to mix upper and lower case. */
   2409 		execlp("sort", "sort", "-f", NULL);
   2410 
   2411 		exit(9);
   2412 	}
   2413 
   2414 	close(spell_fd[0]);
   2415 	close(sort_fd[1]);
   2416 
   2417 	/* Fork a process to run uniq in. */
   2418 	if ((pid_uniq = fork()) == 0) {
   2419 		if (dup2(sort_fd[0], STDIN_FILENO) < 0)
   2420 			exit(7);
   2421 
   2422 		if (dup2(uniq_fd[1], STDOUT_FILENO) < 0)
   2423 			exit(8);
   2424 
   2425 		close(sort_fd[0]);
   2426 		close(uniq_fd[0]);
   2427 		close(uniq_fd[1]);
   2428 
   2429 		execlp("uniq", "uniq", NULL);
   2430 
   2431 		exit(9);
   2432 	}
   2433 
   2434 	close(sort_fd[0]);
   2435 	close(uniq_fd[1]);
   2436 
   2437 	/* When some child process was not forked successfully... */
   2438 	if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
   2439 		statusline(ALERT, _("Could not fork: %s"), strerror(errno));
   2440 		close(uniq_fd[0]);
   2441 		return;
   2442 	}
   2443 
   2444 	/* Get the system pipe buffer size. */
   2445 	pipesize = fpathconf(uniq_fd[0], _PC_PIPE_BUF);
   2446 
   2447 	if (pipesize < 1) {
   2448 		statusline(ALERT, _("Could not get size of pipe buffer"));
   2449 		close(uniq_fd[0]);
   2450 		return;
   2451 	}
   2452 
   2453 	/* Leave curses mode so that error messages go to the original screen. */
   2454 	endwin();
   2455 
   2456 	/* Block SIGWINCHes while reading misspelled words from the third pipe. */
   2457 	block_sigwinch(TRUE);
   2458 
   2459 	totalread = 0;
   2460 	buffersize = pipesize + 1;
   2461 	misspellings = nmalloc(buffersize);
   2462 	pointer = misspellings;
   2463 
   2464 	while ((bytesread = read(uniq_fd[0], pointer, pipesize)) > 0) {
   2465 		totalread += bytesread;
   2466 		buffersize += pipesize;
   2467 		misspellings = nrealloc(misspellings, buffersize);
   2468 		pointer = misspellings + totalread;
   2469 	}
   2470 
   2471 	*pointer = '\0';
   2472 	close(uniq_fd[0]);
   2473 
   2474 	block_sigwinch(FALSE);
   2475 
   2476 	/* Re-enter curses mode. */
   2477 	terminal_init();
   2478 	doupdate();
   2479 
   2480 	/* Save the settings of the global flags. */
   2481 	memcpy(stash, flags, sizeof(flags));
   2482 
   2483 	/* Do any replacements case-sensitively, forward, and without regexes. */
   2484 	SET(CASE_SENSITIVE);
   2485 	UNSET(BACKWARDS_SEARCH);
   2486 	UNSET(USE_REGEXP);
   2487 
   2488 	pointer = misspellings;
   2489 	oneword = misspellings;
   2490 
   2491 	/* Process each of the misspelled words. */
   2492 	while (*pointer != '\0') {
   2493 		if ((*pointer == '\r') || (*pointer == '\n')) {
   2494 			*pointer = '\0';
   2495 			if (oneword != pointer) {
   2496 				if (!fix_spello(oneword)) {
   2497 					oneword = pointer;
   2498 					break;
   2499 				}
   2500 			}
   2501 			oneword = pointer + 1;
   2502 		}
   2503 		pointer++;
   2504 	}
   2505 
   2506 	/* Special case: the last word doesn't end with '\r' or '\n'. */
   2507 	if (oneword != pointer)
   2508 		fix_spello(oneword);
   2509 
   2510 	free(misspellings);
   2511 	refresh_needed = TRUE;
   2512 
   2513 	/* Restore the settings of the global flags. */
   2514 	memcpy(flags, stash, sizeof(flags));
   2515 
   2516 	/* Process the end of the three processes. */
   2517 	waitpid(pid_spell, &spell_status, 0);
   2518 	waitpid(pid_sort, &sort_status, 0);
   2519 	waitpid(pid_uniq, &uniq_status, 0);
   2520 
   2521 	if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
   2522 		statusline(ALERT, _("Error invoking \"uniq\""));
   2523 	else if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status))
   2524 		statusline(ALERT, _("Error invoking \"sort\""));
   2525 	else if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
   2526 		statusline(ALERT, _("Error invoking \"spell\""));
   2527 	else
   2528 		statusline(REMARK, _("Finished checking spelling"));
   2529 #endif
   2530 }
   2531 
   2532 /* Spell check the current file.  If an alternate spell checker is
   2533  * specified, use it.  Otherwise, use the internal spell checker. */
   2534 void do_spell(void)
   2535 {
   2536 	FILE *stream;
   2537 	char *temp_name;
   2538 	bool okay;
   2539 
   2540 	ran_a_tool = TRUE;
   2541 
   2542 	if (in_restricted_mode())
   2543 		return;
   2544 
   2545 	temp_name = safe_tempfile(&stream);
   2546 
   2547 	if (temp_name == NULL) {
   2548 		statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
   2549 		return;
   2550 	}
   2551 
   2552 #ifndef NANO_TINY
   2553 	if (openfile->mark)
   2554 		okay = write_region_to_file(temp_name, stream, TEMPORARY, OVERWRITE);
   2555 	else
   2556 #endif
   2557 		okay = write_file(temp_name, stream, TEMPORARY, OVERWRITE, NONOTES);
   2558 
   2559 	if (!okay) {
   2560 		statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
   2561 		unlink(temp_name);
   2562 		free(temp_name);
   2563 		return;
   2564 	}
   2565 
   2566 	blank_bottombars();
   2567 
   2568 	if (alt_speller && *alt_speller)
   2569 		treat(temp_name, alt_speller, TRUE);
   2570 	else
   2571 		do_int_speller(temp_name);
   2572 
   2573 	unlink(temp_name);
   2574 	free(temp_name);
   2575 
   2576 	/* Ensure the help lines will be redrawn and a selection is retained. */
   2577 	currmenu = MMOST;
   2578 	shift_held = TRUE;
   2579 }
   2580 #endif /* ENABLE_SPELLER */
   2581 
   2582 #ifdef ENABLE_LINTER
   2583 /* Run a linting program on the current buffer. */
   2584 void do_linter(void)
   2585 {
   2586 #if defined(HAVE_FORK) && defined(HAVE_WAITPID)
   2587 	char *lintings, *pointer, *onelint;
   2588 	long pipesize;
   2589 	size_t buffersize, bytesread, totalread;
   2590 	bool parsesuccess = FALSE;
   2591 	int lint_status, lint_fd[2];
   2592 	pid_t pid_lint;
   2593 	bool helpless = ISSET(NO_HELP);
   2594 	lintstruct *lints = NULL, *tmplint = NULL, *curlint = NULL;
   2595 	time_t last_wait = 0;
   2596 
   2597 	ran_a_tool = TRUE;
   2598 
   2599 	if (in_restricted_mode())
   2600 		return;
   2601 
   2602 	if (!openfile->syntax || !openfile->syntax->linter || !*openfile->syntax->linter) {
   2603 		statusline(AHEM, _("No linter is defined for this type of file"));
   2604 		return;
   2605 	}
   2606 
   2607 #ifndef NANO_TINY
   2608 	openfile->mark = NULL;
   2609 #endif
   2610 	edit_refresh();
   2611 
   2612 	if (openfile->modified) {
   2613 		int choice = ask_user(YESORNO, _("Save modified buffer before linting?"));
   2614 
   2615 		if (choice == CANCEL) {
   2616 			statusbar(_("Cancelled"));
   2617 			return;
   2618 		} else if (choice == YES && (write_it_out(FALSE, FALSE) != 1))
   2619 			return;
   2620 	}
   2621 
   2622 	/* Create a pipe up front. */
   2623 	if (pipe(lint_fd) == -1) {
   2624 		statusline(ALERT, _("Could not create pipe: %s"), strerror(errno));
   2625 		return;
   2626 	}
   2627 
   2628 	blank_bottombars();
   2629 	currmenu = MLINTER;
   2630 	statusbar(_("Invoking linter..."));
   2631 
   2632 	/* Fork a process to run the linter in. */
   2633 	if ((pid_lint = fork()) == 0) {
   2634 		char **lintargs = NULL;
   2635 
   2636 		/* Redirect standard output and standard error into the pipe. */
   2637 		if (dup2(lint_fd[1], STDOUT_FILENO) < 0)
   2638 			exit(7);
   2639 		if (dup2(lint_fd[1], STDERR_FILENO) < 0)
   2640 			exit(8);
   2641 
   2642 		close(lint_fd[0]);
   2643 		close(lint_fd[1]);
   2644 
   2645 		construct_argument_list(&lintargs, openfile->syntax->linter, openfile->filename);
   2646 
   2647 		/* Start the linter program; we are using $PATH. */
   2648 		execvp(lintargs[0], lintargs);
   2649 
   2650 		/* This is only reached when the linter is not found. */
   2651 		exit(9);
   2652 	}
   2653 
   2654 	/* Parent continues here. */
   2655 	close(lint_fd[1]);
   2656 
   2657 	/* If the child process was not forked successfully... */
   2658 	if (pid_lint < 0) {
   2659 		statusline(ALERT, _("Could not fork: %s"), strerror(errno));
   2660 		close(lint_fd[0]);
   2661 		return;
   2662 	}
   2663 
   2664 	/* Get the system pipe buffer size. */
   2665 	pipesize = fpathconf(lint_fd[0], _PC_PIPE_BUF);
   2666 
   2667 	if (pipesize < 1) {
   2668 		statusline(ALERT, _("Could not get size of pipe buffer"));
   2669 		close(lint_fd[0]);
   2670 		return;
   2671 	}
   2672 
   2673 	/* Block resizing signals while reading from the pipe. */
   2674 	block_sigwinch(TRUE);
   2675 
   2676 	/* Read in the returned syntax errors. */
   2677 	totalread = 0;
   2678 	buffersize = pipesize + 1;
   2679 	lintings = nmalloc(buffersize);
   2680 	pointer = lintings;
   2681 
   2682 	while ((bytesread = read(lint_fd[0], pointer, pipesize)) > 0) {
   2683 		totalread += bytesread;
   2684 		buffersize += pipesize;
   2685 		lintings = nrealloc(lintings, buffersize);
   2686 		pointer = lintings + totalread;
   2687 	}
   2688 
   2689 	*pointer = '\0';
   2690 	close(lint_fd[0]);
   2691 
   2692 	block_sigwinch(FALSE);
   2693 
   2694 	pointer = lintings;
   2695 	onelint = lintings;
   2696 
   2697 	/* Now parse the output of the linter. */
   2698 	while (*pointer != '\0') {
   2699 		if ((*pointer == '\r') || (*pointer == '\n')) {
   2700 			*pointer = '\0';
   2701 			if (onelint != pointer) {
   2702 				char *filename, *linestring, *colstring;
   2703 				char *complaint = copy_of(onelint);
   2704 				char *spacer = strstr(complaint, " ");
   2705 
   2706 				/* The recognized format is "filename:line:column: message",
   2707 				 * where ":column" may be absent or be ",column" instead. */
   2708 				if ((filename = strtok(onelint, ":")) && spacer) {
   2709 					if ((linestring = strtok(NULL, ":"))) {
   2710 						if ((colstring = strtok(NULL, " "))) {
   2711 							ssize_t linenumber = strtol(linestring, NULL, 10);
   2712 							ssize_t colnumber = strtol(colstring, NULL, 10);
   2713 
   2714 							if (linenumber <= 0) {
   2715 								free(complaint);
   2716 								pointer++;
   2717 								continue;
   2718 							}
   2719 
   2720 							if (colnumber <= 0) {
   2721 								colnumber = 1;
   2722 								strtok(linestring, ",");
   2723 								if ((colstring = strtok(NULL, ",")))
   2724 									colnumber = strtol(colstring, NULL, 10);
   2725 							}
   2726 
   2727 							parsesuccess = TRUE;
   2728 							tmplint = curlint;
   2729 							curlint = nmalloc(sizeof(lintstruct));
   2730 							curlint->next = NULL;
   2731 							curlint->prev = tmplint;
   2732 							if (curlint->prev != NULL)
   2733 								curlint->prev->next = curlint;
   2734 							curlint->filename = copy_of(filename);
   2735 							curlint->lineno = linenumber;
   2736 							curlint->colno = colnumber;
   2737 							curlint->msg = copy_of(spacer + 1);
   2738 
   2739 							if (lints == NULL)
   2740 								lints = curlint;
   2741 						}
   2742 					}
   2743 				}
   2744 				free(complaint);
   2745 			}
   2746 			onelint = pointer + 1;
   2747 		}
   2748 		pointer++;
   2749 	}
   2750 
   2751 	free(lintings);
   2752 
   2753 	/* Process the end of the linting process. */
   2754 	waitpid(pid_lint, &lint_status, 0);
   2755 
   2756 	if (!WIFEXITED(lint_status) || WEXITSTATUS(lint_status) > 2) {
   2757 		statusline(ALERT, _("Error invoking '%s'"), openfile->syntax->linter);
   2758 		for (curlint = lints; curlint != NULL;) {
   2759 			tmplint = curlint;
   2760 			curlint = curlint->next;
   2761 			free(tmplint->msg);
   2762 			free(tmplint->filename);
   2763 			free(tmplint);
   2764 		}
   2765 		return;
   2766 	}
   2767 
   2768 	if (!parsesuccess) {
   2769 		statusline(REMARK, _("Got 0 parsable lines from command: %s"),
   2770 						openfile->syntax->linter);
   2771 		return;
   2772 	}
   2773 
   2774 	/* When the help lines are off and there is room, force them on. */
   2775 	if (helpless && LINES > 5) {
   2776 		UNSET(NO_HELP);
   2777 		window_init();
   2778 	}
   2779 
   2780 	/* Show that we are in the linter now. */
   2781 	titlebar(NULL);
   2782 	bottombars(MLINTER);
   2783 
   2784 	tmplint = NULL;
   2785 	curlint = lints;
   2786 
   2787 	while (TRUE) {
   2788 		int kbinput;
   2789 		functionptrtype function;
   2790 		struct stat lintfileinfo;
   2791 
   2792 		if (stat(curlint->filename, &lintfileinfo) != -1 &&
   2793 					(openfile->statinfo == NULL ||
   2794 					openfile->statinfo->st_ino != lintfileinfo.st_ino)) {
   2795 #ifdef ENABLE_MULTIBUFFER
   2796 			const openfilestruct *started_at = openfile;
   2797 
   2798 			openfile = openfile->next;
   2799 			while (openfile != started_at && (openfile->statinfo == NULL ||
   2800 						openfile->statinfo->st_ino != lintfileinfo.st_ino))
   2801 				openfile = openfile->next;
   2802 
   2803 			if (openfile->statinfo == NULL ||
   2804 						openfile->statinfo->st_ino != lintfileinfo.st_ino) {
   2805 				char *msg = nmalloc(1024 + strlen(curlint->filename));
   2806 				int choice;
   2807 
   2808 				sprintf(msg, _("This message is for unopened file %s,"
   2809 							" open it in a new buffer?"), curlint->filename);
   2810 				choice = ask_user(YESORNO, msg);
   2811 				currmenu = MLINTER;
   2812 				free(msg);
   2813 
   2814 				if (choice == CANCEL) {
   2815 					statusbar(_("Cancelled"));
   2816 					break;
   2817 				} else if (choice == YES) {
   2818 					open_buffer(curlint->filename, TRUE);
   2819 				} else {
   2820 #endif
   2821 					char *dontwantfile = copy_of(curlint->filename);
   2822 					lintstruct *restlint = NULL;
   2823 
   2824 					while (curlint != NULL) {
   2825 						if (strcmp(curlint->filename, dontwantfile) == 0) {
   2826 							if (curlint == lints)
   2827 								lints = curlint->next;
   2828 							else
   2829 								curlint->prev->next = curlint->next;
   2830 							if (curlint->next != NULL)
   2831 								curlint->next->prev = curlint->prev;
   2832 							tmplint = curlint;
   2833 							curlint = curlint->next;
   2834 							free(tmplint->msg);
   2835 							free(tmplint->filename);
   2836 							free(tmplint);
   2837 						} else {
   2838 							if (restlint == NULL)
   2839 								restlint = curlint;
   2840 							curlint = curlint->next;
   2841 						}
   2842 					}
   2843 
   2844 					free(dontwantfile);
   2845 
   2846 					if (restlint == NULL) {
   2847 						statusline(REMARK, _("No messages for this file"));
   2848 						break;
   2849 					} else {
   2850 						curlint = restlint;
   2851 						continue;
   2852 					}
   2853 #ifdef ENABLE_MULTIBUFFER
   2854 				}
   2855 			}
   2856 #endif
   2857 		}
   2858 
   2859 		if (tmplint != curlint) {
   2860 			/* Put the cursor at the reported position, but don't go beyond EOL
   2861 			 * when the second number is a column number instead of an index. */
   2862 			goto_line_posx(curlint->lineno, curlint->colno - 1);
   2863 			openfile->current_x = actual_x(openfile->current->data, openfile->placewewant);
   2864 			titlebar(NULL);
   2865 			adjust_viewport(CENTERING);
   2866 #ifdef ENABLE_LINENUMBERS
   2867 			confirm_margin();
   2868 #endif
   2869 			edit_refresh();
   2870 			statusline(NOTICE, "%s", curlint->msg);
   2871 			bottombars(MLINTER);
   2872 		}
   2873 
   2874 		/* Place the cursor to indicate the affected line. */
   2875 		place_the_cursor();
   2876 		wnoutrefresh(midwin);
   2877 
   2878 		kbinput = get_kbinput(footwin, VISIBLE);
   2879 
   2880 #ifndef NANO_TINY
   2881 		if (kbinput == THE_WINDOW_RESIZED)
   2882 			continue;
   2883 #endif
   2884 		function = func_from_key(kbinput);
   2885 		tmplint = curlint;
   2886 
   2887 		if (function == do_cancel || function == do_enter) {
   2888 			wipe_statusbar();
   2889 			break;
   2890 		} else if (function == do_help) {
   2891 			tmplint = NULL;
   2892 			do_help();
   2893 		} else if (function == do_page_up || function == to_prev_block) {
   2894 			if (curlint->prev != NULL)
   2895 				curlint = curlint->prev;
   2896 			else if (last_wait != time(NULL)) {
   2897 				statusbar(_("At first message"));
   2898 				beep();
   2899 				napms(600);
   2900 				last_wait = time(NULL);
   2901 				statusline(NOTICE, "%s", curlint->msg);
   2902 			}
   2903 		} else if (function == do_page_down || function == to_next_block) {
   2904 			if (curlint->next != NULL)
   2905 				curlint = curlint->next;
   2906 			else if (last_wait != time(NULL)) {
   2907 				statusbar(_("At last message"));
   2908 				beep();
   2909 				napms(600);
   2910 				last_wait = time(NULL);
   2911 				statusline(NOTICE, "%s", curlint->msg);
   2912 			}
   2913 		} else
   2914 			beep();
   2915 	}
   2916 
   2917 	for (curlint = lints; curlint != NULL;) {
   2918 		tmplint = curlint;
   2919 		curlint = curlint->next;
   2920 		free(tmplint->msg);
   2921 		free(tmplint->filename);
   2922 		free(tmplint);
   2923 	}
   2924 
   2925 	if (helpless) {
   2926 		SET(NO_HELP);
   2927 		window_init();
   2928 		refresh_needed = TRUE;
   2929 	}
   2930 
   2931 	lastmessage = VACUUM;
   2932 	currmenu = MMOST;
   2933 	titlebar(NULL);
   2934 #endif
   2935 }
   2936 #endif /* ENABLE_LINTER */
   2937 
   2938 #ifdef ENABLE_FORMATTER
   2939 /* Run a manipulation program on the contents of the buffer. */
   2940 void do_formatter(void)
   2941 {
   2942 	FILE *stream;
   2943 	char *temp_name;
   2944 	bool okay = FALSE;
   2945 
   2946 	ran_a_tool = TRUE;
   2947 
   2948 	if (in_restricted_mode())
   2949 		return;
   2950 
   2951 	if (!openfile->syntax || !openfile->syntax->formatter || !*openfile->syntax->formatter) {
   2952 		statusline(AHEM, _("No formatter is defined for this type of file"));
   2953 		return;
   2954 	}
   2955 
   2956 #ifndef NANO_TINY
   2957 	openfile->mark = NULL;
   2958 #endif
   2959 
   2960 	temp_name = safe_tempfile(&stream);
   2961 
   2962 	if (temp_name != NULL)
   2963 		okay = write_file(temp_name, stream, TEMPORARY, OVERWRITE, NONOTES);
   2964 
   2965 	if (!okay)
   2966 		statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
   2967 	else
   2968 		treat(temp_name, openfile->syntax->formatter, FALSE);
   2969 
   2970 	unlink(temp_name);
   2971 	free(temp_name);
   2972 }
   2973 #endif /* ENABLE_FORMATTER */
   2974 
   2975 #ifndef NANO_TINY
   2976 /* Our own version of "wc".  Note that the character count is in
   2977  * multibyte characters instead of single-byte characters. */
   2978 void count_lines_words_and_characters(void)
   2979 {
   2980 	linestruct *was_current = openfile->current;
   2981 	size_t was_x = openfile->current_x;
   2982 	linestruct *topline, *botline;
   2983 	size_t top_x, bot_x;
   2984 	size_t words = 0, chars = 0;
   2985 	ssize_t lines = 0;
   2986 
   2987 	/* Set the start and end point of the area to measure: either the marked
   2988 	 * region or the whole buffer.  Then compute the number of characters. */
   2989 	if (openfile->mark) {
   2990 		get_region(&topline, &top_x, &botline, &bot_x);
   2991 
   2992 		if (topline != botline)
   2993 			chars = number_of_characters_in(topline->next, botline) + 1;
   2994 
   2995 		chars += mbstrlen(topline->data + top_x) - mbstrlen(botline->data + bot_x);
   2996 	} else {
   2997 		topline = openfile->filetop;
   2998 		top_x = 0;
   2999 		botline = openfile->filebot;
   3000 		bot_x = strlen(botline->data);
   3001 
   3002 		chars = openfile->totsize;
   3003 	}
   3004 
   3005 	/* Compute the number of lines. */
   3006 	lines = botline->lineno - topline->lineno;
   3007 	lines += (bot_x == 0 || (topline == botline && top_x == bot_x)) ? 0 : 1;
   3008 
   3009 	openfile->current = topline;
   3010 	openfile->current_x = top_x;
   3011 
   3012 	/* Keep stepping to the next word (considering punctuation as part of a
   3013 	 * word, as "wc -w" does), until we reach the end of the relevant area,
   3014 	 * incrementing the word count for each successful step. */
   3015 	while (openfile->current->lineno < botline->lineno ||
   3016 				(openfile->current == botline && openfile->current_x < bot_x)) {
   3017 		if (do_next_word(FALSE))
   3018 			words++;
   3019 	}
   3020 
   3021 	/* Restore where we were. */
   3022 	openfile->current = was_current;
   3023 	openfile->current_x = was_x;
   3024 
   3025 	/* Report on the status bar the number of lines, words, and characters. */
   3026 	statusline(INFO, _("%s%zd %s,  %zu %s,  %zu %s"),
   3027 						openfile->mark ? _("In Selection:  ") : "",
   3028 						lines, P_("line", "lines", lines),
   3029 						words, P_("word", "words", words),
   3030 						chars, P_("character", "characters", chars));
   3031 }
   3032 #endif /* !NANO_TINY */
   3033 
   3034 /* Get verbatim input. */
   3035 void do_verbatim_input(void)
   3036 {
   3037 	size_t count = 1;
   3038 	char *bytes;
   3039 
   3040 #ifndef NANO_TINY
   3041 	/* When barless and with cursor on bottom row, make room for the feedback. */
   3042 	if (ISSET(ZERO) && openfile->cursor_row == editwinrows - 1 && LINES > 1) {
   3043 		edit_scroll(FORWARD);
   3044 		edit_refresh();
   3045 	}
   3046 #endif
   3047 	/* TRANSLATORS: Shown when the next keystroke will be inserted verbatim. */
   3048 	statusline(INFO, _("Verbatim Input"));
   3049 	place_the_cursor();
   3050 
   3051 	/* Read in the first one or two bytes of the next keystroke. */
   3052 	bytes = get_verbatim_kbinput(midwin, &count);
   3053 
   3054 	/* When something valid was obtained, unsuppress cursor-position display,
   3055 	 * insert the bytes into the edit buffer, and blank the status bar. */
   3056 	if (count > 0) {
   3057 		if (ISSET(CONSTANT_SHOW) || ISSET(MINIBAR))
   3058 			lastmessage = VACUUM;
   3059 
   3060 		if (count < 999)
   3061 			inject(bytes, count);
   3062 
   3063 #ifndef NANO_TINY
   3064 		/* Ensure that the feedback will be overwritten, or clear it. */
   3065 		if (ISSET(ZERO) && currmenu == MMAIN)
   3066 			wredrawln(midwin, editwinrows - 1, 1);
   3067 		else
   3068 #endif
   3069 			wipe_statusbar();
   3070 	} else
   3071 		/* TRANSLATORS: An invalid verbatim Unicode code was typed. */
   3072 		statusline(AHEM, _("Invalid code"));
   3073 
   3074 	free(bytes);
   3075 }
   3076 
   3077 #ifdef ENABLE_WORDCOMPLETION
   3078 /* Return a copy of the found completion candidate. */
   3079 char *copy_completion(char *text)
   3080 {
   3081 	char *word;
   3082 	size_t length = 0, index = 0;
   3083 
   3084 	/* Find the end of the candidate word to get its length. */
   3085 	while (is_word_char(&text[length], FALSE))
   3086 		length = step_right(text, length);
   3087 
   3088 	/* Now copy this candidate to a new string. */
   3089 	word = nmalloc(length + 1);
   3090 	while (index < length)
   3091 		word[index++] = *(text++);
   3092 	word[index] = '\0';
   3093 
   3094 	return word;
   3095 }
   3096 
   3097 /* Look at the fragment the user has typed, then search all buffers
   3098  * for the first word that starts with this fragment, and tentatively
   3099  * complete the fragment.  If the user hits 'Complete' again, search
   3100  * and paste the next possible completion. */
   3101 void complete_a_word(void)
   3102 {
   3103 	static openfilestruct *scouring = NULL;
   3104 		/* The buffer that is being searched for possible completions. */
   3105 	static completionstruct *list_of_completions;
   3106 		/* A linked list of the completions that have been attempted. */
   3107 	static int pletion_x = 0;
   3108 		/* The x position in `pletion_line` of the last found completion. */
   3109 #ifdef ENABLE_WRAPPING
   3110 	bool was_set_wrapping = ISSET(BREAK_LONG_LINES);
   3111 #endif
   3112 	size_t start_of_shard;
   3113 	size_t shard_length = 0;
   3114 	char *shard;
   3115 
   3116 	/* If this is a fresh completion attempt... */
   3117 	if (pletion_line == NULL) {
   3118 		/* Clear the list of words of a previous completion run. */
   3119 		while (list_of_completions != NULL) {
   3120 			completionstruct *dropit = list_of_completions;
   3121 			list_of_completions = list_of_completions->next;
   3122 			free(dropit->word);
   3123 			free(dropit);
   3124 		}
   3125 
   3126 		/* Prevent a completion from being merged with typed text. */
   3127 		openfile->last_action = OTHER;
   3128 
   3129 		/* Initialize the starting point for searching. */
   3130 		scouring = openfile;
   3131 		pletion_line = openfile->filetop;
   3132 		pletion_x = 0;
   3133 
   3134 		/* Wipe the "No further matches" message. */
   3135 		wipe_statusbar();
   3136 	} else {
   3137 		/* Remove the attempted completion from the buffer. */
   3138 		do_undo();
   3139 	}
   3140 
   3141 	/* Find the start of the fragment that the user typed. */
   3142 	start_of_shard = openfile->current_x;
   3143 	while (start_of_shard > 0) {
   3144 		size_t oneleft = step_left(openfile->current->data, start_of_shard);
   3145 
   3146 		if (!is_word_char(&openfile->current->data[oneleft], FALSE))
   3147 			break;
   3148 		start_of_shard = oneleft;
   3149 	}
   3150 
   3151 	/* If there is no word fragment before the cursor, do nothing. */
   3152 	if (start_of_shard == openfile->current_x) {
   3153 		/* TRANSLATORS: Shown when no text is directly left of the cursor. */
   3154 		statusline(AHEM, _("No word fragment"));
   3155 		pletion_line = NULL;
   3156 		return;
   3157 	}
   3158 
   3159 	shard = nmalloc(openfile->current_x - start_of_shard + 1);
   3160 
   3161 	/* Copy the fragment that has to be searched for. */
   3162 	while (start_of_shard < openfile->current_x)
   3163 		shard[shard_length++] = openfile->current->data[start_of_shard++];
   3164 	shard[shard_length] = '\0';
   3165 
   3166 	/* Run through all of the lines in the buffer, looking for shard. */
   3167 	while (pletion_line != NULL) {
   3168 		ssize_t threshold = strlen(pletion_line->data) - shard_length - 1;
   3169 				/* The point where we can stop searching for shard. */
   3170 		completionstruct *some_word;
   3171 		char *completion;
   3172 		size_t i, j;
   3173 
   3174 		/* Traverse the whole line, looking for shard. */
   3175 		for (i = pletion_x; (ssize_t)i < threshold; i++) {
   3176 			/* If the first byte doesn't match, run on. */
   3177 			if (pletion_line->data[i] != shard[0])
   3178 				continue;
   3179 
   3180 			/* Compare the rest of the bytes in shard. */
   3181 			for (j = 1; j < shard_length; j++)
   3182 				if (pletion_line->data[i + j] != shard[j])
   3183 					break;
   3184 
   3185 			/* If not all of the bytes matched, continue searching. */
   3186 			if (j < shard_length)
   3187 				continue;
   3188 
   3189 			/* If the found match is not /longer/ than shard, skip it. */
   3190 			if (!is_word_char(&pletion_line->data[i + j], FALSE))
   3191 				continue;
   3192 
   3193 			/* If the match is not a separate word, skip it. */
   3194 			if (i > 0 && is_word_char(&pletion_line->data[
   3195 								step_left(pletion_line->data, i)], FALSE))
   3196 				continue;
   3197 
   3198 			/* If this match is the shard itself, ignore it. */
   3199 			if (pletion_line == openfile->current &&
   3200 								i == openfile->current_x - shard_length)
   3201 				continue;
   3202 
   3203 			completion = copy_completion(pletion_line->data + i);
   3204 
   3205 			/* Look among earlier attempted completions for a duplicate. */
   3206 			some_word = list_of_completions;
   3207 			while (some_word && strcmp(some_word->word, completion) != 0)
   3208 				some_word = some_word->next;
   3209 
   3210 			/* If we've already tried this word, skip it. */
   3211 			if (some_word != NULL) {
   3212 				free(completion);
   3213 				continue;
   3214 			}
   3215 
   3216 			/* Add the found word to the list of completions. */
   3217 			some_word = nmalloc(sizeof(completionstruct));
   3218 			some_word->word = completion;
   3219 			some_word->next = list_of_completions;
   3220 			list_of_completions = some_word;
   3221 
   3222 #ifdef ENABLE_WRAPPING
   3223 			/* Temporarily disable wrapping so only one undo item is added. */
   3224 			UNSET(BREAK_LONG_LINES);
   3225 #endif
   3226 			/* Inject the completion into the buffer. */
   3227 			inject(&completion[shard_length], strlen(completion) - shard_length);
   3228 
   3229 #ifdef ENABLE_WRAPPING
   3230 			/* If needed, reenable wrapping and wrap the current line. */
   3231 			if (was_set_wrapping) {
   3232 				SET(BREAK_LONG_LINES);
   3233 				do_wrap();
   3234 			}
   3235 #endif
   3236 			/* Set the position for a possible next search attempt. */
   3237 			pletion_x = ++i;
   3238 
   3239 			free(shard);
   3240 			return;
   3241 		}
   3242 
   3243 		pletion_line = pletion_line->next;
   3244 		pletion_x = 0;
   3245 
   3246 #ifdef ENABLE_MULTIBUFFER
   3247 		/* When at end of buffer and there is another, search that one. */
   3248 		if (pletion_line == NULL && scouring->next != openfile) {
   3249 			scouring = scouring->next;
   3250 			pletion_line = scouring->filetop;
   3251 		}
   3252 #endif
   3253 	}
   3254 
   3255 	/* The search has gone through all buffers. */
   3256 	if (list_of_completions != NULL) {
   3257 		edit_refresh();
   3258 		statusline(AHEM, _("No further matches"));
   3259 	} else
   3260 		/* TRANSLATORS: Shown when there are zero possible completions. */
   3261 		statusline(AHEM, _("No matches"));
   3262 
   3263 	free(shard);
   3264 }
   3265 #endif /* ENABLE_WORDCOMPLETION */