text.c 72.5 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3
 *   text.c                                                               *
4
 *                                                                        *
5
 *   Copyright (C) 1999-2004 Chris Allegretta                             *
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
6
 *   Copyright (C) 2005-2006 David Lawrence Ramsey                        *
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
 *   the Free Software Foundation; either version 2, or (at your option)  *
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

26
#include <stdio.h>
27
28
29
30
31
32
33
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>

34
#ifndef NANO_TINY
35
static pid_t pid = -1;
36
37
	/* The PID of the forked process in execute_command(), for use
	 * with the cancel_command() signal handler. */
38
39
#endif
#ifndef DISABLE_WRAPPING
40
static bool prepend_wrap = FALSE;
41
42
43
44
	/* Should we prepend wrapped text to the next line? */
#endif
#ifndef DISABLE_JUSTIFY
static filestruct *jusbottom = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
45
	/* Pointer to the end of the justify buffer. */
46
47
#endif

48
#ifndef NANO_TINY
49
/* Toggle the mark. */
50
51
52
53
54
55
56
57
58
59
60
61
62
63
void do_mark(void)
{
    openfile->mark_set = !openfile->mark_set;
    if (openfile->mark_set) {
	statusbar(_("Mark Set"));
	openfile->mark_begin = openfile->current;
	openfile->mark_begin_x = openfile->current_x;
    } else {
	statusbar(_("Mark UNset"));
	openfile->mark_begin = NULL;
	openfile->mark_begin_x = 0;
	edit_refresh();
    }
}
64
#endif /* !NANO_TINY */
65

66
/* Delete one character. */
67
68
69
70
void do_delete(void)
{
    bool do_refresh = FALSE;
	/* Do we have to call edit_refresh(), or can we get away with
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
71
	 * just update_line()? */
72
73
74
75
76
77
78

    assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data));

    openfile->placewewant = xplustabs();

    if (openfile->current->data[openfile->current_x] != '\0') {
	int char_buf_len = parse_mbchar(openfile->current->data +
79
		openfile->current_x, NULL, NULL);
80
81
82
83
84
85
86
87
88
89
90
91
	size_t line_len = strlen(openfile->current->data +
		openfile->current_x);

	assert(openfile->current_x < strlen(openfile->current->data));

	/* Let's get dangerous. */
	charmove(&openfile->current->data[openfile->current_x],
		&openfile->current->data[openfile->current_x +
		char_buf_len], line_len - char_buf_len + 1);

	null_at(&openfile->current->data, openfile->current_x +
		line_len - char_buf_len);
92
#ifndef NANO_TINY
93
94
95
96
97
98
	if (openfile->mark_set && openfile->mark_begin ==
		openfile->current && openfile->current_x <
		openfile->mark_begin_x)
	    openfile->mark_begin_x -= char_buf_len;
#endif
	openfile->totsize--;
99
    } else if (openfile->current != openfile->filebot) {
100
101
102
103
104
105
106
107
108
109
110
111
112
	filestruct *foo = openfile->current->next;

	assert(openfile->current_x == strlen(openfile->current->data));

	/* If we're deleting at the end of a line, we need to call
	 * edit_refresh(). */
	if (openfile->current->data[openfile->current_x] == '\0')
	    do_refresh = TRUE;

	openfile->current->data = charealloc(openfile->current->data,
		openfile->current_x + strlen(foo->data) + 1);
	strcpy(openfile->current->data + openfile->current_x,
		foo->data);
113
#ifndef NANO_TINY
114
115
116
117
118
119
120
121
122
123
124
125
126
	if (openfile->mark_set && openfile->mark_begin ==
		openfile->current->next) {
	    openfile->mark_begin = openfile->current;
	    openfile->mark_begin_x += openfile->current_x;
	}
#endif
	if (openfile->filebot == foo)
	    openfile->filebot = openfile->current;

	unlink_node(foo);
	delete_node(foo);
	renumber(openfile->current);
	openfile->totsize--;
127

128
129
	/* If the NO_NEWLINES flag isn't set, and text has been added to
	 * the magicline as a result of deleting at the end of the line
130
	 * before filebot, add a new magicline. */
131
132
	if (!ISSET(NO_NEWLINES) && openfile->current ==
		openfile->filebot && openfile->current->data[0] != '\0')
133
	    new_magicline();
134
135
136
    } else
	return;

137
    set_modified();
138
139
140
141
142
143
144
145
146
147
148
149
150
151

#ifdef ENABLE_COLOR
    /* If color syntaxes are available and turned on, we need to call
     * edit_refresh(). */
    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX))
	do_refresh = TRUE;
#endif

    if (do_refresh)
	edit_refresh();
    else
	update_line(openfile->current, openfile->current_x);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
152
153
/* Backspace over one character.  That is, move the cursor left one
 * character, and then delete the character there. */
154
155
156
157
void do_backspace(void)
{
    if (openfile->current != openfile->fileage ||
	openfile->current_x > 0) {
158
	do_left();
159
160
161
162
	do_delete();
    }
}

163
164
/* Insert a tab.  If the TABS_TO_SPACES flag is set, insert the number
 * of spaces that a tab would normally take up. */
165
166
void do_tab(void)
{
167
#ifndef NANO_TINY
168
169
    if (ISSET(TABS_TO_SPACES)) {
	char *output;
170
	size_t output_len = 0, new_pww = xplustabs();
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

	do {
	    new_pww++;
	    output_len++;
	} while (new_pww % tabsize != 0);

	output = charalloc(output_len + 1);

	charset(output, ' ', output_len);
	output[output_len] = '\0';

	do_output(output, output_len, TRUE);

	free(output);
    } else {
#endif
	do_output("\t", 1, TRUE);
188
#ifndef NANO_TINY
189
190
191
192
    }
#endif
}

193
#ifndef NANO_TINY
194
195
196
197
198
199
/* Indent or unindent the current line (or all lines covered by the mark
 * if the mark is on) len columns, depending on whether len is positive
 * or negative.  If the TABS_TO_SPACES flag is set, indent/unindent by
 * len spaces.  Otherwise, indent/unindent by (len / tabsize) tabs and
 * (len % tabsize) spaces. */
void do_indent(ssize_t cols)
200
201
202
203
204
{
    bool indent_changed = FALSE;
	/* Whether any indenting or unindenting was done. */
    bool unindent = FALSE;
	/* 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
209
210
211
212
213
214
	/* The length of the text added to each line in order to indent
	 * it. */
    filestruct *top, *bot, *f;
    size_t top_x, bot_x;

    assert(openfile->current != NULL && openfile->current->data != NULL);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
215
216
    /* If cols is zero, get out. */
    if (cols == 0)
217
218
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
219
220
221
222
    /* If cols is negative, make it positive and set unindent to
     * TRUE. */
    if (cols < 0) {
	cols = -cols;
223
224
225
226
227
228
	unindent = TRUE;
    /* Otherwise, we're indenting, in which case the file will always be
     * modified, so set indent_changed to TRUE. */
    } else
	indent_changed = TRUE;

229
230
231
232
233
234
235
236
237
    /* If the mark is on, use all lines covered by the mark. */
    if (openfile->mark_set)
	mark_order((const filestruct **)&top, &top_x,
		(const filestruct **)&bot, &bot_x, NULL);
    /* Otherwise, use the current line. */
    else {
	top = openfile->current;
	bot = top;
    }
238

239
240
241
    if (!unindent) {
	/* Set up the text we'll be using as indentation. */
	line_indent = charalloc(cols + 1);
242

243
244
245
246
247
248
249
250
251
	if (ISSET(TABS_TO_SPACES)) {
	    /* Set the indentation to cols spaces. */
	    charset(line_indent, ' ', cols);
	    line_indent_len = cols;
	} else {
	    /* Set the indentation to (cols / tabsize) tabs and (cols %
	     * tabsize) spaces. */
	    size_t num_tabs = cols / tabsize;
	    size_t num_spaces = cols % tabsize;
252

253
254
	    charset(line_indent, '\t', num_tabs);
	    charset(line_indent + num_tabs, ' ', num_spaces);
255

256
257
	    line_indent_len = num_tabs + num_spaces;
	}
258

259
260
	line_indent[line_indent_len] = '\0';
    }
261

262
    /* Go through each line of the text. */
263
264
    for (f = top; f != bot->next; f = f->next) {
	size_t line_len = strlen(f->data);
265
	size_t indent_len = indent_length(f->data);
266

267
268
269
270
271
272
273
274
275
276
277
	if (!unindent) {
	    /* If we're indenting, add the characters in line_indent to
	     * the beginning of the non-whitespace text of this line. */
	    f->data = charealloc(f->data, line_len +
		line_indent_len + 1);
	    charmove(&f->data[indent_len + line_indent_len],
		&f->data[indent_len], line_len - indent_len + 1);
	    strncpy(f->data + indent_len, line_indent, line_indent_len);
	    openfile->totsize += line_indent_len;

	    /* Keep track of the change in the current line. */
278
279
	    if (openfile->mark_set && f == openfile->mark_begin &&
		openfile->mark_begin_x >= indent_len)
280
281
282
283
284
285
286
287
288
289
290
		openfile->mark_begin_x += line_indent_len;

	    if (f == openfile->current && openfile->current_x >=
		indent_len)
		openfile->current_x += line_indent_len;

	    /* If the NO_NEWLINES flag isn't set, and this is the
	     * magicline, add a new magicline. */
	    if (!ISSET(NO_NEWLINES) && f == openfile->filebot)
		new_magicline();
	} else {
291
	    size_t indent_col = strnlenpt(f->data, indent_len);
292
293
		/* The length in columns of the indentation on this
		 * line. */
294

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
295
296
297
	    if (cols <= indent_col) {
		size_t indent_new = actual_x(f->data, indent_col -
			cols);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
298
299
			/* The length of the indentation remaining on
			 * this line after we unindent. */
300
		size_t indent_shift = indent_len - indent_new;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
301
302
			/* The change in the indentation on this line
			 * after we unindent. */
303

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
304
		/* If we're unindenting, and there's at least cols
305
306
307
308
309
310
		 * columns' worth of indentation at the beginning of the
		 * non-whitespace text of this line, remove it. */
		charmove(&f->data[indent_new], &f->data[indent_len],
			line_len - indent_shift - indent_new + 1);
		null_at(&f->data, line_len - indent_shift + 1);
		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
315
316
317
318
319
			openfile->mark_begin_x > indent_new) {
		    if (openfile->mark_begin_x <= indent_len)
			openfile->mark_begin_x = indent_new;
		    else
			openfile->mark_begin_x -= indent_shift;
		}
320

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
321
		if (f == openfile->current && openfile->current_x >
322
323
324
325
326
327
			indent_new) {
		    if (openfile->current_x <= indent_len)
			openfile->current_x = indent_new;
		    else
			openfile->current_x -= indent_shift;
		}
328

329
330
331
332
333
334
335
		/* We've unindented, so set indent_changed to TRUE. */
		if (!indent_changed)
		    indent_changed = TRUE;
	    }
	}
    }

