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