1/* $Id: text.c,v 1.163.2.1 2007/04/22 15:04:05 dolorous Exp $ */
2/**************************************************************************
3 *   text.c                                                               *
4 *                                                                        *
5 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Chris Allegretta    *
6 *   Copyright (C) 2005, 2006, 2007 David Lawrence Ramsey                 *
7 *   This program is free software; you can redistribute it and/or modify *
8 *   it under the terms of the GNU General Public License as published by *
9 *   the Free Software Foundation; either version 2, or (at your option)  *
10 *   any later version.                                                   *
11 *                                                                        *
12 *   This program is distributed in the hope that it will be useful, but  *
13 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
15 *   General Public License for more details.                             *
16 *                                                                        *
17 *   You should have received a copy of the GNU General Public License    *
18 *   along with this program; if not, write to the Free Software          *
19 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
20 *   02110-1301, USA.                                                     *
21 *                                                                        *
22 **************************************************************************/
23
24#include "proto.h"
25
26#include <stdio.h>
27#include <signal.h>
28#include <unistd.h>
29#include <string.h>
30#include <fcntl.h>
31#include <sys/wait.h>
32#include <errno.h>
33
34#ifndef NANO_TINY
35static pid_t pid = -1;
36	/* The PID of the forked process in execute_command(), for use
37	 * with the cancel_command() signal handler. */
38#endif
39#ifndef DISABLE_WRAPPING
40static bool prepend_wrap = FALSE;
41	/* Should we prepend wrapped text to the next line? */
42#endif
43#ifndef DISABLE_JUSTIFY
44static filestruct *jusbottom = NULL;
45	/* Pointer to the end of the justify buffer. */
46#endif
47
48#ifndef NANO_TINY
49/* Toggle the mark. */
50void do_mark(void)
51{
52    openfile->mark_set = !openfile->mark_set;
53    if (openfile->mark_set) {
54	statusbar(_("Mark Set"));
55	openfile->mark_begin = openfile->current;
56	openfile->mark_begin_x = openfile->current_x;
57    } else {
58	statusbar(_("Mark Unset"));
59	openfile->mark_begin = NULL;
60	openfile->mark_begin_x = 0;
61	edit_refresh();
62    }
63}
64#endif /* !NANO_TINY */
65
66/* Delete the character under the cursor. */
67void do_delete(void)
68{
69    bool do_refresh = FALSE;
70	/* Do we have to call edit_refresh(), or can we get away with
71	 * just update_line()? */
72
73    assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data));
74
75    openfile->placewewant = xplustabs();
76
77    if (openfile->current->data[openfile->current_x] != '\0') {
78	int char_buf_len = parse_mbchar(openfile->current->data +
79		openfile->current_x, NULL, NULL);
80	size_t line_len = strlen(openfile->current->data +
81		openfile->current_x);
82
83	assert(openfile->current_x < strlen(openfile->current->data));
84
85	/* Let's get dangerous. */
86	charmove(&openfile->current->data[openfile->current_x],
87		&openfile->current->data[openfile->current_x +
88		char_buf_len], line_len - char_buf_len + 1);
89
90	null_at(&openfile->current->data, openfile->current_x +
91		line_len - char_buf_len);
92#ifndef NANO_TINY
93	if (openfile->mark_set && openfile->mark_begin ==
94		openfile->current && openfile->current_x <
95		openfile->mark_begin_x)
96	    openfile->mark_begin_x -= char_buf_len;
97#endif
98	openfile->totsize--;
99    } else if (openfile->current != openfile->filebot) {
100	filestruct *foo = openfile->current->next;
101
102	assert(openfile->current_x == strlen(openfile->current->data));
103
104	/* If we're deleting at the end of a line, we need to call
105	 * edit_refresh(). */
106	if (openfile->current->data[openfile->current_x] == '\0')
107	    do_refresh = TRUE;
108
109	openfile->current->data = charealloc(openfile->current->data,
110		openfile->current_x + strlen(foo->data) + 1);
111	strcpy(openfile->current->data + openfile->current_x,
112		foo->data);
113#ifndef NANO_TINY
114	if (openfile->mark_set && openfile->mark_begin ==
115		openfile->current->next) {
116	    openfile->mark_begin = openfile->current;
117	    openfile->mark_begin_x += openfile->current_x;
118	}
119#endif
120	if (openfile->filebot == foo)
121	    openfile->filebot = openfile->current;
122
123	unlink_node(foo);
124	delete_node(foo);
125	renumber(openfile->current);
126	openfile->totsize--;
127
128	/* If the NO_NEWLINES flag isn't set, and text has been added to
129	 * the magicline as a result of deleting at the end of the line
130	 * before filebot, add a new magicline. */
131	if (!ISSET(NO_NEWLINES) && openfile->current ==
132		openfile->filebot && openfile->current->data[0] != '\0')
133	    new_magicline();
134    } else
135	return;
136
137    set_modified();
138
139#ifdef ENABLE_COLOR
140    /* If color syntaxes are available and turned on, we need to call
141     * edit_refresh(). */
142    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX))
143	do_refresh = TRUE;
144#endif
145
146    if (do_refresh)
147	edit_refresh();
148    else
149	update_line(openfile->current, openfile->current_x);
150}
151
152/* Backspace over one character.  That is, move the cursor left one
153 * character, and then delete the character under the cursor. */
154void do_backspace(void)
155{
156    if (openfile->current != openfile->fileage ||
157	openfile->current_x > 0) {
158	do_left();
159	do_delete();
160    }
161}
162
163/* Insert a tab.  If the TABS_TO_SPACES flag is set, insert the number
164 * of spaces that a tab would normally take up. */
165void do_tab(void)
166{
167#ifndef NANO_TINY
168    if (ISSET(TABS_TO_SPACES)) {
169	char *output;
170	size_t output_len = 0, new_pww = xplustabs();
171
172	do {
173	    new_pww++;
174	    output_len++;
175	} while (new_pww % tabsize != 0);
176
177	output = charalloc(output_len + 1);
178
179	charset(output, ' ', output_len);
180	output[output_len] = '\0';
181
182	do_output(output, output_len, TRUE);
183
184	free(output);
185    } else {
186#endif
187	do_output("\t", 1, TRUE);
188#ifndef NANO_TINY
189    }
190#endif
191}
192
193#ifndef NANO_TINY
194/* Indent or unindent the current line (or, if the mark is on, all lines
195 * covered by the mark) len columns, depending on whether len is
196 * positive or negative.  If the TABS_TO_SPACES flag is set, indent or
197 * unindent by len spaces.  Otherwise, indent or unindent by (len /
198 * tabsize) tabs and (len % tabsize) spaces. */
199void do_indent(ssize_t cols)
200{
201    bool indent_changed = FALSE;
202	/* Whether any indenting or unindenting was done. */
203    bool unindent = FALSE;
204	/* Whether we're unindenting text. */
205    char *line_indent = NULL;
206	/* The text added to each line in order to indent it. */
207    size_t line_indent_len = 0;
208	/* The length of the text added to each line in order to indent
209	 * it. */
210    filestruct *top, *bot, *f;
211    size_t top_x, bot_x;
212
213    assert(openfile->current != NULL && openfile->current->data != NULL);
214
215    /* If cols is zero, get out. */
216    if (cols == 0)
217	return;
218
219    /* If cols is negative, make it positive and set unindent to
220     * TRUE. */
221    if (cols < 0) {
222	cols = -cols;
223	unindent = TRUE;
224    /* Otherwise, we're indenting, in which case the file will always be
225     * modified, so set indent_changed to TRUE. */
226    } else
227	indent_changed = TRUE;
228
229    /* If the mark is on, use all lines covered by the mark. */
230    if (openfile->mark_set)
231	mark_order((const filestruct **)&top, &top_x,
232		(const filestruct **)&bot, &bot_x, NULL);
233    /* Otherwise, use the current line. */
234    else {
235	top = openfile->current;
236	bot = top;
237    }
238
239    if (!unindent) {
240	/* Set up the text we'll be using as indentation. */
241	line_indent = charalloc(cols + 1);
242
243	if (ISSET(TABS_TO_SPACES)) {
244	    /* Set the indentation to cols spaces. */
245	    charset(line_indent, ' ', cols);
246	    line_indent_len = cols;
247	} else {
248	    /* Set the indentation to (cols / tabsize) tabs and (cols %
249	     * tabsize) spaces. */
250	    size_t num_tabs = cols / tabsize;
251	    size_t num_spaces = cols % tabsize;
252
253	    charset(line_indent, '\t', num_tabs);
254	    charset(line_indent + num_tabs, ' ', num_spaces);
255
256	    line_indent_len = num_tabs + num_spaces;
257	}
258
259	line_indent[line_indent_len] = '\0';
260    }
261
262    /* Go through each line of the text. */
263    for (f = top; f != bot->next; f = f->next) {
264	size_t line_len = strlen(f->data);
265	size_t indent_len = indent_length(f->data);
266
267	if (!unindent) {
268	    /* If we're indenting, add the characters in line_indent to
269	     * the beginning of the non-whitespace text of this line. */
270	    f->data = charealloc(f->data, line_len +
271		line_indent_len + 1);
272	    charmove(&f->data[indent_len + line_indent_len],
273		&f->data[indent_len], line_len - indent_len + 1);
274	    strncpy(f->data + indent_len, line_indent, line_indent_len);
275	    openfile->totsize += line_indent_len;
276
277	    /* Keep track of the change in the current line. */
278	    if (openfile->mark_set && f == openfile->mark_begin &&
279		openfile->mark_begin_x >= indent_len)
280		openfile->mark_begin_x += line_indent_len;
281
282	    if (f == openfile->current && openfile->current_x >=
283		indent_len)
284		openfile->current_x += line_indent_len;
285
286	    /* If the NO_NEWLINES flag isn't set, and this is the
287	     * magicline, add a new magicline. */
288	    if (!ISSET(NO_NEWLINES) && f == openfile->filebot)
289		new_magicline();
290	} else {
291	    size_t indent_col = strnlenpt(f->data, indent_len);
292		/* The length in columns of the indentation on this
293		 * line. */
294
295	    if (cols <= indent_col) {
296		size_t indent_new = actual_x(f->data, indent_col -
297			cols);
298			/* The length of the indentation remaining on
299			 * this line after we unindent. */
300		size_t indent_shift = indent_len - indent_new;
301			/* The change in the indentation on this line
302			 * after we unindent. */
303
304		/* If we're unindenting, and there's at least cols
305		 * columns' worth of indentation at the beginning of the
306		 * non-whitespace text of this line, remove it. */
307		charmove(&f->data[indent_new], &f->data[indent_len],
308			line_len - indent_shift - indent_new + 1);
309		null_at(&f->data, line_len - indent_shift + 1);
310		openfile->totsize -= indent_shift;
311
312		/* Keep track of the change in the current line. */
313		if (openfile->mark_set && f == openfile->mark_begin &&
314			openfile->mark_begin_x > indent_new) {
315		    if (openfile->mark_begin_x <= indent_len)
316			openfile->mark_begin_x = indent_new;
317		    else
318			openfile->mark_begin_x -= indent_shift;
319		}
320
321		if (f == openfile->current && openfile->current_x >
322			indent_new) {
323		    if (openfile->current_x <= indent_len)
324			openfile->current_x = indent_new;
325		    else
326			openfile->current_x -= indent_shift;
327		}
328
329		/* We've unindented, so set indent_changed to TRUE. */
330		if (!indent_changed)
331		    indent_changed = TRUE;
332	    }
333	}
334    }
335
336    if (!unindent)
337	/* Clean up. */
338	free(line_indent);
339
340    if (indent_changed) {
341	/* Mark the file as modified. */
342	set_modified();
343
344	/* Update the screen. */
345	edit_refresh();
346    }
347}
348
349/* Indent the current line, or all lines covered by the mark if the mark
350 * is on, tabsize columns. */
351void do_indent_void(void)
352{
353    do_indent(tabsize);
354}
355
356/* Unindent the current line, or all lines covered by the mark if the
357 * mark is on, tabsize columns. */
358void do_unindent(void)
359{
360    do_indent(-tabsize);
361}
362#endif /* !NANO_TINY */
363
364/* Someone hits Enter *gasp!* */
365void do_enter(void)
366{
367    filestruct *newnode = make_new_node(openfile->current);
368    size_t extra = 0;
369
370    assert(openfile->current != NULL && openfile->current->data != NULL);
371
372#ifndef NANO_TINY
373    /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
374    if (ISSET(AUTOINDENT)) {
375	/* If we are breaking the line in the indentation, the new
376	 * indentation should have only current_x characters, and
377	 * current_x should not change. */
378	extra = indent_length(openfile->current->data);
379	if (extra > openfile->current_x)
380	    extra = openfile->current_x;
381    }
382#endif
383    newnode->data = charalloc(strlen(openfile->current->data +
384	openfile->current_x) + extra + 1);
385    strcpy(&newnode->data[extra], openfile->current->data +
386	openfile->current_x);
387#ifndef NANO_TINY
388    if (ISSET(AUTOINDENT)) {
389	strncpy(newnode->data, openfile->current->data, extra);
390	openfile->totsize += mbstrlen(newnode->data);
391    }
392#endif
393    null_at(&openfile->current->data, openfile->current_x);
394#ifndef NANO_TINY
395    if (openfile->mark_set && openfile->current ==
396	openfile->mark_begin && openfile->current_x <
397	openfile->mark_begin_x) {
398	openfile->mark_begin = newnode;
399	openfile->mark_begin_x += extra - openfile->current_x;
400    }
401#endif
402    openfile->current_x = extra;
403
404    if (openfile->current == openfile->filebot)
405	openfile->filebot = newnode;
406    splice_node(openfile->current, newnode,
407	openfile->current->next);
408
409    renumber(openfile->current);
410    openfile->current = newnode;
411
412    openfile->totsize++;
413    set_modified();
414
415    openfile->placewewant = xplustabs();
416
417    edit_refresh();
418}
419
420#ifndef NANO_TINY
421/* Send a SIGKILL (unconditional kill) to the forked process in
422 * execute_command(). */
423RETSIGTYPE cancel_command(int signal)
424{
425    if (kill(pid, SIGKILL) == -1)
426	nperror("kill");
427}
428
429/* Execute command in a shell.  Return TRUE on success. */
430bool execute_command(const char *command)
431{
432    int fd[2];
433    FILE *f;
434    char *shellenv;
435    struct sigaction oldaction, newaction;
436	/* Original and temporary handlers for SIGINT. */
437    bool sig_failed = FALSE;
438	/* Did sigaction() fail without changing the signal handlers? */
439
440    /* Make our pipes. */
441    if (pipe(fd) == -1) {
442	statusbar(_("Could not pipe"));
443	return FALSE;
444    }
445
446    /* Check $SHELL for the shell to use.  If it isn't set, use
447     * /bin/sh.  Note that $SHELL should contain only a path, with no
448     * arguments. */
449    shellenv = getenv("SHELL");
450    if (shellenv == NULL)
451	shellenv = "/bin/sh";
452
453    /* Fork a child. */
454    if ((pid = fork()) == 0) {
455	close(fd[0]);
456	dup2(fd[1], fileno(stdout));
457	dup2(fd[1], fileno(stderr));
458
459	/* If execl() returns at all, there was an error. */
460	execl(shellenv, tail(shellenv), "-c", command, NULL);
461	exit(0);
462    }
463
464    /* Continue as parent. */
465    close(fd[1]);
466
467    if (pid == -1) {
468	close(fd[0]);
469	statusbar(_("Could not fork"));
470	return FALSE;
471    }
472
473    /* Before we start reading the forked command's output, we set
474     * things up so that Ctrl-C will cancel the new process. */
475
476    /* Enable interpretation of the special control keys so that we get
477     * SIGINT when Ctrl-C is pressed. */
478    enable_signals();
479
480    if (sigaction(SIGINT, NULL, &newaction) == -1) {
481	sig_failed = TRUE;
482	nperror("sigaction");
483    } else {
484	newaction.sa_handler = cancel_command;
485	if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
486	    sig_failed = TRUE;
487	    nperror("sigaction");
488	}
489    }
490
491    /* Note that now oldaction is the previous SIGINT signal handler,
492     * to be restored later. */
493
494    f = fdopen(fd[0], "rb");
495    if (f == NULL)
496	nperror("fdopen");
497
498    read_file(f, "stdin");
499
500    if (wait(NULL) == -1)
501	nperror("wait");
502
503    if (!sig_failed && sigaction(SIGINT, &oldaction, NULL) == -1)
504	nperror("sigaction");
505
506    /* Disable interpretation of the special control keys so that we can
507     * use Ctrl-C for other things. */
508    disable_signals();
509
510    return TRUE;
511}
512#endif /* !NANO_TINY */
513
514#ifndef DISABLE_WRAPPING
515/* Unset the prepend_wrap flag.  We need to do this as soon as we do
516 * something other than type text. */
517void wrap_reset(void)
518{
519    prepend_wrap = FALSE;
520}
521
522/* We wrap the given line.  Precondition: we assume the cursor has been
523 * moved forward since the last typed character.  Return TRUE if we
524 * wrapped, and FALSE otherwise. */
525bool do_wrap(filestruct *line)
526{
527    size_t line_len;
528	/* The length of the line we wrap. */
529    ssize_t wrap_loc;
530	/* The index of line->data where we wrap. */
531#ifndef NANO_TINY
532    const char *indent_string = NULL;
533	/* Indentation to prepend to the new line. */
534    size_t indent_len = 0;
535	/* The length of indent_string. */
536#endif
537    const char *after_break;
538	/* The text after the wrap point. */
539    size_t after_break_len;
540	/* The length of after_break. */
541    bool prepending = FALSE;
542	/* Do we prepend to the next line? */
543    const char *next_line = NULL;
544	/* The next line, minus indentation. */
545    size_t next_line_len = 0;
546	/* The length of next_line. */
547    char *new_line = NULL;
548	/* The line we create. */
549    size_t new_line_len = 0;
550	/* The eventual length of new_line. */
551
552    /* There are three steps.  First, we decide where to wrap.  Then, we
553     * create the new wrap line.  Finally, we clean up. */
554
555    /* Step 1, finding where to wrap.  We are going to add a new line
556     * after a blank character.  In this step, we call break_line() to
557     * get the location of the last blank we can break the line at, and
558     * set wrap_loc to the location of the character after it, so that
559     * the blank is preserved at the end of the line.
560     *
561     * If there is no legal wrap point, or we reach the last character
562     * of the line while trying to find one, we should return without
563     * wrapping.  Note that if autoindent is turned on, we don't break
564     * at the end of it! */
565    assert(line != NULL && line->data != NULL);
566
567    /* Save the length of the line. */
568    line_len = strlen(line->data);
569
570    /* Find the last blank where we can break the line. */
571    wrap_loc = break_line(line->data, fill
572#ifndef DISABLE_HELP
573	, FALSE
574#endif
575	);
576
577    /* If we couldn't break the line, or we've reached the end of it, we
578     * don't wrap. */
579    if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
580	return FALSE;
581
582    /* Otherwise, move forward to the character just after the blank. */
583    wrap_loc += move_mbright(line->data + wrap_loc, 0);
584
585    /* If we've reached the end of the line, we don't wrap. */
586    if (line->data[wrap_loc] == '\0')
587	return FALSE;
588
589#ifndef NANO_TINY
590    /* If autoindent is turned on, and we're on the character just after
591     * the indentation, we don't wrap. */
592    if (ISSET(AUTOINDENT)) {
593	/* Get the indentation of this line. */
594	indent_string = line->data;
595	indent_len = indent_length(indent_string);
596
597	if (wrap_loc == indent_len)
598	    return FALSE;
599    }
600#endif
601
602    /* Step 2, making the new wrap line.  It will consist of indentation
603     * followed by the text after the wrap point, optionally followed by
604     * a space (if the text after the wrap point doesn't end in a blank)
605     * and the text of the next line, if they can fit without wrapping,
606     * the next line exists, and the prepend_wrap flag is set. */
607
608    /* after_break is the text that will be wrapped to the next line. */
609    after_break = line->data + wrap_loc;
610    after_break_len = line_len - wrap_loc;
611
612    assert(strlen(after_break) == after_break_len);
613
614    /* We prepend the wrapped text to the next line, if the prepend_wrap
615     * flag is set, there is a next line, and prepending would not make
616     * the line too long. */
617    if (prepend_wrap && line != openfile->filebot) {
618	const char *end = after_break + move_mbleft(after_break,
619		after_break_len);
620
621	/* If after_break doesn't end in a blank, make sure it ends in a
622	 * space. */
623	if (!is_blank_mbchar(end)) {
624	    line_len++;
625	    line->data = charealloc(line->data, line_len + 1);
626	    line->data[line_len - 1] = ' ';
627	    line->data[line_len] = '\0';
628	    after_break = line->data + wrap_loc;
629	    after_break_len++;
630	    openfile->totsize++;
631	}
632
633	next_line = line->next->data;
634	next_line_len = strlen(next_line);
635
636	if (after_break_len + next_line_len <= fill) {
637	    prepending = TRUE;
638	    new_line_len += next_line_len;
639	}
640    }
641
642    /* new_line_len is now the length of the text that will be wrapped
643     * to the next line, plus (if we're prepending to it) the length of
644     * the text of the next line. */
645    new_line_len += after_break_len;
646
647#ifndef NANO_TINY
648    if (ISSET(AUTOINDENT)) {
649	if (prepending) {
650	    /* If we're prepending, the indentation will come from the
651	     * next line. */
652	    indent_string = next_line;
653	    indent_len = indent_length(indent_string);
654	    next_line += indent_len;
655	} else {
656	    /* Otherwise, it will come from this line, in which case
657	     * we should increase new_line_len to make room for it. */
658	    new_line_len += indent_len;
659	    openfile->totsize += mbstrnlen(indent_string, indent_len);
660	}
661    }
662#endif
663
664    /* Now we allocate the new line and copy the text into it. */
665    new_line = charalloc(new_line_len + 1);
666    new_line[0] = '\0';
667
668#ifndef NANO_TINY
669    if (ISSET(AUTOINDENT)) {
670	/* Copy the indentation. */
671	strncpy(new_line, indent_string, indent_len);
672	new_line[indent_len] = '\0';
673	new_line_len += indent_len;
674    }
675#endif
676
677    /* Copy all the text after the wrap point of the current line. */
678    strcat(new_line, after_break);
679
680    /* Break the current line at the wrap point. */
681    null_at(&line->data, wrap_loc);
682
683    if (prepending) {
684	/* If we're prepending, copy the text from the next line, minus
685	 * the indentation that we already copied above. */
686	strcat(new_line, next_line);
687
688	free(line->next->data);
689	line->next->data = new_line;
690
691	/* If the NO_NEWLINES flag isn't set, and text has been added to
692	 * the magicline, make a new magicline. */
693	if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
694	    new_magicline();
695    } else {
696	/* Otherwise, make a new line and copy the text after where we
697	 * broke this line to the beginning of the new line. */
698	splice_node(openfile->current, make_new_node(openfile->current),
699		openfile->current->next);
700
701	/* If the current line is the last line of the file, move the
702	 * last line of the file down to the next line. */
703	if (openfile->filebot == openfile->current)
704	    openfile->filebot = openfile->current->next;
705
706	openfile->current->next->data = new_line;
707
708	openfile->totsize++;
709    }
710
711    /* Step 3, clean up.  Reposition the cursor and mark, and do some
712     * other sundry things. */
713
714    /* Set the prepend_wrap flag, so that later wraps of this line will
715     * be prepended to the next line. */
716    prepend_wrap = TRUE;
717
718    /* Each line knows its number.  We recalculate these if we inserted
719     * a new line. */
720    if (!prepending)
721	renumber(line);
722
723    /* If the cursor was after the break point, we must move it.  We
724     * also clear the prepend_wrap flag in this case. */
725    if (openfile->current_x > wrap_loc) {
726	prepend_wrap = FALSE;
727
728	openfile->current = openfile->current->next;
729	openfile->current_x -= wrap_loc
730#ifndef NANO_TINY
731		- indent_len
732#endif
733		;
734	openfile->placewewant = xplustabs();
735    }
736
737#ifndef NANO_TINY
738    /* If the mark was on this line after the wrap point, we move it
739     * down.  If it was on the next line and we prepended to that line,
740     * we move it right. */
741    if (openfile->mark_set) {
742	if (openfile->mark_begin == line && openfile->mark_begin_x >
743		wrap_loc) {
744	    openfile->mark_begin = line->next;
745	    openfile->mark_begin_x -= wrap_loc - indent_len + 1;
746	} else if (prepending && openfile->mark_begin == line->next)
747	    openfile->mark_begin_x += after_break_len;
748    }
749#endif
750
751    return TRUE;
752}
753#endif /* !DISABLE_WRAPPING */
754
755#if !defined(DISABLE_HELP) || !defined(DISABLE_WRAPJUSTIFY)
756/* We are trying to break a chunk off line.  We find the last blank such
757 * that the display length to there is at most (goal + 1).  If there is
758 * no such blank, then we find the first blank.  We then take the last
759 * blank in that group of blanks.  The terminating '\0' counts as a
760 * blank, as does a '\n' if newline is TRUE. */
761ssize_t break_line(const char *line, ssize_t goal
762#ifndef DISABLE_HELP
763	, bool newline
764#endif
765	)
766{
767    ssize_t blank_loc = -1;
768	/* Current tentative return value.  Index of the last blank we
769	 * found with short enough display width.  */
770    ssize_t cur_loc = 0;
771	/* Current index in line. */
772    size_t cur_pos = 0;
773	/* Current column position in line. */
774    int line_len;
775
776    assert(line != NULL);
777
778    while (*line != '\0' && goal >= cur_pos) {
779	line_len = parse_mbchar(line, NULL, &cur_pos);
780
781	if (is_blank_mbchar(line)
782#ifndef DISABLE_HELP
783		|| (newline && *line == '\n')
784#endif
785		) {
786	    blank_loc = cur_loc;
787
788#ifndef DISABLE_HELP
789	    if (newline && *line == '\n')
790		break;
791#endif
792	}
793
794	line += line_len;
795	cur_loc += line_len;
796    }
797
798    if (goal >= cur_pos)
799	/* In fact, the whole line displays shorter than goal. */
800	return cur_loc;
801
802    if (blank_loc == -1) {
803	/* No blank was found that was short enough. */
804	bool found_blank = FALSE;
805	ssize_t found_blank_loc = 0;
806
807	while (*line != '\0') {
808	    line_len = parse_mbchar(line, NULL, NULL);
809
810	    if (is_blank_mbchar(line)
811#ifndef DISABLE_HELP
812		|| (newline && *line == '\n')
813#endif
814		) {
815		if (!found_blank)
816		    found_blank = TRUE;
817		found_blank_loc = cur_loc;
818	    } else if (found_blank)
819		return found_blank_loc;
820
821	    line += line_len;
822	    cur_loc += line_len;
823	}
824
825	return -1;
826    }
827
828    /* Move to the last blank after blank_loc, if there is one. */
829    line -= cur_loc;
830    line += blank_loc;
831    line_len = parse_mbchar(line, NULL, NULL);
832    line += line_len;
833
834    while (*line != '\0' && (is_blank_mbchar(line)
835#ifndef DISABLE_HELP
836	|| (newline && *line == '\n')
837#endif
838	)) {
839#ifndef DISABLE_HELP
840	if (newline && *line == '\n')
841	    break;
842#endif
843
844	line_len = parse_mbchar(line, NULL, NULL);
845
846	line += line_len;
847	blank_loc += line_len;
848    }
849
850    return blank_loc;
851}
852#endif /* !DISABLE_HELP || !DISABLE_WRAPJUSTIFY */
853
854#if !defined(NANO_TINY) || !defined(DISABLE_JUSTIFY)
855/* The "indentation" of a line is the whitespace between the quote part
856 * and the non-whitespace of the line. */
857size_t indent_length(const char *line)
858{
859    size_t len = 0;
860    char *blank_mb;
861    int blank_mb_len;
862
863    assert(line != NULL);
864
865    blank_mb = charalloc(mb_cur_max());
866
867    while (*line != '\0') {
868	blank_mb_len = parse_mbchar(line, blank_mb, NULL);
869
870	if (!is_blank_mbchar(blank_mb))
871	    break;
872
873	line += blank_mb_len;
874	len += blank_mb_len;
875    }
876
877    free(blank_mb);
878
879    return len;
880}
881#endif /* !NANO_TINY || !DISABLE_JUSTIFY */
882
883#ifndef DISABLE_JUSTIFY
884/* justify_format() replaces blanks with spaces and multiple spaces by 1
885 * (except it maintains up to 2 after a character in punct optionally
886 * followed by a character in brackets, and removes all from the end).
887 *
888 * justify_format() might make paragraph->data shorter, and change the
889 * actual pointer with null_at().
890 *
891 * justify_format() will not look at the first skip characters of
892 * paragraph.  skip should be at most strlen(paragraph->data).  The
893 * character at paragraph[skip + 1] must not be blank. */
894void justify_format(filestruct *paragraph, size_t skip)
895{
896    char *end, *new_end, *new_paragraph_data;
897    size_t shift = 0;
898#ifndef NANO_TINY
899    size_t mark_shift = 0;
900#endif
901
902    /* These four asserts are assumptions about the input data. */
903    assert(paragraph != NULL);
904    assert(paragraph->data != NULL);
905    assert(skip < strlen(paragraph->data));
906    assert(!is_blank_mbchar(paragraph->data + skip));
907
908    end = paragraph->data + skip;
909    new_paragraph_data = charalloc(strlen(paragraph->data) + 1);
910    strncpy(new_paragraph_data, paragraph->data, skip);
911    new_end = new_paragraph_data + skip;
912
913    while (*end != '\0') {
914	int end_len;
915
916	/* If this character is blank, change it to a space if
917	 * necessary, and skip over all blanks after it. */
918	if (is_blank_mbchar(end)) {
919	    end_len = parse_mbchar(end, NULL, NULL);
920
921	    *new_end = ' ';
922	    new_end++;
923	    end += end_len;
924
925	    while (*end != '\0' && is_blank_mbchar(end)) {
926		end_len = parse_mbchar(end, NULL, NULL);
927
928		end += end_len;
929		shift += end_len;
930
931#ifndef NANO_TINY
932		/* Keep track of the change in the current line. */
933		if (openfile->mark_set && openfile->mark_begin ==
934			paragraph && openfile->mark_begin_x >= end -
935			paragraph->data)
936		    mark_shift += end_len;
937#endif
938	    }
939	/* If this character is punctuation optionally followed by a
940	 * bracket and then followed by blanks, change no more than two
941	 * of the blanks to spaces if necessary, and skip over all
942	 * blanks after them. */
943	} else if (mbstrchr(punct, end) != NULL) {
944	    end_len = parse_mbchar(end, NULL, NULL);
945
946	    while (end_len > 0) {
947		*new_end = *end;
948		new_end++;
949		end++;
950		end_len--;
951	    }
952
953	    if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
954		end_len = parse_mbchar(end, NULL, NULL);
955
956		while (end_len > 0) {
957		    *new_end = *end;
958		    new_end++;
959		    end++;
960		    end_len--;
961		}
962	    }
963
964	    if (*end != '\0' && is_blank_mbchar(end)) {
965		end_len = parse_mbchar(end, NULL, NULL);
966
967		*new_end = ' ';
968		new_end++;
969		end += end_len;
970	    }
971
972	    if (*end != '\0' && is_blank_mbchar(end)) {
973		end_len = parse_mbchar(end, NULL, NULL);
974
975		*new_end = ' ';
976		new_end++;
977		end += end_len;
978	    }
979
980	    while (*end != '\0' && is_blank_mbchar(end)) {
981		end_len = parse_mbchar(end, NULL, NULL);
982
983		end += end_len;
984		shift += end_len;
985
986#ifndef NANO_TINY
987		/* Keep track of the change in the current line. */
988		if (openfile->mark_set && openfile->mark_begin ==
989			paragraph && openfile->mark_begin_x >= end -
990			paragraph->data)
991		    mark_shift += end_len;
992#endif
993	    }
994	/* If this character is neither blank nor punctuation, leave it
995	 * unchanged. */
996	} else {
997	    end_len = parse_mbchar(end, NULL, NULL);
998
999	    while (end_len > 0) {
1000		*new_end = *end;
1001		new_end++;
1002		end++;
1003		end_len--;
1004	    }
1005	}
1006    }
1007
1008    assert(*end == '\0');
1009
1010    *new_end = *end;
1011
1012    /* If there are spaces at the end of the line, remove them. */
1013    while (new_end > new_paragraph_data + skip &&
1014	*(new_end - 1) == ' ') {
1015	new_end--;
1016	shift++;
1017    }
1018
1019    if (shift > 0) {
1020	openfile->totsize -= shift;
1021	null_at(&new_paragraph_data, new_end - new_paragraph_data);
1022	free(paragraph->data);
1023	paragraph->data = new_paragraph_data;
1024
1025#ifndef NANO_TINY
1026	/* Adjust the mark coordinates to compensate for the change in
1027	 * the current line. */
1028	if (openfile->mark_set && openfile->mark_begin == paragraph) {
1029	    openfile->mark_begin_x -= mark_shift;
1030	    if (openfile->mark_begin_x > new_end - new_paragraph_data)
1031		openfile->mark_begin_x = new_end - new_paragraph_data;
1032	}
1033#endif
1034    } else
1035	free(new_paragraph_data);
1036}
1037
1038/* The "quote part" of a line is the largest initial substring matching
1039 * the quote string.  This function returns the length of the quote part
1040 * of the given line.
1041 *
1042 * Note that if !HAVE_REGEX_H then we match concatenated copies of
1043 * quotestr. */
1044size_t quote_length(const char *line)
1045{
1046#ifdef HAVE_REGEX_H
1047    regmatch_t matches;
1048    int rc = regexec(&quotereg, line, 1, &matches, 0);
1049
1050    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
1051	return 0;
1052    /* matches.rm_so should be 0, since the quote string should start
1053     * with the caret ^. */
1054    return matches.rm_eo;
1055#else	/* !HAVE_REGEX_H */
1056    size_t qdepth = 0;
1057
1058    /* Compute quote depth level. */
1059    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
1060	qdepth += quotelen;
1061    return qdepth;
1062#endif	/* !HAVE_REGEX_H */
1063}
1064
1065/* a_line and b_line are lines of text.  The quotation part of a_line is
1066 * the first a_quote characters.  Check that the quotation part of
1067 * b_line is the same. */
1068bool quotes_match(const char *a_line, size_t a_quote, const char
1069	*b_line)
1070{
1071    /* Here is the assumption about a_quote. */
1072    assert(a_quote == quote_length(a_line));
1073
1074    return (a_quote == quote_length(b_line) &&
1075	strncmp(a_line, b_line, a_quote) == 0);
1076}
1077
1078/* We assume a_line and b_line have no quote part.  Then, we return
1079 * whether b_line could follow a_line in a paragraph. */
1080bool indents_match(const char *a_line, size_t a_indent, const char
1081	*b_line, size_t b_indent)
1082{
1083    assert(a_indent == indent_length(a_line));
1084    assert(b_indent == indent_length(b_line));
1085
1086    return (b_indent <= a_indent &&
1087	strncmp(a_line, b_line, b_indent) == 0);
1088}
1089
1090/* Is foo the beginning of a paragraph?
1091 *
1092 *   A line of text consists of a "quote part", followed by an
1093 *   "indentation part", followed by text.  The functions quote_length()
1094 *   and indent_length() calculate these parts.
1095 *
1096 *   A line is "part of a paragraph" if it has a part not in the quote
1097 *   part or the indentation.
1098 *
1099 *   A line is "the beginning of a paragraph" if it is part of a
1100 *   paragraph and
1101 *	1) it is the top line of the file, or
1102 *	2) the line above it is not part of a paragraph, or
1103 *	3) the line above it does not have precisely the same quote
1104 *	   part, or
1105 *	4) the indentation of this line is not an initial substring of
1106 *	   the indentation of the previous line, or
1107 *	5) this line has no quote part and some indentation, and
1108 *	   autoindent isn't turned on.
1109 *   The reason for number 5) is that if autoindent isn't turned on,
1110 *   then an indented line is expected to start a paragraph, as in
1111 *   books.  Thus, nano can justify an indented paragraph only if
1112 *   autoindent is turned on. */
1113bool begpar(const filestruct *const foo)
1114{
1115    size_t quote_len, indent_len, temp_id_len;
1116
1117    if (foo == NULL)
1118	return FALSE;
1119
1120    /* Case 1). */
1121    if (foo == openfile->fileage)
1122	return TRUE;
1123
1124    quote_len = quote_length(foo->data);
1125    indent_len = indent_length(foo->data + quote_len);
1126
1127    /* Not part of a paragraph. */
1128    if (foo->data[quote_len + indent_len] == '\0')
1129	return FALSE;
1130
1131    /* Case 3). */
1132    if (!quotes_match(foo->data, quote_len, foo->prev->data))
1133	return TRUE;
1134
1135    temp_id_len = indent_length(foo->prev->data + quote_len);
1136
1137    /* Case 2) or 5) or 4). */
1138    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
1139	(quote_len == 0 && indent_len > 0
1140#ifndef NANO_TINY
1141	&& !ISSET(AUTOINDENT)
1142#endif
1143	) || !indents_match(foo->prev->data + quote_len, temp_id_len,
1144	foo->data + quote_len, indent_len))
1145	return TRUE;
1146
1147    return FALSE;
1148}
1149
1150/* Is foo inside a paragraph? */
1151bool inpar(const filestruct *const foo)
1152{
1153    size_t quote_len;
1154
1155    if (foo == NULL)
1156	return FALSE;
1157
1158    quote_len = quote_length(foo->data);
1159
1160    return (foo->data[quote_len + indent_length(foo->data +
1161	quote_len)] != '\0');
1162}
1163
1164/* Move the next par_len lines, starting with first_line, into the
1165 * justify buffer, leaving copies of those lines in place.  Assume that
1166 * par_len is greater than zero, and that there are enough lines after
1167 * first_line. */
1168void backup_lines(filestruct *first_line, size_t par_len)
1169{
1170    filestruct *top = first_line;
1171	/* The top of the paragraph we're backing up. */
1172    filestruct *bot = first_line;
1173	/* The bottom of the paragraph we're backing up. */
1174    size_t i;
1175	/* Generic loop variable. */
1176    size_t current_x_save = openfile->current_x;
1177    ssize_t fl_lineno_save = first_line->lineno;
1178    ssize_t edittop_lineno_save = openfile->edittop->lineno;
1179    ssize_t current_lineno_save = openfile->current->lineno;
1180#ifndef NANO_TINY
1181    bool old_mark_set = openfile->mark_set;
1182    ssize_t mb_lineno_save = 0;
1183    size_t mark_begin_x_save = 0;
1184
1185    if (old_mark_set) {
1186	mb_lineno_save = openfile->mark_begin->lineno;
1187	mark_begin_x_save = openfile->mark_begin_x;
1188    }
1189#endif
1190
1191    /* par_len will be one greater than the number of lines between
1192     * current and filebot if filebot is the last line in the
1193     * paragraph. */
1194    assert(par_len > 0 && openfile->current->lineno + par_len <=
1195	openfile->filebot->lineno + 1);
1196
1197    /* Move bot down par_len lines to the line after the last line of
1198     * the paragraph, if there is one. */
1199    for (i = par_len; i > 0 && bot != openfile->filebot; i--)
1200	bot = bot->next;
1201
1202    /* Move the paragraph from the current buffer's filestruct to the
1203     * justify buffer. */
1204    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot,
1205	(i == 1 && bot == openfile->filebot) ? strlen(bot->data) : 0);
1206
1207    /* Copy the paragraph back to the current buffer's filestruct from
1208     * the justify buffer. */
1209    copy_from_filestruct(jusbuffer, jusbottom);
1210
1211    /* Move upward from the last line of the paragraph to the first
1212     * line, putting first_line, edittop, current, and mark_begin at the
1213     * same lines in the copied paragraph that they had in the original
1214     * paragraph. */
1215    if (openfile->current != openfile->fileage) {
1216	top = openfile->current->prev;
1217#ifndef NANO_TINY
1218	if (old_mark_set &&
1219		openfile->current->lineno == mb_lineno_save) {
1220	    openfile->mark_begin = openfile->current;
1221	    openfile->mark_begin_x = mark_begin_x_save;
1222	}
1223#endif
1224    } else
1225	top = openfile->current;
1226    for (i = par_len; i > 0 && top != NULL; i--) {
1227	if (top->lineno == fl_lineno_save)
1228	    first_line = top;
1229	if (top->lineno == edittop_lineno_save)
1230	    openfile->edittop = top;
1231	if (top->lineno == current_lineno_save)
1232	    openfile->current = top;
1233#ifndef NANO_TINY
1234	if (old_mark_set && top->lineno == mb_lineno_save) {
1235	    openfile->mark_begin = top;
1236	    openfile->mark_begin_x = mark_begin_x_save;
1237	}
1238#endif
1239	top = top->prev;
1240    }
1241
1242    /* Put current_x at the same place in the copied paragraph that it
1243     * had in the original paragraph. */
1244    openfile->current_x = current_x_save;
1245
1246    set_modified();
1247}
1248
1249/* Find the beginning of the current paragraph if we're in one, or the
1250 * beginning of the next paragraph if we're not.  Afterwards, save the
1251 * quote length and paragraph length in *quote and *par.  Return TRUE if
1252 * we found a paragraph, and FALSE if there was an error or we didn't
1253 * find a paragraph.
1254 *
1255 * See the comment at begpar() for more about when a line is the
1256 * beginning of a paragraph. */
1257bool find_paragraph(size_t *const quote, size_t *const par)
1258{
1259    size_t quote_len;
1260	/* Length of the initial quotation of the paragraph we search
1261	 * for. */
1262    size_t par_len;
1263	/* Number of lines in the paragraph we search for. */
1264    filestruct *current_save;
1265	/* The line at the beginning of the paragraph we search for. */
1266    ssize_t current_y_save;
1267	/* The y-coordinate at the beginning of the paragraph we search
1268	 * for. */
1269
1270#ifdef HAVE_REGEX_H
1271    if (quoterc != 0) {
1272	statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
1273	return FALSE;
1274    }
1275#endif
1276
1277    assert(openfile->current != NULL);
1278
1279    /* If we're at the end of the last line of the file, it means that
1280     * there aren't any paragraphs left, so get out. */
1281    if (openfile->current == openfile->filebot && openfile->current_x ==
1282	strlen(openfile->filebot->data))
1283	return FALSE;
1284
1285    /* If the current line isn't in a paragraph, move forward to the
1286     * last line of the next paragraph, if any. */
1287    if (!inpar(openfile->current)) {
1288	do_para_end(FALSE);
1289
1290	/* If we end up past the beginning of the line, it means that
1291	 * we're at the end of the last line of the file, and the line
1292	 * isn't blank, in which case the last line of the file is the
1293	 * last line of the next paragraph.
1294	 *
1295	 * Otherwise, if we end up on a line that's in a paragraph, it
1296	 * means that we're on the line after the last line of the next
1297	 * paragraph, in which case we should move back to the last line
1298	 * of the next paragraph. */
1299	if (openfile->current_x == 0) {
1300	    if (!inpar(openfile->current->prev))
1301		return FALSE;
1302	    if (openfile->current != openfile->fileage)
1303		openfile->current = openfile->current->prev;
1304	}
1305    }
1306
1307    /* If the current line isn't the first line of the paragraph, move
1308     * back to the first line of the paragraph. */
1309    if (!begpar(openfile->current))
1310	do_para_begin(FALSE);
1311
1312    /* Now current is the first line of the paragraph.  Set quote_len to
1313     * the quotation length of that line, and set par_len to the number
1314     * of lines in this paragraph. */
1315    quote_len = quote_length(openfile->current->data);
1316    current_save = openfile->current;
1317    current_y_save = openfile->current_y;
1318    do_para_end(FALSE);
1319    par_len = openfile->current->lineno - current_save->lineno;
1320
1321    /* If we end up past the beginning of the line, it means that we're
1322     * at the end of the last line of the file, and the line isn't
1323     * blank, in which case the last line of the file is part of the
1324     * paragraph. */
1325    if (openfile->current_x > 0)
1326	par_len++;
1327    openfile->current = current_save;
1328    openfile->current_y = current_y_save;
1329
1330    /* Save the values of quote_len and par_len. */
1331    assert(quote != NULL && par != NULL);
1332
1333    *quote = quote_len;
1334    *par = par_len;
1335
1336    return TRUE;
1337}
1338
1339/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
1340 * the current paragraph. */
1341void do_justify(bool full_justify)
1342{
1343    filestruct *first_par_line = NULL;
1344	/* Will be the first line of the justified paragraph(s), if any.
1345	 * For restoring after unjustify. */
1346    filestruct *last_par_line = NULL;
1347	/* Will be the line after the last line of the justified
1348	 * paragraph(s), if any.  Also for restoring after unjustify. */
1349    bool filebot_inpar = FALSE;
1350	/* Whether the text at filebot is part of the current
1351	 * paragraph. */
1352
1353    /* We save these variables to be restored if the user
1354     * unjustifies. */
1355    filestruct *edittop_save = openfile->edittop;
1356    filestruct *current_save = openfile->current;
1357    size_t current_x_save = openfile->current_x;
1358    size_t pww_save = openfile->placewewant;
1359    size_t totsize_save = openfile->totsize;
1360#ifndef NANO_TINY
1361    filestruct *mark_begin_save = openfile->mark_begin;
1362    size_t mark_begin_x_save = openfile->mark_begin_x;
1363#endif
1364    bool modified_save = openfile->modified;
1365
1366    int kbinput;
1367    bool meta_key, func_key, s_or_t, ran_func, finished;
1368
1369    /* Move to the beginning of the current line, so that justifying at
1370     * the end of the last line of the file, if that line isn't blank,
1371     * will work the first time through. */
1372    openfile->current_x = 0;
1373
1374    /* If we're justifying the entire file, start at the beginning. */
1375    if (full_justify)
1376	openfile->current = openfile->fileage;
1377
1378    while (TRUE) {
1379	size_t i;
1380	    /* Generic loop variable. */
1381	filestruct *curr_first_par_line;
1382	    /* The first line of the current paragraph. */
1383	size_t quote_len;
1384	    /* Length of the initial quotation of the current
1385	     * paragraph. */
1386	size_t indent_len;
1387	    /* Length of the initial indentation of the current
1388	     * paragraph. */
1389	size_t par_len;
1390	    /* Number of lines in the current paragraph. */
1391	ssize_t break_pos;
1392	    /* Where we will break lines. */
1393	char *indent_string;
1394	    /* The first indentation that doesn't match the initial
1395	     * indentation of the current paragraph.  This is put at the
1396	     * beginning of every line broken off the first justified
1397	     * line of the paragraph.  Note that this works because a
1398	     * paragraph can only contain two indentations at most: the
1399	     * initial one, and a different one starting on a line after
1400	     * the first.  See the comment at begpar() for more about
1401	     * when a line is part of a paragraph. */
1402
1403	/* Find the first line of the paragraph to be justified.  That
1404	 * is the start of this paragraph if we're in one, or the start
1405	 * of the next otherwise.  Save the quote length and paragraph
1406	 * length (number of lines).  Don't refresh the screen yet,
1407	 * since we'll do that after we justify.
1408	 *
1409	 * If the search failed, we do one of two things.  If we're
1410	 * justifying the whole file, and we've found at least one
1411	 * paragraph, it means that we should justify all the way to the
1412	 * last line of the file, so set the last line of the text to be
1413	 * justified to the last line of the file and break out of the
1414	 * loop.  Otherwise, it means that there are no paragraph(s) to
1415	 * justify, so refresh the screen and get out. */
1416	if (!find_paragraph(&quote_len, &par_len)) {
1417	    if (full_justify && first_par_line != NULL) {
1418		last_par_line = openfile->filebot;
1419		break;
1420	    } else {
1421		edit_refresh();
1422		return;
1423	    }
1424	}
1425
1426	/* par_len will be one greater than the number of lines between
1427	 * current and filebot if filebot is the last line in the
1428	 * paragraph.  Set filebot_inpar to TRUE if this is the case. */
1429	filebot_inpar = (openfile->current->lineno + par_len ==
1430		openfile->filebot->lineno + 1);
1431
1432	/* If we haven't already done it, move the original paragraph(s)
1433	 * to the justify buffer, splice a copy of the original
1434	 * paragraph(s) into the file in the same place, and set
1435	 * first_par_line to the first line of the copy. */
1436	if (first_par_line == NULL) {
1437	    backup_lines(openfile->current, full_justify ?
1438		openfile->filebot->lineno - openfile->current->lineno +
1439		((openfile->filebot->data[0] != '\0') ? 1 : 0) :
1440		par_len);
1441	    first_par_line = openfile->current;
1442	}
1443
1444	/* Set curr_first_par_line to the first line of the current
1445	 * paragraph. */
1446	curr_first_par_line = openfile->current;
1447
1448	/* Initialize indent_string to a blank string. */
1449	indent_string = mallocstrcpy(NULL, "");
1450
1451	/* Find the first indentation in the paragraph that doesn't
1452	 * match the indentation of the first line, and save it in
1453	 * indent_string.  If all the indentations are the same, save
1454	 * the indentation of the first line in indent_string. */
1455	{
1456	    const filestruct *indent_line = openfile->current;
1457	    bool past_first_line = FALSE;
1458
1459	    for (i = 0; i < par_len; i++) {
1460		indent_len = quote_len +
1461			indent_length(indent_line->data + quote_len);
1462
1463		if (indent_len != strlen(indent_string)) {
1464		    indent_string = mallocstrncpy(indent_string,
1465			indent_line->data, indent_len + 1);
1466		    indent_string[indent_len] = '\0';
1467
1468		    if (past_first_line)
1469			break;
1470		}
1471
1472		if (indent_line == openfile->current)
1473		    past_first_line = TRUE;
1474
1475		indent_line = indent_line->next;
1476	    }
1477	}
1478
1479	/* Now tack all the lines of the paragraph together, skipping
1480	 * the quoting and indentation on all lines after the first. */
1481	for (i = 0; i < par_len - 1; i++) {
1482	    filestruct *next_line = openfile->current->next;
1483	    size_t line_len = strlen(openfile->current->data);
1484	    size_t next_line_len =
1485		strlen(openfile->current->next->data);
1486
1487	    indent_len = quote_len +
1488		indent_length(openfile->current->next->data +
1489		quote_len);
1490
1491	    next_line_len -= indent_len;
1492	    openfile->totsize -= indent_len;
1493
1494	    /* We're just about to tack the next line onto this one.  If
1495	     * this line isn't empty, make sure it ends in a space. */
1496	    if (line_len > 0 &&
1497		openfile->current->data[line_len - 1] != ' ') {
1498		line_len++;
1499		openfile->current->data =
1500			charealloc(openfile->current->data,
1501			line_len + 1);
1502		openfile->current->data[line_len - 1] = ' ';
1503		openfile->current->data[line_len] = '\0';
1504		openfile->totsize++;
1505	    }
1506
1507	    openfile->current->data =
1508		charealloc(openfile->current->data, line_len +
1509		next_line_len + 1);
1510	    strcat(openfile->current->data, next_line->data +
1511		indent_len);
1512
1513	    /* Don't destroy edittop or filebot! */
1514	    if (next_line == openfile->edittop)
1515		openfile->edittop = openfile->current;
1516	    if (next_line == openfile->filebot)
1517		openfile->filebot = openfile->current;
1518
1519#ifndef NANO_TINY
1520	    /* Adjust the mark coordinates to compensate for the change
1521	     * in the next line. */
1522	    if (openfile->mark_set && openfile->mark_begin ==
1523		next_line) {
1524		openfile->mark_begin = openfile->current;
1525		openfile->mark_begin_x += line_len - indent_len;
1526	    }
1527#endif
1528
1529	    unlink_node(next_line);
1530	    delete_node(next_line);
1531
1532	    /* If we've removed the next line, we need to go through
1533	     * this line again. */
1534	    i--;
1535
1536	    par_len--;
1537	    openfile->totsize--;
1538	}
1539
1540	/* Call justify_format() on the paragraph, which will remove
1541	 * excess spaces from it and change all blank characters to
1542	 * spaces. */
1543	justify_format(openfile->current, quote_len +
1544		indent_length(openfile->current->data + quote_len));
1545
1546	while (par_len > 0 && strlenpt(openfile->current->data) >
1547		fill) {
1548	    size_t line_len = strlen(openfile->current->data);
1549
1550	    indent_len = strlen(indent_string);
1551
1552	    /* If this line is too long, try to wrap it to the next line
1553	     * to make it short enough. */
1554	    break_pos = break_line(openfile->current->data + indent_len,
1555		fill - strnlenpt(openfile->current->data, indent_len)
1556#ifndef DISABLE_HELP
1557		, FALSE
1558#endif
1559		);
1560
1561	    /* We can't break the line, or don't need to, so get out. */
1562	    if (break_pos == -1 || break_pos + indent_len == line_len)
1563		break;
1564
1565	    /* Move forward to the character after the indentation and
1566	     * just after the space. */
1567	    break_pos += indent_len + 1;
1568
1569	    assert(break_pos <= line_len);
1570
1571	    /* Make a new line, and copy the text after where we're
1572	     * going to break this line to the beginning of the new
1573	     * line. */
1574	    splice_node(openfile->current,
1575		make_new_node(openfile->current),
1576		openfile->current->next);
1577
1578	    /* If this paragraph is non-quoted, and autoindent isn't
1579	     * turned on, set the indentation length to zero so that the
1580	     * indentation is treated as part of the line. */
1581	    if (quote_len == 0
1582#ifndef NANO_TINY
1583		&& !ISSET(AUTOINDENT)
1584#endif
1585		)
1586		indent_len = 0;
1587
1588	    /* Copy the text after where we're going to break the
1589	     * current line to the next line. */
1590	    openfile->current->next->data = charalloc(indent_len + 1 +
1591		line_len - break_pos);
1592	    strncpy(openfile->current->next->data, indent_string,
1593		indent_len);
1594	    strcpy(openfile->current->next->data + indent_len,
1595		openfile->current->data + break_pos);
1596
1597	    par_len++;
1598	    openfile->totsize += indent_len + 1;
1599
1600#ifndef NANO_TINY
1601	    /* Adjust the mark coordinates to compensate for the change
1602	     * in the current line. */
1603	    if (openfile->mark_set && openfile->mark_begin ==
1604		openfile->current && openfile->mark_begin_x >
1605		break_pos) {
1606		openfile->mark_begin = openfile->current->next;
1607		openfile->mark_begin_x -= break_pos - indent_len;
1608	    }
1609#endif
1610
1611	    /* Break the current line. */
1612	    null_at(&openfile->current->data, break_pos);
1613
1614	    /* If the current line is the last line of the file, move
1615	     * the last line of the file down to the next line. */
1616	    if (openfile->filebot == openfile->current)
1617		openfile->filebot = openfile->filebot->next;
1618
1619	    /* Go to the next line. */
1620	    par_len--;
1621	    openfile->current_y++;
1622	    openfile->current = openfile->current->next;
1623	}
1624
1625	/* We're done breaking lines, so we don't need indent_string
1626	 * anymore. */
1627	free(indent_string);
1628
1629	/* Go to the next line, if possible.  If there is no next line,
1630	 * move to the end of the current line. */
1631	if (openfile->current != openfile->filebot) {
1632	    openfile->current_y++;
1633	    openfile->current = openfile->current->next;
1634	} else
1635	    openfile->current_x = strlen(openfile->current->data);
1636
1637	/* Renumber the lines of the now-justified current paragraph,
1638	 * since both find_paragraph() and edit_refresh() need the line
1639	 * numbers to be right. */
1640	renumber(curr_first_par_line);
1641
1642	/* We've just finished justifying the paragraph.  If we're not
1643	 * justifying the entire file, break out of the loop.
1644	 * Otherwise, continue the loop so that we justify all the
1645	 * paragraphs in the file. */
1646	if (!full_justify)
1647	    break;
1648    }
1649
1650    /* We are now done justifying the paragraph or the file, so clean
1651     * up.  current_y and totsize have been maintained above.  If we
1652     * actually justified something, set last_par_line to the new end of
1653     * the paragraph. */
1654    if (first_par_line != NULL)
1655	last_par_line = openfile->current;
1656
1657    edit_refresh();
1658
1659#ifndef NANO_TINY
1660    /* We're going to set jump_buf so that we return here after a
1661     * SIGWINCH instead of to main().  Indicate this. */
1662    jump_buf_main = FALSE;
1663
1664    /* Return here after a SIGWINCH. */
1665    sigsetjmp(jump_buf, 1);
1666#endif
1667
1668    statusbar(_("Can now UnJustify!"));
1669
1670    /* If constant cursor position display is on, make sure the current
1671     * cursor position will be properly displayed on the statusbar. */
1672    if (ISSET(CONST_UPDATE))
1673	do_cursorpos(TRUE);
1674
1675    /* Display the shortcut list with UnJustify. */
1676    shortcut_init(TRUE);
1677    display_main_list();
1678
1679    /* Now get a keystroke and see if it's unjustify.  If not, put back
1680     * the keystroke and return. */
1681    kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func,
1682	&finished, FALSE);
1683
1684    if (s_or_t && kbinput == NANO_UNJUSTIFY_KEY) {
1685	/* Splice the justify buffer back into the file, but only if we
1686	 * actually justified something. */
1687	if (first_par_line != NULL) {
1688	    filestruct *top_save;
1689
1690	    /* Partition the filestruct so that it contains only the
1691	     * text of the justified paragraph. */
1692	    filepart = partition_filestruct(first_par_line, 0,
1693		last_par_line, filebot_inpar ?
1694		strlen(last_par_line->data) : 0);
1695
1696	    /* Remove the text of the justified paragraph, and
1697	     * replace it with the text in the justify buffer. */
1698	    free_filestruct(openfile->fileage);
1699	    openfile->fileage = jusbuffer;
1700	    openfile->filebot = jusbottom;
1701
1702	    top_save = openfile->fileage;
1703
1704	    /* Unpartition the filestruct so that it contains all the
1705	     * text again.  Note that the justified paragraph has been
1706	     * replaced with the unjustified paragraph. */
1707	    unpartition_filestruct(&filepart);
1708
1709	     /* Renumber starting with the beginning line of the old
1710	      * partition. */
1711	    renumber(top_save);
1712
1713	    /* Restore the justify we just did (ungrateful user!). */
1714	    openfile->edittop = edittop_save;
1715	    openfile->current = current_save;
1716	    openfile->current_x = current_x_save;
1717	    openfile->placewewant = pww_save;
1718	    openfile->totsize = totsize_save;
1719#ifndef NANO_TINY
1720	    if (openfile->mark_set) {
1721		openfile->mark_begin = mark_begin_save;
1722		openfile->mark_begin_x = mark_begin_x_save;
1723	    }
1724#endif
1725	    openfile->modified = modified_save;
1726
1727	    /* Clear the justify buffer. */
1728	    jusbuffer = NULL;
1729
1730	    if (!openfile->modified)
1731		titlebar(NULL);
1732	    edit_refresh();
1733	}
1734    } else {
1735	unget_kbinput(kbinput, meta_key, func_key);
1736
1737	/* Blow away the text in the justify buffer. */
1738	free_filestruct(jusbuffer);
1739	jusbuffer = NULL;
1740    }
1741
1742    blank_statusbar();
1743
1744    /* Display the shortcut list with UnCut. */
1745    shortcut_init(FALSE);
1746    display_main_list();
1747}
1748
1749/* Justify the current paragraph. */
1750void do_justify_void(void)
1751{
1752    do_justify(FALSE);
1753}
1754
1755/* Justify the entire file. */
1756void do_full_justify(void)
1757{
1758    do_justify(TRUE);
1759}
1760#endif /* !DISABLE_JUSTIFY */
1761
1762#ifndef DISABLE_SPELLER
1763/* A word is misspelled in the file.  Let the user replace it.  We
1764 * return FALSE if the user cancels. */
1765bool do_int_spell_fix(const char *word)
1766{
1767    char *save_search, *save_replace;
1768    size_t match_len, current_x_save = openfile->current_x;
1769    size_t pww_save = openfile->placewewant;
1770    filestruct *edittop_save = openfile->edittop;
1771    filestruct *current_save = openfile->current;
1772	/* Save where we are. */
1773    bool canceled = FALSE;
1774	/* The return value. */
1775    bool case_sens_set = ISSET(CASE_SENSITIVE);
1776#ifndef NANO_TINY
1777    bool backwards_search_set = ISSET(BACKWARDS_SEARCH);
1778#endif
1779#ifdef HAVE_REGEX_H
1780    bool regexp_set = ISSET(USE_REGEXP);
1781#endif
1782#ifndef NANO_TINY
1783    bool old_mark_set = openfile->mark_set;
1784    bool added_magicline = FALSE;
1785	/* Whether we added a magicline after filebot. */
1786    bool right_side_up = FALSE;
1787	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
1788	 * FALSE if (current, current_x) is. */
1789    filestruct *top, *bot;
1790    size_t top_x, bot_x;
1791#endif
1792
1793    /* Make sure spell-check is case sensitive. */
1794    SET(CASE_SENSITIVE);
1795
1796#ifndef NANO_TINY
1797    /* Make sure spell-check goes forward only. */
1798    UNSET(BACKWARDS_SEARCH);
1799#endif
1800#ifdef HAVE_REGEX_H
1801    /* Make sure spell-check doesn't use regular expressions. */
1802    UNSET(USE_REGEXP);
1803#endif
1804
1805    /* Save the current search/replace strings. */
1806    search_init_globals();
1807    save_search = last_search;
1808    save_replace = last_replace;
1809
1810    /* Set the search/replace strings to the misspelled word. */
1811    last_search = mallocstrcpy(NULL, word);
1812    last_replace = mallocstrcpy(NULL, word);
1813
1814#ifndef NANO_TINY
1815    if (old_mark_set) {
1816	/* If the mark is on, partition the filestruct so that it
1817	 * contains only the marked text; if the NO_NEWLINES flag isn't
1818	 * set, keep track of whether the text will have a magicline
1819	 * added when we're done correcting misspelled words; and
1820	 * turn the mark off. */
1821	mark_order((const filestruct **)&top, &top_x,
1822	    (const filestruct **)&bot, &bot_x, &right_side_up);
1823	filepart = partition_filestruct(top, top_x, bot, bot_x);
1824	if (!ISSET(NO_NEWLINES))
1825	    added_magicline = (openfile->filebot->data[0] != '\0');
1826	openfile->mark_set = FALSE;
1827    }
1828#endif
1829
1830    /* Start from the top of the file. */
1831    openfile->edittop = openfile->fileage;
1832    openfile->current = openfile->fileage;
1833    openfile->current_x = (size_t)-1;
1834    openfile->placewewant = 0;
1835
1836    /* Find the first whole occurrence of word. */
1837    findnextstr_wrap_reset();
1838    while (findnextstr(TRUE, FALSE, openfile->fileage, 0, word,
1839	&match_len)) {
1840	if (is_whole_word(openfile->current_x, openfile->current->data,
1841		word)) {
1842	    size_t xpt = xplustabs();
1843	    char *exp_word = display_string(openfile->current->data,
1844		xpt, strnlenpt(openfile->current->data,
1845		openfile->current_x + match_len) - xpt, FALSE);
1846
1847	    edit_refresh();
1848
1849	    do_replace_highlight(TRUE, exp_word);
1850
1851	    /* Allow all instances of the word to be corrected. */
1852	    canceled = (do_prompt(FALSE,
1853#ifndef DISABLE_TABCOMP
1854		TRUE,
1855#endif
1856		spell_list, word,
1857#ifndef NANO_TINY
1858		NULL,
1859#endif
1860		edit_refresh, _("Edit a replacement")) == -1);
1861
1862	    do_replace_highlight(FALSE, exp_word);
1863
1864	    free(exp_word);
1865
1866	    if (!canceled && strcmp(word, answer) != 0) {
1867		openfile->current_x--;
1868		do_replace_loop(TRUE, &canceled, openfile->current,
1869			&openfile->current_x, word);
1870	    }
1871
1872	    break;
1873	}
1874    }
1875
1876#ifndef NANO_TINY
1877    if (old_mark_set) {
1878	/* If the mark was on, the NO_NEWLINES flag isn't set, and we
1879	 * added a magicline, remove it now. */
1880	if (!ISSET(NO_NEWLINES) && added_magicline)
1881	    remove_magicline();
1882
1883	/* Put the beginning and the end of the mark at the beginning
1884	 * and the end of the spell-checked text. */
1885	if (openfile->fileage == openfile->filebot)
1886	    bot_x += top_x;
1887	if (right_side_up) {
1888	    openfile->mark_begin_x = top_x;
1889	    current_x_save = bot_x;
1890	} else {
1891	    current_x_save = top_x;
1892	    openfile->mark_begin_x = bot_x;
1893	}
1894
1895	/* Unpartition the filestruct so that it contains all the text
1896	 * again, and turn the mark back on. */
1897	unpartition_filestruct(&filepart);
1898	openfile->mark_set = TRUE;
1899    }
1900#endif
1901
1902    /* Restore the search/replace strings. */
1903    free(last_search);
1904    last_search = save_search;
1905    free(last_replace);
1906    last_replace = save_replace;
1907
1908    /* Restore where we were. */
1909    openfile->edittop = edittop_save;
1910    openfile->current = current_save;
1911    openfile->current_x = current_x_save;
1912    openfile->placewewant = pww_save;
1913
1914    /* Restore case sensitivity setting. */
1915    if (!case_sens_set)
1916	UNSET(CASE_SENSITIVE);
1917
1918#ifndef NANO_TINY
1919    /* Restore search/replace direction. */
1920    if (backwards_search_set)
1921	SET(BACKWARDS_SEARCH);
1922#endif
1923#ifdef HAVE_REGEX_H
1924    /* Restore regular expression usage setting. */
1925    if (regexp_set)
1926	SET(USE_REGEXP);
1927#endif
1928
1929    return !canceled;
1930}
1931
1932/* Internal (integrated) spell checking using the spell program,
1933 * filtered through the sort and uniq programs.  Return NULL for normal
1934 * termination, and the error string otherwise. */
1935const char *do_int_speller(const char *tempfile_name)
1936{
1937    char *read_buff, *read_buff_ptr, *read_buff_word;
1938    size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
1939    int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
1940    pid_t pid_spell, pid_sort, pid_uniq;
1941    int spell_status, sort_status, uniq_status;
1942
1943    /* Create all three pipes up front. */
1944    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
1945	pipe(uniq_fd) == -1)
1946	return _("Could not create pipe");
1947
1948    statusbar(_("Creating misspelled word list, please wait..."));
1949
1950    /* A new process to run spell in. */
1951    if ((pid_spell = fork()) == 0) {
1952	/* Child continues (i.e. future spell process). */
1953	close(spell_fd[0]);
1954
1955	/* Replace the standard input with the temp file. */
1956	if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
1957	    goto close_pipes_and_exit;
1958
1959	if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
1960	    goto close_pipes_and_exit;
1961
1962	close(tempfile_fd);
1963
1964	/* Send spell's standard output to the pipe. */
1965	if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
1966	    goto close_pipes_and_exit;
1967
1968	close(spell_fd[1]);
1969
1970	/* Start the spell program; we are using $PATH. */
1971	execlp("spell", "spell", NULL);
1972
1973	/* This should not be reached if spell is found. */
1974	exit(1);
1975    }
1976
1977    /* Parent continues here. */
1978    close(spell_fd[1]);
1979
1980    /* A new process to run sort in. */
1981    if ((pid_sort = fork()) == 0) {
1982	/* Child continues (i.e. future spell process).  Replace the
1983	 * standard input with the standard output of the old pipe. */
1984	if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
1985	    goto close_pipes_and_exit;
1986
1987	close(spell_fd[0]);
1988
1989	/* Send sort's standard output to the new pipe. */
1990	if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
1991	    goto close_pipes_and_exit;
1992
1993	close(sort_fd[1]);
1994
1995	/* Start the sort program.  Use -f to remove mixed case.  If
1996	 * this isn't portable, let me know. */
1997	execlp("sort", "sort", "-f", NULL);
1998
1999	/* This should not be reached if sort is found. */
2000	exit(1);
2001    }
2002
2003    close(spell_fd[0]);
2004    close(sort_fd[1]);
2005
2006    /* A new process to run uniq in. */
2007    if ((pid_uniq = fork()) == 0) {
2008	/* Child continues (i.e. future uniq process).  Replace the
2009	 * standard input with the standard output of the old pipe. */
2010	if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
2011	    goto close_pipes_and_exit;
2012
2013	close(sort_fd[0]);
2014
2015	/* Send uniq's standard output to the new pipe. */
2016	if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
2017	    goto close_pipes_and_exit;
2018
2019	close(uniq_fd[1]);
2020
2021	/* Start the uniq program; we are using PATH. */
2022	execlp("uniq", "uniq", NULL);
2023
2024	/* This should not be reached if uniq is found. */
2025	exit(1);
2026    }
2027
2028    close(sort_fd[0]);
2029    close(uniq_fd[1]);
2030
2031    /* The child process was not forked successfully. */
2032    if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
2033	close(uniq_fd[0]);
2034	return _("Could not fork");
2035    }
2036
2037    /* Get the system pipe buffer size. */
2038    if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
2039	close(uniq_fd[0]);
2040	return _("Could not get size of pipe buffer");
2041    }
2042
2043    /* Read in the returned spelling errors. */
2044    read_buff_read = 0;
2045    read_buff_size = pipe_buff_size + 1;
2046    read_buff = read_buff_ptr = charalloc(read_buff_size);
2047
2048    while ((bytesread = read(uniq_fd[0], read_buff_ptr,
2049	pipe_buff_size)) > 0) {
2050	read_buff_read += bytesread;
2051	read_buff_size += pipe_buff_size;
2052	read_buff = read_buff_ptr = charealloc(read_buff,
2053		read_buff_size);
2054	read_buff_ptr += read_buff_read;
2055    }
2056
2057    *read_buff_ptr = '\0';
2058    close(uniq_fd[0]);
2059
2060    /* Process the spelling errors. */
2061    read_buff_word = read_buff_ptr = read_buff;
2062
2063    while (*read_buff_ptr != '\0') {
2064	if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) {
2065	    *read_buff_ptr = '\0';
2066	    if (read_buff_word != read_buff_ptr) {
2067		if (!do_int_spell_fix(read_buff_word)) {
2068		    read_buff_word = read_buff_ptr;
2069		    break;
2070		}
2071	    }
2072	    read_buff_word = read_buff_ptr + 1;
2073	}
2074	read_buff_ptr++;
2075    }
2076
2077    /* Special case: the last word doesn't end with '\r' or '\n'. */
2078    if (read_buff_word != read_buff_ptr)
2079	do_int_spell_fix(read_buff_word);
2080
2081    free(read_buff);
2082    search_replace_abort();
2083    edit_refresh();
2084
2085    /* Process the end of the spell process. */
2086    waitpid(pid_spell, &spell_status, 0);
2087    waitpid(pid_sort, &sort_status, 0);
2088    waitpid(pid_uniq, &uniq_status, 0);
2089
2090    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
2091	return _("Error invoking \"spell\"");
2092
2093    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
2094	return _("Error invoking \"sort -f\"");
2095
2096    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
2097	return _("Error invoking \"uniq\"");
2098
2099    /* Otherwise... */
2100    return NULL;
2101
2102  close_pipes_and_exit:
2103    /* Don't leak any handles. */
2104    close(tempfile_fd);
2105    close(spell_fd[0]);
2106    close(spell_fd[1]);
2107    close(sort_fd[0]);
2108    close(sort_fd[1]);
2109    close(uniq_fd[0]);
2110    close(uniq_fd[1]);
2111    exit(1);
2112}
2113
2114/* External (alternate) spell checking.  Return NULL for normal
2115 * termination, and the error string otherwise. */
2116const char *do_alt_speller(char *tempfile_name)
2117{
2118    int alt_spell_status;
2119    size_t current_x_save = openfile->current_x;
2120    size_t pww_save = openfile->placewewant;
2121    ssize_t current_y_save = openfile->current_y;
2122    ssize_t lineno_save = openfile->current->lineno;
2123    pid_t pid_spell;
2124    char *ptr;
2125    static int arglen = 3;
2126    static char **spellargs = NULL;
2127#ifndef NANO_TINY
2128    bool old_mark_set = openfile->mark_set;
2129    bool added_magicline = FALSE;
2130	/* Whether we added a magicline after filebot. */
2131    bool right_side_up = FALSE;
2132	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
2133	 * FALSE if (current, current_x) is. */
2134    filestruct *top, *bot;
2135    size_t top_x, bot_x;
2136    ssize_t mb_lineno_save = 0;
2137	/* We're going to close the current file, and open the output of
2138	 * the alternate spell command.  The line that mark_begin points
2139	 * to will be freed, so we save the line number and restore it
2140	 * afterwards. */
2141    size_t totsize_save = openfile->totsize;
2142	/* Our saved value of totsize, used when we spell-check a marked
2143	 * selection. */
2144
2145    if (old_mark_set) {
2146	/* If the mark is on, save the number of the line it starts on,
2147	 * and then turn the mark off. */
2148	mb_lineno_save = openfile->mark_begin->lineno;
2149	openfile->mark_set = FALSE;
2150    }
2151#endif
2152
2153    endwin();
2154
2155    /* Set up an argument list to pass execvp(). */
2156    if (spellargs == NULL) {
2157	spellargs = (char **)nmalloc(arglen * sizeof(char *));
2158
2159	spellargs[0] = strtok(alt_speller, " ");
2160	while ((ptr = strtok(NULL, " ")) != NULL) {
2161	    arglen++;
2162	    spellargs = (char **)nrealloc(spellargs, arglen *
2163		sizeof(char *));
2164	    spellargs[arglen - 3] = ptr;
2165	}
2166	spellargs[arglen - 1] = NULL;
2167    }
2168    spellargs[arglen - 2] = tempfile_name;
2169
2170    /* Start a new process for the alternate speller. */
2171    if ((pid_spell = fork()) == 0) {
2172	/* Start alternate spell program; we are using $PATH. */
2173	execvp(spellargs[0], spellargs);
2174
2175	/* Should not be reached, if alternate speller is found!!! */
2176	exit(1);
2177    }
2178
2179    /* If we couldn't fork, get out. */
2180    if (pid_spell < 0)
2181	return _("Could not fork");
2182
2183#ifndef NANO_TINY
2184    /* Don't handle a pending SIGWINCH until the alternate spell checker
2185     * is finished and we've loaded the spell-checked file back in. */
2186    allow_pending_sigwinch(FALSE);
2187#endif
2188
2189    /* Wait for the alternate spell checker to finish. */
2190    wait(&alt_spell_status);
2191
2192    /* Reenter curses mode. */
2193    doupdate();
2194
2195    /* Restore the terminal to its previous state. */
2196    terminal_init();
2197
2198    /* Turn the cursor back on for sure. */
2199    curs_set(1);
2200
2201    /* The screen might have been resized.  If it has, reinitialize all
2202     * the windows based on the new screen dimensions. */
2203    window_init();
2204
2205    if (!WIFEXITED(alt_spell_status) ||
2206		WEXITSTATUS(alt_spell_status) != 0) {
2207	char *alt_spell_error;
2208	char *invoke_error = _("Error invoking \"%s\"");
2209
2210#ifndef NANO_TINY
2211	/* Turn the mark back on if it was on before. */
2212	openfile->mark_set = old_mark_set;
2213#endif
2214
2215	alt_spell_error =
2216		charalloc(strlen(invoke_error) +
2217		strlen(alt_speller) + 1);
2218	sprintf(alt_spell_error, invoke_error, alt_speller);
2219	return alt_spell_error;
2220    }
2221
2222#ifndef NANO_TINY
2223    if (old_mark_set) {
2224	/* If the mark is on, partition the filestruct so that it
2225	 * contains only the marked text; if the NO_NEWLINES flag isn't
2226	 * set, keep track of whether the text will have a magicline
2227	 * added when we're done correcting misspelled words; and
2228	 * turn the mark off. */
2229	mark_order((const filestruct **)&top, &top_x,
2230		(const filestruct **)&bot, &bot_x, &right_side_up);
2231	filepart = partition_filestruct(top, top_x, bot, bot_x);
2232	if (!ISSET(NO_NEWLINES))
2233	    added_magicline = (openfile->filebot->data[0] != '\0');
2234
2235	/* Get the number of characters in the marked text, and subtract
2236	 * it from the saved value of totsize. */
2237	totsize_save -= get_totsize(top, bot);
2238    }
2239#endif
2240
2241    /* Replace the text of the current buffer with the spell-checked
2242     * text. */
2243    replace_buffer(tempfile_name);
2244
2245#ifndef NANO_TINY
2246    if (old_mark_set) {
2247	filestruct *top_save = openfile->fileage;
2248
2249	/* If the mark was on, the NO_NEWLINES flag isn't set, and we
2250	 * added a magicline, remove it now. */
2251	if (!ISSET(NO_NEWLINES) && added_magicline)
2252	    remove_magicline();
2253
2254	/* Put the beginning and the end of the mark at the beginning
2255	 * and the end of the spell-checked text. */
2256	if (openfile->fileage == openfile->filebot)
2257	    bot_x += top_x;
2258	if (right_side_up) {
2259	    openfile->mark_begin_x = top_x;
2260	    current_x_save = bot_x;
2261	} else {
2262	    current_x_save = top_x;
2263	    openfile->mark_begin_x = bot_x;
2264	}
2265
2266	/* Unpartition the filestruct so that it contains all the text
2267	 * again.  Note that we've replaced the marked text originally
2268	 * in the partition with the spell-checked marked text in the
2269	 * temp file. */
2270	unpartition_filestruct(&filepart);
2271
2272	/* Renumber starting with the beginning line of the old
2273	 * partition.  Also add the number of characters in the
2274	 * spell-checked marked text to the saved value of totsize, and
2275	 * then make that saved value the actual value. */
2276	renumber(top_save);
2277	totsize_save += openfile->totsize;
2278	openfile->totsize = totsize_save;
2279
2280	/* Assign mark_begin to the line where the mark began before. */
2281	do_gotopos(mb_lineno_save, openfile->mark_begin_x,
2282		current_y_save, 0);
2283	openfile->mark_begin = openfile->current;
2284
2285	/* Assign mark_begin_x to the location in mark_begin where the
2286	 * mark began before, adjusted for any shortening of the
2287	 * line. */
2288	openfile->mark_begin_x = openfile->current_x;
2289
2290	/* Turn the mark back on. */
2291	openfile->mark_set = TRUE;
2292    }
2293#endif
2294
2295    /* Go back to the old position, and mark the file as modified. */
2296    do_gotopos(lineno_save, current_x_save, current_y_save, pww_save);
2297    set_modified();
2298
2299#ifndef NANO_TINY
2300    /* Handle a pending SIGWINCH again. */
2301    allow_pending_sigwinch(TRUE);
2302#endif
2303
2304    return NULL;
2305}
2306
2307/* Spell check the current file.  If an alternate spell checker is
2308 * specified, use it.  Otherwise, use the internal spell checker. */
2309void do_spell(void)
2310{
2311    bool status;
2312    FILE *temp_file;
2313    char *temp = safe_tempfile(&temp_file);
2314    const char *spell_msg;
2315
2316    if (temp == NULL) {
2317	statusbar(_("Error writing temp file: %s"), strerror(errno));
2318	return;
2319    }
2320
2321    status =
2322#ifndef NANO_TINY
2323	openfile->mark_set ? write_marked_file(temp, temp_file, TRUE,
2324	OVERWRITE) :
2325#endif
2326	write_file(temp, temp_file, TRUE, OVERWRITE, FALSE);
2327
2328    if (!status) {
2329	statusbar(_("Error writing temp file: %s"), strerror(errno));
2330	free(temp);
2331	return;
2332    }
2333
2334    spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) :
2335	do_int_speller(temp);
2336    unlink(temp);
2337    free(temp);
2338
2339    currshortcut = main_list;
2340
2341    /* If the spell-checker printed any error messages onscreen, make
2342     * sure that they're cleared off. */
2343    total_refresh();
2344
2345    if (spell_msg != NULL) {
2346	if (errno == 0)
2347	    /* Don't display an error message of "Success". */
2348	    statusbar(_("Spell checking failed: %s"), spell_msg);
2349	else
2350	    statusbar(_("Spell checking failed: %s: %s"), spell_msg,
2351		strerror(errno));
2352    } else
2353	statusbar(_("Finished checking spelling"));
2354}
2355#endif /* !DISABLE_SPELLER */
2356
2357#ifndef NANO_TINY
2358/* Our own version of "wc".  Note that its character counts are in
2359 * multibyte characters instead of single-byte characters. */
2360void do_wordlinechar_count(void)
2361{
2362    size_t words = 0, chars = 0;
2363    ssize_t lines = 0;
2364    size_t current_x_save = openfile->current_x;
2365    size_t pww_save = openfile->placewewant;
2366    filestruct *current_save = openfile->current;
2367    bool old_mark_set = openfile->mark_set;
2368    filestruct *top, *bot;
2369    size_t top_x, bot_x;
2370
2371    if (old_mark_set) {
2372	/* If the mark is on, partition the filestruct so that it
2373	 * contains only the marked text, and turn the mark off. */
2374	mark_order((const filestruct **)&top, &top_x,
2375	    (const filestruct **)&bot, &bot_x, NULL);
2376	filepart = partition_filestruct(top, top_x, bot, bot_x);
2377	openfile->mark_set = FALSE;
2378    }
2379
2380    /* Start at the top of the file. */
2381    openfile->current = openfile->fileage;
2382    openfile->current_x = 0;
2383    openfile->placewewant = 0;
2384
2385    /* Keep moving to the next word (counting punctuation characters as
2386     * part of a word, as "wc -w" does), without updating the screen,
2387     * until we reach the end of the file, incrementing the total word
2388     * count whenever we're on a word just before moving. */
2389    while (openfile->current != openfile->filebot ||
2390	openfile->current->data[openfile->current_x] != '\0') {
2391	if (do_next_word(TRUE, FALSE))
2392	    words++;
2393    }
2394
2395    /* Get the total line and character counts, as "wc -l"  and "wc -c"
2396     * do, but get the latter in multibyte characters. */
2397    if (old_mark_set) {
2398	lines = openfile->filebot->lineno -
2399		openfile->fileage->lineno + 1;
2400	chars = get_totsize(openfile->fileage, openfile->filebot);
2401
2402	/* Unpartition the filestruct so that it contains all the text
2403	 * again, and turn the mark back on. */
2404	unpartition_filestruct(&filepart);
2405	openfile->mark_set = TRUE;
2406    } else {
2407	lines = openfile->filebot->lineno;
2408	chars = openfile->totsize;
2409    }
2410
2411    /* Restore where we were. */
2412    openfile->current = current_save;
2413    openfile->current_x = current_x_save;
2414    openfile->placewewant = pww_save;
2415
2416    /* Display the total word, line, and character counts on the
2417     * statusbar. */
2418    statusbar(_("%sWords: %lu  Lines: %ld  Chars: %lu"), old_mark_set ?
2419	_("In Selection:  ") : "", (unsigned long)words, (long)lines,
2420	(unsigned long)chars);
2421}
2422#endif /* !NANO_TINY */
2423
2424/* Get verbatim input. */
2425void do_verbatim_input(void)
2426{
2427    int *kbinput;
2428    size_t kbinput_len, i;
2429    char *output;
2430
2431    /* TRANSLATORS: This is displayed when the next keystroke will be
2432     * inserted verbatim. */
2433    statusbar(_("Verbatim Input"));
2434
2435    /* Read in all the verbatim characters. */
2436    kbinput = get_verbatim_kbinput(edit, &kbinput_len);
2437
2438    /* If constant cursor position display is on, make sure the current
2439     * cursor position will be properly displayed on the statusbar.
2440     * Otherwise, blank the statusbar. */
2441    if (ISSET(CONST_UPDATE))
2442	do_cursorpos(TRUE);
2443    else {
2444	blank_statusbar();
2445	wnoutrefresh(bottomwin);
2446    }
2447
2448    /* Display all the verbatim characters at once, not filtering out
2449     * control characters. */
2450    output = charalloc(kbinput_len + 1);
2451
2452    for (i = 0; i < kbinput_len; i++)
2453	output[i] = (char)kbinput[i];
2454    output[i] = '\0';
2455
2456    free(kbinput);
2457
2458    do_output(output, kbinput_len, TRUE);
2459
2460    free(output);
2461}
2462