336
    if (!unindent)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
337
	/* Clean up. */
338
	free(line_indent);
339
340
341
342
343
344
345
346
347
348

    if (indent_changed) {
	/* Mark the file as modified. */
	set_modified();

	/* Update the screen. */
	edit_refresh();
    }
}

349
350
351
/* Indent the current line, or all lines covered by the mark if the mark
 * is on, tabsize columns. */
void do_indent_void(void)
352
{
353
    do_indent(tabsize);
354
355
}

356
357
358
/* Unindent the current line, or all lines covered by the mark if the
 * mark is on, tabsize columns. */
void do_unindent(void)
359
{
360
    do_indent(-tabsize);
361
362
363
}
#endif /* !NANO_TINY */

364
/* Someone hits Enter *gasp!* */
365
366
367
368
369
370
371
void do_enter(void)
{
    filestruct *newnode = make_new_node(openfile->current);
    size_t extra = 0;

    assert(openfile->current != NULL && openfile->current->data != NULL);

372
#ifndef NANO_TINY
373
374
375
376
377
378
379
380
381
382
383
384
385
386
    /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
    if (ISSET(AUTOINDENT)) {
	/* If we are breaking the line in the indentation, the new
	 * indentation should have only current_x characters, and
	 * current_x should not change. */
	extra = indent_length(openfile->current->data);
	if (extra > openfile->current_x)
	    extra = openfile->current_x;
    }
#endif
    newnode->data = charalloc(strlen(openfile->current->data +
	openfile->current_x) + extra + 1);
    strcpy(&newnode->data[extra], openfile->current->data +
	openfile->current_x);
387
#ifndef NANO_TINY
388
389
390
391
392
393
    if (ISSET(AUTOINDENT)) {
	strncpy(newnode->data, openfile->current->data, extra);
	openfile->totsize += mbstrlen(newnode->data);
    }
#endif
    null_at(&openfile->current->data, openfile->current_x);
394
#ifndef NANO_TINY
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
    if (openfile->mark_set && openfile->current ==
	openfile->mark_begin && openfile->current_x <
	openfile->mark_begin_x) {
	openfile->mark_begin = newnode;
	openfile->mark_begin_x += extra - openfile->current_x;
    }
#endif
    openfile->current_x = extra;

    if (openfile->current == openfile->filebot)
	openfile->filebot = newnode;
    splice_node(openfile->current, newnode,
	openfile->current->next);

    renumber(openfile->current);
    openfile->current = newnode;

    openfile->totsize++;
    set_modified();
414

415
    openfile->placewewant = xplustabs();
416
417

    edit_refresh();
418
419
}

420
#ifndef NANO_TINY
421
422
/* Send a SIGKILL (unconditional kill) to the forked process in
 * execute_command(). */
423
RETSIGTYPE cancel_command(int signal)
424
425
426
427
428
{
    if (kill(pid, SIGKILL) == -1)
	nperror("kill");
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
429
/* Execute command in a shell.  Return TRUE on success. */
430
431
432
433
bool execute_command(const char *command)
{
    int fd[2];
    FILE *f;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
434
    char *shellenv;
435
436
437
438
439
440
441
442
443
444
445
    struct sigaction oldaction, newaction;
	/* Original and temporary handlers for SIGINT. */
    bool sig_failed = FALSE;
	/* Did sigaction() fail without changing the signal handlers? */

    /* Make our pipes. */
    if (pipe(fd) == -1) {
	statusbar(_("Could not pipe"));
	return FALSE;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
446
    /* Check $SHELL for the shell to use.  If it isn't set, use
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
447
448
     * /bin/sh.  Note that $SHELL should contain only a path, with no
     * arguments. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
449
450
451
452
    shellenv = getenv("SHELL");
    if (shellenv == NULL)
	shellenv = "/bin/sh";

453
454
455
456
457
458
459
    /* Fork a child. */
    if ((pid = fork()) == 0) {
	close(fd[0]);
	dup2(fd[1], fileno(stdout));
	dup2(fd[1], fileno(stderr));

	/* If execl() returns at all, there was an error. */
460
	execl(shellenv, tail(shellenv), "-c", command, NULL);
461
462
463
	exit(0);
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
464
    /* Continue as parent. */
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
    close(fd[1]);

    if (pid == -1) {
	close(fd[0]);
	statusbar(_("Could not fork"));
	return FALSE;
    }

    /* Before we start reading the forked command's output, we set
     * things up so that Ctrl-C will cancel the new process. */

    /* Enable interpretation of the special control keys so that we get
     * SIGINT when Ctrl-C is pressed. */
    enable_signals();

    if (sigaction(SIGINT, NULL, &newaction) == -1) {
	sig_failed = TRUE;
	nperror("sigaction");
    } else {
	newaction.sa_handler = cancel_command;
	if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
	    sig_failed = TRUE;
	    nperror("sigaction");
	}
    }

    /* Note that now oldaction is the previous SIGINT signal handler,
     * to be restored later. */

    f = fdopen(fd[0], "rb");
    if (f == NULL)
	nperror("fdopen");

    read_file(f, "stdin");

    if (wait(NULL) == -1)
	nperror("wait");

    if (!sig_failed && sigaction(SIGINT, &oldaction, NULL) == -1)
	nperror("sigaction");

    /* Disable interpretation of the special control keys so that we can
     * use Ctrl-C for other things. */
    disable_signals();

    return TRUE;
}
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. */
517
518
void wrap_reset(void)
{
519
    prepend_wrap = FALSE;
520
521
522
}

/* We wrap the given line.  Precondition: we assume the cursor has been
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
523
524
 * moved forward since the last typed character.  Return TRUE if we
 * wrapped, and FALSE otherwise. */
525
526
527
bool do_wrap(filestruct *line)
{
    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
533
    const char *indent_string = NULL;
	/* Indentation to prepend to the new line. */
534
535
    size_t indent_len = 0;
	/* The length of indent_string. */
536
#endif
537
538
539
540
    const char *after_break;
	/* The text after the wrap point. */
    size_t after_break_len;
	/* The length of after_break. */
541
    bool prepending = FALSE;
542
	/* Do we prepend to the next line? */
543
544
    const char *next_line = NULL;
	/* The next line, minus indentation. */
545
546
547
548
549
550
    size_t next_line_len = 0;
	/* The length of next_line. */
    char *new_line = NULL;
	/* The line we create. */
    size_t new_line_len = 0;
	/* The eventual length of new_line. */
551
552
553
554
555
556
557

    /* There are three steps.  First, we decide where to wrap.  Then, we
     * create the new wrap line.  Finally, we clean up. */

    /* Step 1, finding where to wrap.  We are going to add a new line
     * after a blank character.  In this step, we call break_line() to
     * get the location of the last blank we can break the line at, and
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
558
559
     * set wrap_loc to the location of the character after it, so that
     * the blank is preserved at the end of the line.
560
561
562
563
564
565
566
567
568
569
570
     *
     * If there is no legal wrap point, or we reach the last character
     * of the line while trying to find one, we should return without
     * wrapping.  Note that if autoindent is turned on, we don't break
     * at the end of it! */
    assert(line != NULL && line->data != NULL);

    /* Save the length of the line. */
    line_len = strlen(line->data);

    /* Find the last blank where we can break the line. */
571
572
573
574
575
    wrap_loc = break_line(line->data, fill
#ifndef DISABLE_HELP
	, FALSE
#endif
	);
576
577
578
579
580
581
582
583
584
585
586
587
588

    /* If we couldn't break the line, or we've reached the end of it, we
     * don't wrap. */
    if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
	return FALSE;

    /* Otherwise, move forward to the character just after the blank. */
    wrap_loc += move_mbright(line->data + wrap_loc, 0);

    /* If we've reached the end of the line, we don't wrap. */
    if (line->data[wrap_loc] == '\0')
	return FALSE;

589
#ifndef NANO_TINY
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
    /* If autoindent is turned on, and we're on the character just after
     * the indentation, we don't wrap. */
    if (ISSET(AUTOINDENT)) {
	/* Get the indentation of this line. */
	indent_string = line->data;
	indent_len = indent_length(indent_string);

	if (wrap_loc == indent_len)
	    return FALSE;
    }
#endif

    /* Step 2, making the new wrap line.  It will consist of indentation
     * followed by the text after the wrap point, optionally followed by
     * a space (if the text after the wrap point doesn't end in a blank)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
605
606
     * and the text of the next line, if they can fit without wrapping,
     * the next line exists, and the prepend_wrap flag is set. */
607
608
609
610
611
612
613

    /* after_break is the text that will be wrapped to the next line. */
    after_break = line->data + wrap_loc;
    after_break_len = line_len - wrap_loc;

    assert(strlen(after_break) == after_break_len);

614
615
616
617
    /* We prepend the wrapped text to the next line, if the prepend_wrap
     * flag is set, there is a next line, and prepending would not make
     * the line too long. */
    if (prepend_wrap && line != openfile->filebot) {
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
	const char *end = after_break + move_mbleft(after_break,
		after_break_len);

	/* If after_break doesn't end in a blank, make sure it ends in a
	 * space. */
	if (!is_blank_mbchar(end)) {
	    line_len++;
	    line->data = charealloc(line->data, line_len + 1);
	    line->data[line_len - 1] = ' ';
	    line->data[line_len] = '\0';
	    after_break = line->data + wrap_loc;
	    after_break_len++;
	    openfile->totsize++;
	}

	next_line = line->next->data;
	next_line_len = strlen(next_line);

	if (after_break_len + next_line_len <= fill) {
637
	    prepending = TRUE;
638
639
640
641
642
643
644
645
646
	    new_line_len += next_line_len;
	}
    }

    /* new_line_len is now the length of the text that will be wrapped
     * to the next line, plus (if we're prepending to it) the length of
     * the text of the next line. */
    new_line_len += after_break_len;

647
#ifndef NANO_TINY
648
    if (ISSET(AUTOINDENT)) {
649
650
	if (prepending) {
	    /* If we're prepending, the indentation will come from the
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
	     * next line. */
	    indent_string = next_line;
	    indent_len = indent_length(indent_string);
	    next_line += indent_len;
	} else {
	    /* Otherwise, it will come from this line, in which case
	     * we should increase new_line_len to make room for it. */
	    new_line_len += indent_len;
	    openfile->totsize += mbstrnlen(indent_string, indent_len);
	}
    }
#endif

    /* Now we allocate the new line and copy the text into it. */
    new_line = charalloc(new_line_len + 1);
    new_line[0] = '\0';

668
#ifndef NANO_TINY
669
670
671
672
673
674
675
676
677
678
679
680
681
682
    if (ISSET(AUTOINDENT)) {
	/* Copy the indentation. */
	strncpy(new_line, indent_string, indent_len);
	new_line[indent_len] = '\0';
	new_line_len += indent_len;
    }
#endif

    /* Copy all the text after the wrap point of the current line. */
    strcat(new_line, after_break);

    /* Break the current line at the wrap point. */
    null_at(&line->data, wrap_loc);

683
684
    if (prepending) {
	/* If we're prepending, copy the text from the next line, minus
685
686
687
688
689
	 * the indentation that we already copied above. */
	strcat(new_line, next_line);

	free(line->next->data);
	line->next->data = new_line;
690
691
692
693
694

	/* If the NO_NEWLINES flag isn't set, and text has been added to
	 * the magicline, make a new magicline. */
	if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
	    new_magicline();
695
696
697
698
699
700
    } else {
	/* Otherwise, make a new line and copy the text after where we
	 * broke this line to the beginning of the new line. */
	splice_node(openfile->current, make_new_node(openfile->current),
		openfile->current->next);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
701
702
	/* If the current line is the last line of the file, move the
	 * last line of the file down to the next line. */
703
704
705
	if (openfile->filebot == openfile->current)
	    openfile->filebot = openfile->current->next;

706
707
708
709
710
711
712
713
	openfile->current->next->data = new_line;

	openfile->totsize++;
    }

    /* Step 3, clean up.  Reposition the cursor and mark, and do some
     * other sundry things. */

714
715
716
    /* Set the prepend_wrap flag, so that later wraps of this line will
     * be prepended to the next line. */
    prepend_wrap = TRUE;
717

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
718
719
    /* Each line knows its number.  We recalculate these if we inserted
     * a new line. */
720
    if (!prepending)
721
722
723
	renumber(line);

    /* 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
729
	openfile->current = openfile->current->next;
	openfile->current_x -= wrap_loc
730
#ifndef NANO_TINY
731
732
733
734
735
736
		- indent_len
#endif
		;
	openfile->placewewant = xplustabs();
    }

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
741
742
743
744
745
     * we move it right. */
    if (openfile->mark_set) {
	if (openfile->mark_begin == line && openfile->mark_begin_x >
		wrap_loc) {
	    openfile->mark_begin = line->next;
	    openfile->mark_begin_x -= wrap_loc - indent_len + 1;
746
	} else if (prepending && openfile->mark_begin == line->next)
747
748
749
750
751
752
753
754
	    openfile->mark_begin_x += after_break_len;
    }
#endif

    return TRUE;
}
#endif /* !DISABLE_WRAPPING */

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
758
 * that the display length to there is at most (goal + 1).  If there is
 * no such blank, then we find the first blank.  We then take the last
759
760
 * blank in that group of blanks.  The terminating '\0' counts as a
 * blank, as does a '\n' if newline is TRUE. */
761
762
763
764
765
ssize_t break_line(const char *line, ssize_t goal
#ifndef DISABLE_HELP
	, bool newline
#endif
	)
766
{
767
768
769
770
771
    ssize_t blank_loc = -1;
	/* Current tentative return value.  Index of the last blank we
	 * found with short enough display width.  */
    ssize_t cur_loc = 0;
	/* Current index in line. */
772
773
    size_t cur_pos = 0;
	/* Current column position in line. */
774
    int line_len;
775

776
    assert(line != NULL);
777

778
779
    while (*line != '\0' && goal >= cur_pos) {
	line_len = parse_mbchar(line, NULL, &cur_pos);
780

781
782
783
784
785
	if (is_blank_mbchar(line)
#ifndef DISABLE_HELP
		|| (newline && *line == '\n')
#endif
		) {
786
	    blank_loc = cur_loc;
787

788
#ifndef DISABLE_HELP
789
790
	    if (newline && *line == '\n')
		break;
791
#endif
792
793
794
795
	}

	line += line_len;
	cur_loc += line_len;
796
797
    }

798
    if (goal >= cur_pos)
799
800
	/* In fact, the whole line displays shorter than goal. */
	return cur_loc;
801

802
803
804
    if (blank_loc == -1) {
	/* No blank was found that was short enough. */
	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
811
812
813
814
	    if (is_blank_mbchar(line)
#ifndef DISABLE_HELP
		|| (newline && *line == '\n')
#endif
		) {
815
816
		if (!found_blank)
		    found_blank = TRUE;
817
		found_blank_loc = cur_loc;
818
	    } else if (found_blank)
819
		return found_blank_loc;
820

821
822
823
	    line += line_len;
	    cur_loc += line_len;
	}
824

825
826
	return -1;
    }
827

828
829
830
    /* Move to the last blank after blank_loc, if there is one. */
    line -= cur_loc;
    line += blank_loc;
831
    line_len = parse_mbchar(line, NULL, NULL);
832
    line += line_len;
833

834
835
836
837
838
    while (*line != '\0' && (is_blank_mbchar(line)
#ifndef DISABLE_HELP
	|| (newline && *line == '\n')
#endif
	)) {
839
840
841
842
843
#ifndef DISABLE_HELP
	if (newline && *line == '\n')
	    break;
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
844
845
	line_len = parse_mbchar(line, NULL, NULL);

846
847
	line += line_len;
	blank_loc += line_len;
848
849
    }

850
851
    return blank_loc;
}
852
#endif /* !DISABLE_HELP || !DISABLE_WRAPJUSTIFY */
853

854
#if !defined(NANO_TINY) || !defined(DISABLE_JUSTIFY)
855
856
857
858
859
860
861
/* The "indentation" of a line is the whitespace between the quote part
 * and the non-whitespace of the line. */
size_t indent_length(const char *line)
{
    size_t len = 0;
    char *blank_mb;
    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
871
	if (!is_blank_mbchar(blank_mb))
	    break;
872

873
874
875
	line += blank_mb_len;
	len += blank_mb_len;
    }
876

877
878
879
    free(blank_mb);

    return len;
880
}
881
#endif /* !NANO_TINY || !DISABLE_JUSTIFY */
882

883
884
885
886
887
888
889
890
891
892
893
894
#ifndef DISABLE_JUSTIFY
/* justify_format() replaces blanks with spaces and multiple spaces by 1
 * (except it maintains up to 2 after a character in punct optionally
 * followed by a character in brackets, and removes all from the end).
 *
 * justify_format() might make paragraph->data shorter, and change the
 * actual pointer with null_at().
 *
 * justify_format() will not look at the first skip characters of
 * paragraph.  skip should be at most strlen(paragraph->data).  The
 * character at paragraph[skip + 1] must not be blank. */
void justify_format(filestruct *paragraph, size_t skip)
895
{
896
897
    char *end, *new_end, *new_paragraph_data;
    size_t shift = 0;
898
#ifndef NANO_TINY
899
900
    size_t mark_shift = 0;
#endif
901

902
903
904
905
906
    /* These four asserts are assumptions about the input data. */
    assert(paragraph != NULL);
    assert(paragraph->data != NULL);
    assert(skip < strlen(paragraph->data));
    assert(!is_blank_mbchar(paragraph->data + skip));
907

908
909
910
911
    end = paragraph->data + skip;
    new_paragraph_data = charalloc(strlen(paragraph->data) + 1);
    strncpy(new_paragraph_data, paragraph->data, skip);
    new_end = new_paragraph_data + skip;
912

913
    while (*end != '\0') {
914
	int end_len;
915

916
917
	/* If this character is blank, change it to a space if
	 * necessary, and skip over all blanks after it. */
918
	if (is_blank_mbchar(end)) {
919
920
	    end_len = parse_mbchar(end, NULL, NULL);

921
922
923
	    *new_end = ' ';
	    new_end++;
	    end += end_len;
924

925
	    while (*end != '\0' && is_blank_mbchar(end)) {
926
		end_len = parse_mbchar(end, NULL, NULL);
927

928
929
		end += end_len;
		shift += end_len;
930

931
#ifndef NANO_TINY
932
933
934
935
936
937
938
939
		/* Keep track of the change in the current line. */
		if (openfile->mark_set && openfile->mark_begin ==
			paragraph && openfile->mark_begin_x >= end -
			paragraph->data)
		    mark_shift += end_len;
#endif
	    }
	/* If this character is punctuation optionally followed by a
940
941
942
	 * bracket and then followed by blanks, change no more than two
	 * of the blanks to spaces if necessary, and skip over all
	 * blanks after them. */
943
	} else if (mbstrchr(punct, end) != NULL) {
944
945
	    end_len = parse_mbchar(end, NULL, NULL);

946
947
948
949
950
951
	    while (end_len > 0) {
		*new_end = *end;
		new_end++;
		end++;
		end_len--;
	    }
952

953
	    if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
954
		end_len = parse_mbchar(end, NULL, NULL);
955

956
957
958
959
960
961
962
		while (end_len > 0) {
		    *new_end = *end;
		    new_end++;
		    end++;
		    end_len--;
		}
	    }
963

964
	    if (*end != '\0' && is_blank_mbchar(end)) {
965
		end_len = parse_mbchar(end, NULL, NULL);
966

967
968
969
970
		*new_end = ' ';
		new_end++;
		end += end_len;
	    }
971

972
	    if (*end != '\0' && is_blank_mbchar(end)) {
973
		end_len = parse_mbchar(end, NULL, NULL);
974

975
976
977
978
		*new_end = ' ';
		new_end++;
		end += end_len;
	    }
979

980
	    while (*end != '\0' && is_blank_mbchar(end)) {
981
		end_len = parse_mbchar(end, NULL, NULL);
982

983
984
		end += end_len;
		shift += end_len;
985

986
#ifndef NANO_TINY
987
988
989
990
991
992
993
994
		/* Keep track of the change in the current line. */
		if (openfile->mark_set && openfile->mark_begin ==
			paragraph && openfile->mark_begin_x >= end -
			paragraph->data)
		    mark_shift += end_len;
#endif
	    }
	/* If this character is neither blank nor punctuation, leave it
995
	 * unchanged. */
996
	} else {
997
998
	    end_len = parse_mbchar(end, NULL, NULL);

999
1000
1001
1002
1003
1004
1005
	    while (end_len > 0) {
		*new_end = *end;
		new_end++;
		end++;
		end_len--;
	    }
	}
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
1014
1015
1016
    while (new_end > new_paragraph_data + skip &&
	*(new_end - 1) == ' ') {
	new_end--;
	shift++;
1017
1018
    }

1019
1020
1021
1022
1023
    if (shift > 0) {
	openfile->totsize -= shift;
	null_at(&new_paragraph_data, new_end - new_paragraph_data);
	free(paragraph->data);
	paragraph->data = new_paragraph_data;
1024

1025
#ifndef NANO_TINY
1026
1027
1028
1029
1030
1031
	/* Adjust the mark coordinates to compensate for the change in
	 * the current line. */
	if (openfile->mark_set && openfile->mark_begin == paragraph) {
	    openfile->mark_begin_x -= mark_shift;
	    if (openfile->mark_begin_x > new_end - new_paragraph_data)
		openfile->mark_begin_x = new_end - new_paragraph_data;
1032
	}
1033
1034
1035
1036
#endif
    } else
	free(new_paragraph_data);
}
1037

1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
/* The "quote part" of a line is the largest initial substring matching
 * the quote string.  This function returns the length of the quote part
 * of the given line.
 *
 * Note that if !HAVE_REGEX_H then we match concatenated copies of
 * quotestr. */
size_t quote_length(const char *line)
{
#ifdef HAVE_REGEX_H
    regmatch_t matches;
    int rc = regexec(&quotereg, line, 1, &matches, 0);
1049

1050
1051
1052
1053
1054
1055
1056
    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
	return 0;
    /* matches.rm_so should be 0, since the quote string should start
     * with the caret ^. */
    return matches.rm_eo;
#else	/* !HAVE_REGEX_H */
    size_t qdepth = 0;
1057

1058
1059
1060
1061
1062
1063
    /* Compute quote depth level. */
    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
	qdepth += quotelen;
    return qdepth;
#endif	/* !HAVE_REGEX_H */
}
1064

1065
1066
1067
1068
1069
1070
1071
1072
/* a_line and b_line are lines of text.  The quotation part of a_line is
 * the first a_quote characters.  Check that the quotation part of
 * b_line is the same. */
bool quotes_match(const char *a_line, size_t a_quote, const char
	*b_line)
{
    /* Here is the assumption about a_quote. */
    assert(a_quote == quote_length(a_line));
1073

1074
1075
1076
    return (a_quote == quote_length(b_line) &&
	strncmp(a_line, b_line, a_quote) == 0);
}
1077

1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
/* We assume a_line and b_line have no quote part.  Then, we return
 * whether b_line could follow a_line in a paragraph. */
bool indents_match(const char *a_line, size_t a_indent, const char
	*b_line, size_t b_indent)
{
    assert(a_indent == indent_length(a_line));
    assert(b_indent == indent_length(b_line));

    return (b_indent <= a_indent &&
	strncmp(a_line, b_line, b_indent) == 0);
1088
1089
}

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
/* Is foo the beginning of a paragraph?
 *
 *   A line of text consists of a "quote part", followed by an
 *   "indentation part", followed by text.  The functions quote_length()
 *   and indent_length() calculate these parts.
 *
 *   A line is "part of a paragraph" if it has a part not in the quote
 *   part or the indentation.
 *
 *   A line is "the beginning of a paragraph" if it is part of a
 *   paragraph and
 *	1) it is the top line of the file, or
 *	2) the line above it is not part of a paragraph, or
 *	3) the line above it does not have precisely the same quote
 *	   part, or
 *	4) the indentation of this line is not an initial substring of
 *	   the indentation of the previous line, or
 *	5) this line has no quote part and some indentation, and
 *	   autoindent isn't turned on.
 *   The reason for number 5) is that if autoindent isn't turned on,
 *   then an indented line is expected to start a paragraph, as in
 *   books.  Thus, nano can justify an indented paragraph only if
 *   autoindent is turned on. */
bool begpar(const filestruct *const foo)
1114
{
1115
1116
1117
1118
    size_t quote_len, indent_len, temp_id_len;

    if (foo == NULL)
	return FALSE;
1119

1120
    /* Case 1). */
1121
    if (foo == openfile->fileage)
1122
	return TRUE;
1123

1124
1125
    quote_len = quote_length(foo->data);
    indent_len = indent_length(foo->data + quote_len);
1126

1127
1128
1129
    /* Not part of a paragraph. */
    if (foo->data[quote_len + indent_len] == '\0')
	return FALSE;
1130

1131
1132
1133
    /* Case 3). */
    if (!quotes_match(foo->data, quote_len, foo->prev->data))
	return TRUE;
1134

1135
    temp_id_len = indent_length(foo->prev->data + quote_len);
1136

1137
1138
1139
    /* Case 2) or 5) or 4). */
    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
	(quote_len == 0 && indent_len > 0
1140
#ifndef NANO_TINY
1141
1142
1143
1144
1145
	&& !ISSET(AUTOINDENT)
#endif
	) || !indents_match(foo->prev->data + quote_len, temp_id_len,
	foo->data + quote_len, indent_len))
	return TRUE;
1146

1147
1148
    return FALSE;
}
1149

1150
1151
1152
1153
/* Is foo inside a paragraph? */
bool inpar(const filestruct *const foo)
{
    size_t quote_len;
1154

1155
1156
    if (foo == NULL)
	return FALSE;
1157

1158
    quote_len = quote_length(foo->data);
1159

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1160
1161
    return (foo->data[quote_len + indent_length(foo->data +
	quote_len)] != '\0');
1162
}
1163

1164
/* Move the next par_len lines, starting with first_line, into the
1165
1166
 * justify buffer, leaving copies of those lines in place.  Assume that
 * par_len is greater than zero, and that there are enough lines after
1167
1168
 * first_line. */
void backup_lines(filestruct *first_line, size_t par_len)
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
{
    filestruct *top = first_line;
	/* The top of the paragraph we're backing up. */
    filestruct *bot = first_line;
	/* The bottom of the paragraph we're backing up. */
    size_t i;
	/* Generic loop variable. */
    size_t current_x_save = openfile->current_x;
    ssize_t fl_lineno_save = first_line->lineno;
    ssize_t edittop_lineno_save = openfile->edittop->lineno;
    ssize_t current_lineno_save = openfile->current->lineno;
1180
#ifndef NANO_TINY
1181
1182
1183
    bool old_mark_set = openfile->mark_set;
    ssize_t mb_lineno_save = 0;
    size_t mark_begin_x_save = 0;
1184
1185

    if (old_mark_set) {
1186
1187
	mb_lineno_save = openfile->mark_begin->lineno;
	mark_begin_x_save = openfile->mark_begin_x;
1188
1189
1190
    }
#endif

1191
1192
1193
    /* par_len will be one greater than the number of lines between
     * current and filebot if filebot is the last line in the
     * paragraph. */
1194
    assert(par_len > 0 && openfile->current->lineno + par_len <=
1195
	openfile->filebot->lineno + 1);
1196

1197
1198
1199
    /* Move bot down par_len lines to the line after the last line of
     * the paragraph, if there is one. */
    for (i = par_len; i > 0 && bot != openfile->filebot; i--)
1200
	bot = bot->next;
1201

1202
1203
    /* Move the paragraph from the current buffer's filestruct to the
     * justify buffer. */
1204
    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot,
1205
	(i == 1 && bot == openfile->filebot) ? strlen(bot->data) : 0);
1206

1207
1208
1209
    /* Copy the paragraph back to the current buffer's filestruct from
     * the justify buffer. */
    copy_from_filestruct(jusbuffer, jusbottom);
1210

1211
1212
1213
1214
    /* Move upward from the last line of the paragraph to the first
     * line, putting first_line, edittop, current, and mark_begin at the
     * same lines in the copied paragraph that they had in the original
     * paragraph. */
1215
    if (openfile->current != openfile->fileage)
1216
1217
1218
	top = openfile->current->prev;
    else
	top = openfile->current;
1219
    for (i = par_len; i > 0 && top != NULL; i--) {
1220
1221
1222
1223
1224
1225
	if (top->lineno == fl_lineno_save)
	    first_line = top;
	if (top->lineno == edittop_lineno_save)
	    openfile->edittop = top;
	if (top->lineno == current_lineno_save)
	    openfile->current = top;
1226
#ifndef NANO_TINY
1227
1228
1229
	if (old_mark_set && top->lineno == mb_lineno_save) {
	    openfile->mark_begin = top;
	    openfile->mark_begin_x = mark_begin_x_save;
1230
	}
1231
1232
1233
#endif
	top = top->prev;
    }
1234

1235
1236
1237
    /* Put current_x at the same place in the copied paragraph that it
     * had in the original paragraph. */
    openfile->current_x = current_x_save;
1238

1239
1240
    set_modified();
}
1241

1242
1243
1244
/* Find the beginning of the current paragraph if we're in one, or the
 * beginning of the next paragraph if we're not.  Afterwards, save the
 * quote length and paragraph length in *quote and *par.  Return TRUE if
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1245
 * we found a paragraph, and FALSE if there was an error or we didn't
1246
 * find a paragraph.
1247
1248
1249
 *
 * See the comment at begpar() for more about when a line is the
 * beginning of a paragraph. */
1250
bool find_paragraph(size_t *const quote, size_t *const par)
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
{
    size_t quote_len;
	/* Length of the initial quotation of the paragraph we search
	 * for. */
    size_t par_len;
	/* Number of lines in the paragraph we search for. */
    filestruct *current_save;
	/* The line at the beginning of the paragraph we search for. */
    ssize_t current_y_save;
	/* The y-coordinate at the beginning of the paragraph we search
	 * for. */
1262

1263
1264
1265
1266
#ifdef HAVE_REGEX_H
    if (quoterc != 0) {
	statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
	return FALSE;
1267
1268
1269
    }
#endif

1270
    assert(openfile->current != NULL);
1271

1272
1273
1274
1275
1276
1277
1278
    /* If we're at the end of the last line of the file, it means that
     * there aren't any paragraphs left, so get out. */
    if (openfile->current == openfile->filebot && openfile->current_x ==
	strlen(openfile->filebot->data))
	return FALSE;

    /* If the current line isn't in a paragraph, move forward to the
1279
     * last line of the next paragraph, if any. */
1280
1281
    if (!inpar(openfile->current)) {
	do_para_end(FALSE);
1282

1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
	/* If we end up past the beginning of the line, it means that
	 * we're at the end of the last line of the file, and the line
	 * isn't blank, in which case the last line of the file is the
	 * last line of the next paragraph.
	 *
	 * Otherwise, if we end up on a line that's in a paragraph, it
	 * means that we're on the line after the last line of the next
	 * paragraph, in which case we should move back to the last line
	 * of the next paragraph. */
	if (openfile->current_x == 0) {
	    if (!inpar(openfile->current->prev))
		return FALSE;
	    if (openfile->current != openfile->fileage)
		openfile->current = openfile->current->prev;
	}
1298
    }
1299

1300
    /* If the current line isn't the first line of the paragraph, move
1301
1302
     * back to the first line of the paragraph. */
    if (!begpar(openfile->current))
1303
	do_para_begin(FALSE);
1304

1305
1306
    /* Now current is the first line of the paragraph.  Set quote_len to
     * the quotation length of that line, and set par_len to the number
1307
     * of lines in this paragraph. */
1308
1309
1310
1311
1312
    quote_len = quote_length(openfile->current->data);
    current_save = openfile->current;
    current_y_save = openfile->current_y;
    do_para_end(FALSE);
    par_len = openfile->current->lineno - current_save->lineno;
1313

1314
1315
1316
1317
    /* If we end up past the beginning of the line, it means that we're
     * at the end of the last line of the file, and the line isn't
     * blank, in which case the last line of the file is part of the
     * paragraph. */
1318
1319
    if (openfile->current_x > 0)
	par_len++;
1320
1321
    openfile->current = current_save;
    openfile->current_y = current_y_save;
1322

1323
1324
    /* Save the values of quote_len and par_len. */
    assert(quote != NULL && par != NULL);
1325

1326
1327
    *quote = quote_len;
    *par = par_len;
1328

1329
    return TRUE;
1330
1331
}

1332
1333
1334
/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
 * the current paragraph. */
void do_justify(bool full_justify)
1335
{
1336
    filestruct *first_par_line = NULL;
1337
1338
	/* Will be the first line of the justified paragraph(s), if any.
	 * For restoring after unjustify. */
1339
    filestruct *last_par_line = NULL;
1340
	/* Will be the line after the last line of the justified
1341
	 * paragraph(s), if any.  Also for restoring after unjustify. */
1342
    bool filebot_inpar = FALSE;
1343
1344
	/* Whether the text at filebot is part of the current
	 * paragraph. */
1345

1346
1347
    /* We save these variables to be restored if the user
     * unjustifies. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1348
1349
    filestruct *edittop_save = openfile->edittop;
    filestruct *current_save = openfile->current;
1350
1351
1352
    size_t current_x_save = openfile->current_x;
    size_t pww_save = openfile->placewewant;
    size_t totsize_save = openfile->totsize;
1353
#ifndef NANO_TINY
1354
1355
1356
    filestruct *mark_begin_save = openfile->mark_begin;
    size_t mark_begin_x_save = openfile->mark_begin_x;
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1357
1358
    bool modified_save = openfile->modified;

1359
1360
    int kbinput;
    bool meta_key, func_key, s_or_t, ran_func, finished;
1361

1362
    /* Move to the beginning of the current line, so that justifying at
1363
1364
     * the end of the last line of the file, if that line isn't blank,
     * will work the first time through. */
1365
1366
    openfile->current_x = 0;

1367
1368
1369
    /* If we're justifying the entire file, start at the beginning. */
    if (full_justify)
	openfile->current = openfile->fileage;
1370

1371
1372
1373
    while (TRUE) {
	size_t i;
	    /* Generic loop variable. */
1374
1375
	filestruct *curr_first_par_line;
	    /* The first line of the current paragraph. */
1376
	size_t quote_len;
1377
1378
	    /* Length of the initial quotation of the current
	     * paragraph. */
1379
	size_t indent_len;
1380
1381
	    /* Length of the initial indentation of the current
	     * paragraph. */
1382
	size_t par_len;
1383
	    /* Number of lines in the current paragraph. */
1384
1385
1386
1387
	ssize_t break_pos;
	    /* Where we will break lines. */
	char *indent_string;
	    /* The first indentation that doesn't match the initial
1388
1389
1390
1391
1392
1393
1394
	     * indentation of the current paragraph.  This is put at the
	     * beginning of every line broken off the first justified
	     * line of the paragraph.  Note that this works because a
	     * paragraph can only contain two indentations at most: the
	     * initial one, and a different one starting on a line after
	     * the first.  See the comment at begpar() for more about
	     * when a line is part of a paragraph. */
1395

1396
1397
1398
1399
1400
1401
1402
	/* Find the first line of the paragraph to be justified.  That
	 * is the start of this paragraph if we're in one, or the start
	 * of the next otherwise.  Save the quote length and paragraph
	 * length (number of lines).  Don't refresh the screen yet,
	 * since we'll do that after we justify.
	 *
	 * If the search failed, we do one of two things.  If we're
1403
1404
	 * justifying the whole file, and we've found at least one
	 * paragraph, it means that we should justify all the way to the
1405
1406
1407
1408
	 * last line of the file, so set the last line of the text to be
	 * justified to the last line of the file and break out of the
	 * loop.  Otherwise, it means that there are no paragraph(s) to
	 * justify, so refresh the screen and get out. */
1409
	if (!find_paragraph(&quote_len, &par_len)) {
1410
	    if (full_justify && first_par_line != NULL) {
1411
		last_par_line = openfile->filebot;
1412
		break;
1413
1414
1415
1416
	    } else {
		edit_refresh();
		return;
	    }
1417
1418
	}

1419
1420
1421
1422
1423
1424
	/* par_len will be one greater than the number of lines between
	 * current and filebot if filebot is the last line in the
	 * paragraph.  Set filebot_inpar to TRUE if this is the case. */
	filebot_inpar = (openfile->current->lineno + par_len ==
		openfile->filebot->lineno + 1);

1425
1426
1427
1428
1429
1430
	/* If we haven't already done it, move the original paragraph(s)
	 * to the justify buffer, splice a copy of the original
	 * paragraph(s) into the file in the same place, and set
	 * first_par_line to the first line of the copy. */
	if (first_par_line == NULL) {
	    backup_lines(openfile->current, full_justify ?
1431
1432
		openfile->filebot->lineno - openfile->current->lineno +
		((openfile->filebot->data[0] != '\0') ? 1 : 0) :
1433
		par_len);
1434
1435
	    first_par_line = openfile->current;
	}
1436

1437
1438
1439
1440
	/* Set curr_first_par_line to the first line of the current
	 * paragraph. */
	curr_first_par_line = openfile->current;

1441
1442
	/* Initialize indent_string to a blank string. */
	indent_string = mallocstrcpy(NULL, "");
1443

1444
	/* Find the first indentation in the paragraph that doesn't
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1445
	 * match the indentation of the first line, and save it in
1446
1447
1448
1449
1450
	 * indent_string.  If all the indentations are the same, save
	 * the indentation of the first line in indent_string. */
	{
	    const filestruct *indent_line = openfile->current;
	    bool past_first_line = FALSE;
1451

1452
1453
1454
	    for (i = 0; i < par_len; i++) {
		indent_len = quote_len +
			indent_length(indent_line->data + quote_len);
1455

1456
1457
1458
1459
		if (indent_len != strlen(indent_string)) {
		    indent_string = mallocstrncpy(indent_string,
			indent_line->data, indent_len + 1);
		    indent_string[indent_len] = '\0';
1460

1461
1462
1463
		    if (past_first_line)
			break;
		}
1464

1465
1466
		if (indent_line == openfile->current)
		    past_first_line = TRUE;
1467

1468
1469
1470
		indent_line = indent_line->next;
	    }
	}
1471

1472
1473
1474
1475
1476
1477
1478
	/* Now tack all the lines of the paragraph together, skipping
	 * the quoting and indentation on all lines after the first. */
	for (i = 0; i < par_len - 1; i++) {
	    filestruct *next_line = openfile->current->next;
	    size_t line_len = strlen(openfile->current->data);
	    size_t next_line_len =
		strlen(openfile->current->next->data);
1479

1480
1481
1482
	    indent_len = quote_len +
		indent_length(openfile->current->next->data +
		quote_len);
1483

1484
1485
	    next_line_len -= indent_len;
	    openfile->totsize -= indent_len;
1486

1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
	    /* We're just about to tack the next line onto this one.  If
	     * this line isn't empty, make sure it ends in a space. */
	    if (line_len > 0 &&
		openfile->current->data[line_len - 1] != ' ') {
		line_len++;
		openfile->current->data =
			charealloc(openfile->current->data,
			line_len + 1);
		openfile->current->data[line_len - 1] = ' ';
		openfile->current->data[line_len] = '\0';
		openfile->totsize++;
	    }
1499

1500
1501
1502
1503
1504
	    openfile->current->data =
		charealloc(openfile->current->data, line_len +
		next_line_len + 1);
	    strcat(openfile->current->data, next_line->data +
		indent_len);
1505

1506
	    /* Don't destroy edittop or filebot! */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1507
	    if (next_line == openfile->edittop)
1508
		openfile->edittop = openfile->current;
1509
1510
	    if (next_line == openfile->filebot)
		openfile->filebot = openfile->current;
1511

1512
#ifndef NANO_TINY
1513
1514
1515
1516
1517
1518
1519
1520
	    /* Adjust the mark coordinates to compensate for the change
	     * in the next line. */
	    if (openfile->mark_set && openfile->mark_begin ==
		next_line) {
		openfile->mark_begin = openfile->current;
		openfile->mark_begin_x += line_len - indent_len;
	    }
#endif
1521

1522
1523
	    unlink_node(next_line);
	    delete_node(next_line);
1524

1525
1526
1527
	    /* If we've removed the next line, we need to go through
	     * this line again. */
	    i--;
1528

1529
1530
1531
	    par_len--;
	    openfile->totsize--;
	}
1532

1533
1534
1535
1536
1537
	/* Call justify_format() on the paragraph, which will remove
	 * excess spaces from it and change all blank characters to
	 * spaces. */
	justify_format(openfile->current, quote_len +
		indent_length(openfile->current->data + quote_len));
1538

1539
1540
	while (par_len > 0 && strlenpt(openfile->current->data) >
		fill) {
1541
	    size_t line_len = strlen(openfile->current->data);
1542

1543
	    indent_len = strlen(indent_string);
1544

1545
1546
	    /* If this line is too long, try to wrap it to the next line
	     * to make it short enough. */
1547
	    break_pos = break_line(openfile->current->data + indent_len,
1548
1549
1550
1551
1552
		fill - strnlenpt(openfile->current->data, indent_len)
#ifndef DISABLE_HELP
		, FALSE
#endif
		);
1553

1554
1555
1556
	    /* We can't break the line, or don't need to, so get out. */
	    if (break_pos == -1 || break_pos + indent_len == line_len)
		break;
1557

1558
1559
1560
	    /* Move forward to the character after the indentation and
	     * just after the space. */
	    break_pos += indent_len + 1;
1561

1562
	    assert(break_pos <= line_len);
1563

1564
1565
1566
1567
1568
1569
	    /* Make a new line, and copy the text after where we're
	     * going to break this line to the beginning of the new
	     * line. */
	    splice_node(openfile->current,
		make_new_node(openfile->current),
		openfile->current->next);
1570

1571
1572
1573
1574
	    /* If this paragraph is non-quoted, and autoindent isn't
	     * turned on, set the indentation length to zero so that the
	     * indentation is treated as part of the line. */
	    if (quote_len == 0
1575
#ifndef NANO_TINY
1576
		&& !ISSET(AUTOINDENT)
1577
#endif
1578
1579
		)
		indent_len = 0;
1580

1581
1582
1583
1584
1585
1586
1587
1588
	    /* Copy the text after where we're going to break the
	     * current line to the next line. */
	    openfile->current->next->data = charalloc(indent_len + 1 +
		line_len - break_pos);
	    strncpy(openfile->current->next->data, indent_string,
		indent_len);
	    strcpy(openfile->current->next->data + indent_len,
		openfile->current->data + break_pos);
1589

1590
1591
	    par_len++;
	    openfile->totsize += indent_len + 1;
1592

1593
#ifndef NANO_TINY
1594
1595
1596
1597
1598
1599
1600
	    /* Adjust the mark coordinates to compensate for the change
	     * in the current line. */
	    if (openfile->mark_set && openfile->mark_begin ==
		openfile->current && openfile->mark_begin_x >
		break_pos) {
		openfile->mark_begin = openfile->current->next;
		openfile->mark_begin_x -= break_pos - indent_len;
1601
	    }
1602
#endif
1603

1604
1605
	    /* Break the current line. */
	    null_at(&openfile->current->data, break_pos);
1606

1607
	    /* If the current line is the last line of the file, move
1608
	     * the last line of the file down to the next line. */
1609
1610
1611
	    if (openfile->filebot == openfile->current)
		openfile->filebot = openfile->filebot->next;

1612
1613
	    /* Go to the next line. */
	    par_len--;
1614
1615
	    openfile->current_y++;
	    openfile->current = openfile->current->next;
1616
	}
1617

1618
1619
1620
	/* We're done breaking lines, so we don't need indent_string
	 * anymore. */
	free(indent_string);
1621

1622
1623
	/* Go to the next line, if possible.  If there is no next line,
	 * move to the end of the current line. */
1624
1625
1626
1627
1628
	if (openfile->current != openfile->filebot) {
	    openfile->current_y++;
	    openfile->current = openfile->current->next;
	} else
	    openfile->current_x = strlen(openfile->current->data);
1629

1630
1631
1632
1633
	/* Renumber the lines of the now-justified current paragraph,
	 * since both find_paragraph() and edit_refresh() need the line
	 * numbers to be right. */
	renumber(curr_first_par_line);
1634
1635
1636
1637
1638

	/* We've just finished justifying the paragraph.  If we're not
	 * justifying the entire file, break out of the loop.
	 * Otherwise, continue the loop so that we justify all the
	 * paragraphs in the file. */
1639
1640
	if (!full_justify)
	    break;
1641
1642
    }

1643
    /* We are now done justifying the paragraph or the file, so clean
1644
     * up.  current_y and totsize have been maintained above.  If we
1645
1646
1647
     * actually justified something, set last_par_line to the new end of
     * the paragraph. */
    if (first_par_line != NULL)
1648
	last_par_line = openfile->current;
1649

1650
    edit_refresh();
1651

1652
#ifndef NANO_TINY
1653
1654
1655
1656
    /* We're going to set jump_buf so that we return here after a
     * SIGWINCH instead of to main().  Indicate this. */
    jump_buf_main = FALSE;

1657
    /* Return here after a SIGWINCH. */
1658
    sigsetjmp(jump_buf, 1);
1659
1660
#endif

1661
    statusbar(_("Can now UnJustify!"));
1662

1663
1664
1665
1666
    /* If constant cursor position display is on, make sure the current
     * cursor position will be properly displayed on the statusbar. */
    if (ISSET(CONST_UPDATE))
	do_cursorpos(TRUE);
1667

1668
1669
1670
    /* Display the shortcut list with UnJustify. */
    shortcut_init(TRUE);
    display_main_list();
1671

1672
1673
1674
1675
    /* Now get a keystroke and see if it's unjustify.  If not, put back
     * the keystroke and return. */
    kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func,
	&finished, FALSE);
1676

1677
    if (s_or_t && kbinput == NANO_UNJUSTIFY_KEY) {
1678
1679
1680
1681
	/* Splice the justify buffer back into the file, but only if we
	 * actually justified something. */
	if (first_par_line != NULL) {
	    filestruct *top_save;
1682

1683
1684
1685
	    /* Partition the filestruct so that it contains only the
	     * text of the justified paragraph. */
	    filepart = partition_filestruct(first_par_line, 0,
1686
1687
		last_par_line, filebot_inpar ?
		strlen(last_par_line->data) : 0);
1688

1689
	    /* Remove the text of the justified paragraph, and
1690
	     * replace it with the text in the justify buffer. */
1691
1692
1693
	    free_filestruct(openfile->fileage);
	    openfile->fileage = jusbuffer;
	    openfile->filebot = jusbottom;
1694

1695
	    top_save = openfile->fileage;
1696

1697
1698
1699
1700
	    /* Unpartition the filestruct so that it contains all the
	     * text again.  Note that the justified paragraph has been
	     * replaced with the unjustified paragraph. */
	    unpartition_filestruct(&filepart);
1701

1702
1703
1704
	     /* Renumber starting with the beginning line of the old
	      * partition. */
	    renumber(top_save);
1705

1706
1707
1708
1709
1710
	    /* Restore the justify we just did (ungrateful user!). */
	    openfile->edittop = edittop_save;
	    openfile->current = current_save;
	    openfile->current_x = current_x_save;
	    openfile->placewewant = pww_save;
1711
	    openfile->totsize = totsize_save;
1712
#ifndef NANO_TINY
1713
1714
1715
1716
1717
1718
	    if (openfile->mark_set) {
		openfile->mark_begin = mark_begin_save;
		openfile->mark_begin_x = mark_begin_x_save;
	    }
#endif
	    openfile->modified = modified_save;
1719

1720
1721
	    /* Clear the justify buffer. */
	    jusbuffer = NULL;
1722

1723
1724
1725
1726
1727
1728
	    if (!openfile->modified)
		titlebar(NULL);
	    edit_refresh();
	}
    } else {
	unget_kbinput(kbinput, meta_key, func_key);
1729

1730
1731
1732
1733
	/* Blow away the text in the justify buffer. */
	free_filestruct(jusbuffer);
	jusbuffer = NULL;
    }
1734

1735
1736
1737
1738
1739
    blank_statusbar();

    /* Display the shortcut list with UnCut. */
    shortcut_init(FALSE);
    display_main_list();
1740
1741
}

1742
/* Justify the current paragraph. */
1743
void do_justify_void(void)
1744
{
1745
1746
    do_justify(FALSE);
}
1747

1748
/* Justify the entire file. */
1749
1750
1751
void do_full_justify(void)
{
    do_justify(TRUE);
1752
}
1753
#endif /* !DISABLE_JUSTIFY */
1754

1755
1756
1757
1758
#ifndef DISABLE_SPELLER
/* A word is misspelled in the file.  Let the user replace it.  We
 * return FALSE if the user cancels. */
bool do_int_spell_fix(const char *word)
1759
{
1760
1761
1762
1763
1764
1765
1766
1767
1768
    char *save_search, *save_replace;
    size_t match_len, current_x_save = openfile->current_x;
    size_t pww_save = openfile->placewewant;
    filestruct *edittop_save = openfile->edittop;
    filestruct *current_save = openfile->current;
	/* Save where we are. */
    bool canceled = FALSE;
	/* The return value. */
    bool case_sens_set = ISSET(CASE_SENSITIVE);
1769
#ifndef NANO_TINY
1770
1771
1772
1773
1774
    bool backwards_search_set = ISSET(BACKWARDS_SEARCH);
#endif
#ifdef HAVE_REGEX_H
    bool regexp_set = ISSET(USE_REGEXP);
#endif
1775
#ifndef NANO_TINY
1776
    bool old_mark_set = openfile->mark_set;
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
    bool added_magicline = FALSE;
	/* Whether we added a magicline after filebot. */
    bool right_side_up = FALSE;
	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
	 * FALSE if (current, current_x) is. */
    filestruct *top, *bot;
    size_t top_x, bot_x;
#endif

    /* Make sure spell-check is case sensitive. */
    SET(CASE_SENSITIVE);
1788

1789
#ifndef NANO_TINY
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
    /* Make sure spell-check goes forward only. */
    UNSET(BACKWARDS_SEARCH);
#endif
#ifdef HAVE_REGEX_H
    /* Make sure spell-check doesn't use regular expressions. */
    UNSET(USE_REGEXP);
#endif

    /* Save the current search/replace strings. */
    search_init_globals();
    save_search = last_search;
    save_replace = last_replace;

    /* Set the search/replace strings to the misspelled word. */
    last_search = mallocstrcpy(NULL, word);
    last_replace = mallocstrcpy(NULL, word);

1807
#ifndef NANO_TINY
1808
    if (old_mark_set) {
1809
	/* If the mark is on, partition the filestruct so that it
1810
1811
1812
1813
	 * contains only the marked text; if the NO_NEWLINES flag isn't
	 * set, keep track of whether the text will have a magicline
	 * added when we're done correcting misspelled words; and
	 * turn the mark off. */
1814
1815
1816
	mark_order((const filestruct **)&top, &top_x,
	    (const filestruct **)&bot, &bot_x, &right_side_up);
	filepart = partition_filestruct(top, top_x, bot, bot_x);
1817
1818
	if (!ISSET(NO_NEWLINES))
	    added_magicline = (openfile->filebot->data[0] != '\0');
1819
	openfile->mark_set = FALSE;
1820
1821
1822
    }
#endif

1823
1824
1825
1826
1827
    /* Start from the top of the file. */
    openfile->edittop = openfile->fileage;
    openfile->current = openfile->fileage;
    openfile->current_x = (size_t)-1;
    openfile->placewewant = 0;
1828

1829
    /* Find the first whole occurrence of word. */
1830
    findnextstr_wrap_reset();
1831
    while (findnextstr(TRUE, FALSE, openfile->fileage, 0, word,
1832
1833
1834
1835
1836
1837
1838
	&match_len)) {
	if (is_whole_word(openfile->current_x, openfile->current->data,
		word)) {
	    size_t xpt = xplustabs();
	    char *exp_word = display_string(openfile->current->data,
		xpt, strnlenpt(openfile->current->data,
		openfile->current_x + match_len) - xpt, FALSE);
1839

1840
	    edit_refresh();
1841

1842
1843
1844
	    do_replace_highlight(TRUE, exp_word);

	    /* Allow all instances of the word to be corrected. */
1845
1846
1847
1848
1849
	    canceled = (do_prompt(FALSE,
#ifndef DISABLE_TABCOMP
		TRUE,
#endif
		spell_list, word,
1850
#ifndef NANO_TINY
1851
		NULL,
1852
#endif
1853
		edit_refresh, _("Edit a replacement")) == -1);
1854

1855
	    do_replace_highlight(FALSE, exp_word);
1856

1857
	    free(exp_word);
1858

1859
1860
	    if (!canceled && strcmp(word, answer) != 0) {
		openfile->current_x--;
1861
1862
		do_replace_loop(TRUE, &canceled, openfile->current,
			&openfile->current_x, word);
1863
	    }
1864

1865
1866
	    break;
	}
1867
1868
    }

1869
#ifndef NANO_TINY
1870
    if (old_mark_set) {
1871
1872
1873
	/* If the mark was on, the NO_NEWLINES flag isn't set, and we
	 * added a magicline, remove it now. */
	if (!ISSET(NO_NEWLINES) && added_magicline)
1874
	    remove_magicline();
1875

1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
	/* Put the beginning and the end of the mark at the beginning
	 * and the end of the spell-checked text. */
	if (openfile->fileage == openfile->filebot)
	    bot_x += top_x;
	if (right_side_up) {
	    openfile->mark_begin_x = top_x;
	    current_x_save = bot_x;
	} else {
	    current_x_save = top_x;
	    openfile->mark_begin_x = bot_x;
	}
1887

1888
1889
1890
1891
	/* Unpartition the filestruct so that it contains all the text
	 * again, and turn the mark back on. */
	unpartition_filestruct(&filepart);
	openfile->mark_set = TRUE;
1892
    }
1893
#endif
1894

1895
1896
1897
1898
1899
1900
1901
1902
    /* Restore the search/replace strings. */
    free(last_search);
    last_search = save_search;
    free(last_replace);
    last_replace = save_replace;

    /* Restore where we were. */
    openfile->edittop = edittop_save;
1903
    openfile->current = current_save;
1904
1905
    openfile->current_x = current_x_save;
    openfile->placewewant = pww_save;
1906

1907
1908
1909
    /* Restore case sensitivity setting. */
    if (!case_sens_set)
	UNSET(CASE_SENSITIVE);
1910

1911
#ifndef NANO_TINY
1912
1913
1914
1915
1916
1917
1918
1919
1920
    /* Restore search/replace direction. */
    if (backwards_search_set)
	SET(BACKWARDS_SEARCH);
#endif
#ifdef HAVE_REGEX_H
    /* Restore regular expression usage setting. */
    if (regexp_set)
	SET(USE_REGEXP);
#endif
1921

1922
    return !canceled;
1923
1924
}

1925
1926
1927
/* Internal (integrated) spell checking using the spell program,
 * filtered through the sort and uniq programs.  Return NULL for normal
 * termination, and the error string otherwise. */
1928
const char *do_int_speller(const char *tempfile_name)
1929
{
1930
1931
1932
1933
1934
    char *read_buff, *read_buff_ptr, *read_buff_word;
    size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
    int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
    pid_t pid_spell, pid_sort, pid_uniq;
    int spell_status, sort_status, uniq_status;
1935

1936
1937
1938
1939
    /* Create all three pipes up front. */
    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
	pipe(uniq_fd) == -1)
	return _("Could not create pipe");
1940

1941
    statusbar(_("Creating misspelled word list, please wait..."));
1942

1943
1944
1945
1946
    /* A new process to run spell in. */
    if ((pid_spell = fork()) == 0) {
	/* Child continues (i.e, future spell process). */
	close(spell_fd[0]);
1947

1948
1949
1950
	/* Replace the standard input with the temp file. */
	if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
	    goto close_pipes_and_exit;
1951

1952
1953
	if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;
1954

1955
	close(tempfile_fd);
1956

1957
1958
1959
	/* Send spell's standard output to the pipe. */
	if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
1960

1961
1962
	close(spell_fd[1]);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1963
	/* Start the spell program; we are using $PATH. */
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
	execlp("spell", "spell", NULL);

	/* This should not be reached if spell is found. */
	exit(1);
    }

    /* Parent continues here. */
    close(spell_fd[1]);

    /* A new process to run sort in. */
    if ((pid_sort = fork()) == 0) {
	/* Child continues (i.e, future spell process).  Replace the
	 * standard input with the standard output of the old pipe. */
	if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;

	close(spell_fd[0]);

	/* Send sort's standard output to the new pipe. */
	if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;

	close(sort_fd[1]);

	/* Start the sort program.  Use -f to remove mixed case.  If
	 * this isn't portable, let me know. */
	execlp("sort", "sort", "-f", NULL);

	/* This should not be reached if sort is found. */
	exit(1);
    }
1995

1996
1997
    close(spell_fd[0]);
    close(sort_fd[1]);
1998

1999
2000
2001
2002
2003
2004
    /* A new process to run uniq in. */
    if ((pid_uniq = fork()) == 0) {
	/* Child continues (i.e, future uniq process).  Replace the
	 * standard input with the standard output of the old pipe. */
	if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;
2005

2006
	close(sort_fd[0]);
2007

2008
2009
2010
	/* Send uniq's standard output to the new pipe. */
	if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
2011

2012
	close(uniq_fd[1]);
2013

2014
2015
	/* Start the uniq program; we are using PATH. */
	execlp("uniq", "uniq", NULL);
2016

2017
2018
2019
	/* This should not be reached if uniq is found. */
	exit(1);
    }
2020

2021
2022
    close(sort_fd[0]);
    close(uniq_fd[1]);
2023

2024
2025
2026
2027
2028
    /* The child process was not forked successfully. */
    if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
	close(uniq_fd[0]);
	return _("Could not fork");
    }
2029

2030
2031
2032
2033
2034
    /* Get the system pipe buffer size. */
    if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
	close(uniq_fd[0]);
	return _("Could not get size of pipe buffer");
    }
2035

2036
2037
2038
2039
    /* Read in the returned spelling errors. */
    read_buff_read = 0;
    read_buff_size = pipe_buff_size + 1;
    read_buff = read_buff_ptr = charalloc(read_buff_size);
2040

2041
2042
2043
2044
2045
2046
2047
2048
    while ((bytesread = read(uniq_fd[0], read_buff_ptr,
	pipe_buff_size)) > 0) {
	read_buff_read += bytesread;
	read_buff_size += pipe_buff_size;
	read_buff = read_buff_ptr = charealloc(read_buff,
		read_buff_size);
	read_buff_ptr += read_buff_read;
    }
2049

2050
2051
    *read_buff_ptr = '\0';
    close(uniq_fd[0]);
2052

2053
2054
    /* Process the spelling errors. */
    read_buff_word = read_buff_ptr = read_buff;
2055

2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
    while (*read_buff_ptr != '\0') {
	if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) {
	    *read_buff_ptr = '\0';
	    if (read_buff_word != read_buff_ptr) {
		if (!do_int_spell_fix(read_buff_word)) {
		    read_buff_word = read_buff_ptr;
		    break;
		}
	    }
	    read_buff_word = read_buff_ptr + 1;
2066
	}
2067
2068
	read_buff_ptr++;
    }
2069

2070
2071
2072
    /* Special case: the last word doesn't end with '\r' or '\n'. */
    if (read_buff_word != read_buff_ptr)
	do_int_spell_fix(read_buff_word);
2073

2074
    free(read_buff);
2075
    search_replace_abort();
2076
    edit_refresh();
2077

2078
2079
2080
2081
    /* Process the end of the spell process. */
    waitpid(pid_spell, &spell_status, 0);
    waitpid(pid_sort, &sort_status, 0);
    waitpid(pid_uniq, &uniq_status, 0);
2082

2083
2084
    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
	return _("Error invoking \"spell\"");
2085

2086
2087
    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
	return _("Error invoking \"sort -f\"");
2088

2089
2090
    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
	return _("Error invoking \"uniq\"");
2091

2092
2093
    /* Otherwise... */
    return NULL;
2094

2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
  close_pipes_and_exit:
    /* Don't leak any handles. */
    close(tempfile_fd);
    close(spell_fd[0]);
    close(spell_fd[1]);
    close(sort_fd[0]);
    close(sort_fd[1]);
    close(uniq_fd[0]);
    close(uniq_fd[1]);
    exit(1);
}
2106

2107
2108
/* External (alternate) spell checking.  Return NULL for normal
 * termination, and the error string otherwise. */
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
const char *do_alt_speller(char *tempfile_name)
{
    int alt_spell_status;
    size_t current_x_save = openfile->current_x;
    size_t pww_save = openfile->placewewant;
    ssize_t current_y_save = openfile->current_y;
    ssize_t lineno_save = openfile->current->lineno;
    pid_t pid_spell;
    char *ptr;
    static int arglen = 3;
    static char **spellargs = NULL;
2120
#ifndef NANO_TINY
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
    bool old_mark_set = openfile->mark_set;
    bool added_magicline = FALSE;
	/* Whether we added a magicline after filebot. */
    bool right_side_up = FALSE;
	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
	 * FALSE if (current, current_x) is. */
    filestruct *top, *bot;
    size_t top_x, bot_x;
    ssize_t mb_lineno_save = 0;
	/* We're going to close the current file, and open the output of
	 * the alternate spell command.  The line that mark_begin points
	 * to will be freed, so we save the line number and restore it
	 * afterwards. */
    size_t totsize_save = openfile->totsize;
	/* Our saved value of totsize, used when we spell-check a marked
	 * selection. */
2137

2138
2139
2140
2141
2142
2143
    if (old_mark_set) {
	/* If the mark is on, save the number of the line it starts on,
	 * and then turn the mark off. */
	mb_lineno_save = openfile->mark_begin->lineno;
	openfile->mark_set = FALSE;
    }
2144
2145
#endif

2146
    endwin();
2147

2148
2149
2150
    /* Set up an argument list to pass execvp(). */
    if (spellargs == NULL) {
	spellargs = (char **)nmalloc(arglen * sizeof(char *));
2151

2152
2153
2154
2155
2156
2157
2158
2159
	spellargs[0] = strtok(alt_speller, " ");
	while ((ptr = strtok(NULL, " ")) != NULL) {
	    arglen++;
	    spellargs = (char **)nrealloc(spellargs, arglen *
		sizeof(char *));
	    spellargs[arglen - 3] = ptr;
	}
	spellargs[arglen - 1] = NULL;
2160
    }
2161
    spellargs[arglen - 2] = tempfile_name;
2162

2163
2164
    /* Start a new process for the alternate speller. */
    if ((pid_spell = fork()) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2165
	/* Start alternate spell program; we are using $PATH. */
2166
2167
2168
2169
	execvp(spellargs[0], spellargs);

	/* Should not be reached, if alternate speller is found!!! */
	exit(1);
2170
2171
    }

2172
2173
2174
    /* If we couldn't fork, get out. */
    if (pid_spell < 0)
	return _("Could not fork");
2175

2176
#ifndef NANO_TINY
2177
    /* Don't handle a pending SIGWINCH until the alternate spell checker
2178
     * is finished and we've loaded the spell-checked file back in. */
2179
2180
2181
2182
    allow_pending_sigwinch(FALSE);
#endif

    /* Wait for the alternate spell checker to finish. */
2183
    wait(&alt_spell_status);
2184

2185
2186
    /* Reenter curses mode. */
    doupdate();
2187

2188
2189
    /* Restore the terminal to its previous state. */
    terminal_init();
2190

2191
2192
    /* Turn the cursor back on for sure. */
    curs_set(1);
2193

2194
2195
2196
2197
    /* The screen might have been resized.  If it has, reinitialize all
     * the windows based on the new screen dimensions. */
    window_init();

2198
2199
2200
2201
    if (!WIFEXITED(alt_spell_status) ||
		WEXITSTATUS(alt_spell_status) != 0) {
	char *altspell_error;
	char *invoke_error = _("Error invoking \"%s\"");
2202

2203
#ifndef NANO_TINY
2204
2205
2206
	/* Turn the mark back on if it was on before. */
	openfile->mark_set = old_mark_set;
#endif
2207

2208
2209
2210
2211
2212
2213
	altspell_error =
		charalloc(strlen(invoke_error) +
		strlen(alt_speller) + 1);
	sprintf(altspell_error, invoke_error, alt_speller);
	return altspell_error;
    }
2214

2215
#ifndef NANO_TINY
2216
    if (old_mark_set) {
2217
2218
2219
2220
2221
	/* If the mark is on, partition the filestruct so that it
	 * contains only the marked text; if the NO_NEWLINES flag isn't
	 * set, keep track of whether the text will have a magicline
	 * added when we're done correcting misspelled words; and
	 * turn the mark off. */
2222
2223
2224
	mark_order((const filestruct **)&top, &top_x,
		(const filestruct **)&bot, &bot_x, &right_side_up);
	filepart = partition_filestruct(top, top_x, bot, bot_x);
2225
2226
	if (!ISSET(NO_NEWLINES))
	    added_magicline = (openfile->filebot->data[0] != '\0');
2227

2228
2229
2230
2231
2232
	/* Get the number of characters in the marked text, and subtract
	 * it from the saved value of totsize. */
	totsize_save -= get_totsize(top, bot);
    }
#endif
2233

2234
2235
2236
    /* Replace the text of the current buffer with the spell-checked
     * text. */
    replace_buffer(tempfile_name);
2237

2238
#ifndef NANO_TINY
2239
2240
    if (old_mark_set) {
	filestruct *top_save = openfile->fileage;
2241

2242
2243
2244
	/* If the mark was on, the NO_NEWLINES flag isn't set, and we
	 * added a magicline, remove it now. */
	if (!ISSET(NO_NEWLINES) && added_magicline)
2245
	    remove_magicline();
2246

2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
	/* Put the beginning and the end of the mark at the beginning
	 * and the end of the spell-checked text. */
	if (openfile->fileage == openfile->filebot)
	    bot_x += top_x;
	if (right_side_up) {
	    openfile->mark_begin_x = top_x;
	    current_x_save = bot_x;
	} else {
	    current_x_save = top_x;
	    openfile->mark_begin_x = bot_x;
2257
2258
	}

2259
2260
2261
2262
2263
2264
2265
	/* Unpartition the filestruct so that it contains all the text
	 * again.  Note that we've replaced the marked text originally
	 * in the partition with the spell-checked marked text in the
	 * temp file. */
	unpartition_filestruct(&filepart);

	/* Renumber starting with the beginning line of the old
2266
2267
2268
	 * partition.  Also add the number of characters in the
	 * spell-checked marked text to the saved value of totsize, and
	 * then make that saved value the actual value. */
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
	renumber(top_save);
	totsize_save += openfile->totsize;
	openfile->totsize = totsize_save;

	/* Assign mark_begin to the line where the mark began before. */
	do_gotopos(mb_lineno_save, openfile->mark_begin_x,
		current_y_save, 0);
	openfile->mark_begin = openfile->current;

	/* Assign mark_begin_x to the location in mark_begin where the
	 * mark began before, adjusted for any shortening of the
	 * line. */
	openfile->mark_begin_x = openfile->current_x;

	/* Turn the mark back on. */
	openfile->mark_set = TRUE;
2285
    }
2286
#endif
2287

2288
2289
2290
    /* Go back to the old position, and mark the file as modified. */
    do_gotopos(lineno_save, current_x_save, current_y_save, pww_save);
    set_modified();
2291

2292
#ifndef NANO_TINY
2293
2294
2295
2296
    /* Handle a pending SIGWINCH again. */
    allow_pending_sigwinch(TRUE);
#endif

2297
    return NULL;
2298
2299
}

2300
2301
/* Spell check the current file.  If an alternate spell checker is
 * specified, use it.  Otherwise, use the internal spell checker. */
2302
void do_spell(void)
2303
{
2304
2305
2306
2307
    int i;
    FILE *temp_file;
    char *temp = safe_tempfile(&temp_file);
    const char *spell_msg;
2308

2309
    if (temp == NULL) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2310
	statusbar(_("Error writing temp file: %s"), strerror(errno));
2311
2312
2313
	return;
    }

2314
#ifndef NANO_TINY
2315
    if (openfile->mark_set)
2316
	i = write_marked_file(temp, temp_file, TRUE, OVERWRITE);
2317
2318
    else
#endif
2319
	i = write_file(temp, temp_file, TRUE, OVERWRITE, FALSE);
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331

    if (i == -1) {
	statusbar(_("Error writing temp file: %s"), strerror(errno));
	free(temp);
	return;
    }

    spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) :
	do_int_speller(temp);
    unlink(temp);
    free(temp);

2332
2333
    currshortcut = main_list;

2334
2335
    /* If the spell-checker printed any error messages onscreen, make
     * sure that they're cleared off. */
2336
    total_refresh();
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346

    if (spell_msg != NULL) {
	if (errno == 0)
	    /* Don't display an error message of "Success". */
	    statusbar(_("Spell checking failed: %s"), spell_msg);
	else
	    statusbar(_("Spell checking failed: %s: %s"), spell_msg,
		strerror(errno));
    } else
	statusbar(_("Finished checking spelling"));
2347
}
2348
#endif /* !DISABLE_SPELLER */
2349

2350
#ifndef NANO_TINY
2351
2352
/* Our own version of "wc".  Note that its character counts are in
 * multibyte characters instead of single-byte characters. */
2353
void do_wordlinechar_count(void)
2354
{
2355
2356
    size_t words = 0, chars = 0;
    ssize_t lines = 0;
2357
    size_t current_x_save = openfile->current_x;
2358
2359
2360
2361
2362
2363
2364
2365
    size_t pww_save = openfile->placewewant;
    filestruct *current_save = openfile->current;
    bool old_mark_set = openfile->mark_set;
    filestruct *top, *bot;
    size_t top_x, bot_x;

    if (old_mark_set) {
	/* If the mark is on, partition the filestruct so that it
2366
	 * contains only the marked text, and turn the mark off. */
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
	mark_order((const filestruct **)&top, &top_x,
	    (const filestruct **)&bot, &bot_x, NULL);
	filepart = partition_filestruct(top, top_x, bot, bot_x);
	openfile->mark_set = FALSE;
    }

    /* Start at the top of the file. */
    openfile->current = openfile->fileage;
    openfile->current_x = 0;
    openfile->placewewant = 0;

    /* Keep moving to the next word (counting punctuation characters as
2379
2380
2381
     * part of a word, as "wc -w" does), without updating the screen,
     * until we reach the end of the file, incrementing the total word
     * count whenever we're on a word just before moving. */
2382
    while (openfile->current != openfile->filebot ||
2383
	openfile->current->data[openfile->current_x] != '\0') {
2384
2385
2386
2387
	if (do_next_word(TRUE, FALSE))
	    words++;
    }

2388
2389
    /* Get the total line and character counts, as "wc -l"  and "wc -c"
     * do, but get the latter in multibyte characters. */
2390
    if (old_mark_set) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2391
2392
	lines = openfile->filebot->lineno -
		openfile->fileage->lineno + 1;
2393
2394
	chars = get_totsize(openfile->fileage, openfile->filebot);

2395
2396
2397
2398
	/* Unpartition the filestruct so that it contains all the text
	 * again, and turn the mark back on. */
	unpartition_filestruct(&filepart);
	openfile->mark_set = TRUE;
2399
    } else {
2400
	lines = openfile->filebot->lineno;
2401
	chars = openfile->totsize;
2402
2403
2404
2405
2406
2407
2408
    }

    /* Restore where we were. */
    openfile->current = current_save;
    openfile->current_x = current_x_save;
    openfile->placewewant = pww_save;

2409
2410
    /* Display the total word, line, and character counts on the
     * statusbar. */
2411
    statusbar(_("%sWords: %lu  Lines: %ld  Chars: %lu"), old_mark_set ?
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2412
	_("In Selection:  ") : "", (unsigned long)words, (long)lines,
2413
	(unsigned long)chars);
2414
}
2415
#endif /* !NANO_TINY */
2416

2417
/* Get verbatim input. */
2418
2419
2420
2421
2422
2423
void do_verbatim_input(void)
{
    int *kbinput;
    size_t kbinput_len, i;
    char *output;

2424
2425
    /* TRANSLATORS: This is displayed when the next keystroke will be
     * inserted verbatim. */
2426
2427
2428
2429
2430
    statusbar(_("Verbatim Input"));

    /* Read in all the verbatim characters. */
    kbinput = get_verbatim_kbinput(edit, &kbinput_len);

2431
2432
2433
2434
2435
2436
2437
2438
2439
    /* If constant cursor position display is on, make sure the current
     * cursor position will be properly displayed on the statusbar.
     * Otherwise, blank the statusbar. */
    if (ISSET(CONST_UPDATE))
	do_cursorpos(TRUE);
    else {
	blank_statusbar();
	wnoutrefresh(bottomwin);
    }
2440

2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
    /* Display all the verbatim characters at once, not filtering out
     * control characters. */
    output = charalloc(kbinput_len + 1);

    for (i = 0; i < kbinput_len; i++)
	output[i] = (char)kbinput[i];
    output[i] = '\0';

    do_output(output, kbinput_len, TRUE);

    free(output);
}