text.c 87.3 KB
Newer Older
1
/* $Id$ */ 
2
/**************************************************************************
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3
 *   text.c                                                               *
4
 *                                                                        *
5
6
 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007   *
 *   Free Software Foundation, Inc.                                       *
7
8
 *   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 *
9
 *   the Free Software Foundation; either version 3, or (at your option)  *
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *   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
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 {
58
	statusbar(_("Mark Unset"));
59
60
61
62
63
	openfile->mark_begin = NULL;
	openfile->mark_begin_x = 0;
	edit_refresh();
    }
}
64
#endif /* !NANO_TINY */
65

66
/* Delete the character under the cursor. */
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
#ifndef NANO_TINY
74
    update_undo(DEL);
75
76
#endif

77
78
79
80
81
82
    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 +
83
		openfile->current_x, NULL, NULL);
84
85
86
87
88
89
90
91
92
93
94
95
	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);
96
#ifndef NANO_TINY
97
98
99
100
101
102
	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--;
103
    } else if (openfile->current != openfile->filebot) {
104
105
106
107
108
109
110
111
112
113
114
115
116
	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);
117
#ifndef NANO_TINY
118
119
120
121
122
123
124
125
126
127
128
129
130
	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--;
131

132
133
	/* 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
134
	 * before filebot, add a new magicline. */
135
136
	if (!ISSET(NO_NEWLINES) && openfile->current ==
		openfile->filebot && openfile->current->data[0] != '\0')
137
	    new_magicline();
138
139
140
    } else
	return;

141
    set_modified();
142
143
144
145
146
147
148
149
150
151
152
153
154
155

#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
156
/* Backspace over one character.  That is, move the cursor left one
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
157
 * character, and then delete the character under the cursor. */
158
159
160
161
void do_backspace(void)
{
    if (openfile->current != openfile->fileage ||
	openfile->current_x > 0) {
162
	do_left();
163
164
165
166
	do_delete();
    }
}

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

	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);
192
#ifndef NANO_TINY
193
194
195
196
    }
#endif
}

197
#ifndef NANO_TINY
198
199
200
201
202
/* Indent or unindent the current line (or, if the mark is on, all lines
 * covered by the mark) len columns, depending on whether len is
 * positive or negative.  If the TABS_TO_SPACES flag is set, indent or
 * unindent by len spaces.  Otherwise, indent or unindent by (len /
 * tabsize) tabs and (len % tabsize) spaces. */
203
void do_indent(ssize_t cols)
204
205
206
207
208
{
    bool indent_changed = FALSE;
	/* Whether any indenting or unindenting was done. */
    bool unindent = FALSE;
	/* Whether we're unindenting text. */
209
    char *line_indent = NULL;
210
	/* The text added to each line in order to indent it. */
211
    size_t line_indent_len = 0;
212
213
214
215
216
217
218
	/* 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
219
220
    /* If cols is zero, get out. */
    if (cols == 0)
221
222
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
223
224
225
226
    /* If cols is negative, make it positive and set unindent to
     * TRUE. */
    if (cols < 0) {
	cols = -cols;
227
228
229
230
231
232
	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;

233
234
235
236
237
238
239
240
241
    /* 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;
    }
242

243
244
245
    if (!unindent) {
	/* Set up the text we'll be using as indentation. */
	line_indent = charalloc(cols + 1);
246

247
248
249
250
251
252
253
254
255
	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;
256

257
258
	    charset(line_indent, '\t', num_tabs);
	    charset(line_indent + num_tabs, ' ', num_spaces);
259

260
261
	    line_indent_len = num_tabs + num_spaces;
	}
262

263
264
	line_indent[line_indent_len] = '\0';
    }
265

266
    /* Go through each line of the text. */
267
268
    for (f = top; f != bot->next; f = f->next) {
	size_t line_len = strlen(f->data);
269
	size_t indent_len = indent_length(f->data);
270

271
272
273
274
275
276
277
278
279
280
281
	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. */
282
283
	    if (openfile->mark_set && f == openfile->mark_begin &&
		openfile->mark_begin_x >= indent_len)
284
285
286
287
288
289
290
291
292
293
294
		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 {
295
	    size_t indent_col = strnlenpt(f->data, indent_len);
296
297
		/* The length in columns of the indentation on this
		 * line. */
298

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
308
		/* If we're unindenting, and there's at least cols
309
310
311
312
313
314
		 * 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;
315

316
		/* Keep track of the change in the current line. */
317
		if (openfile->mark_set && f == openfile->mark_begin &&
318
319
320
321
322
323
			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;
		}
324

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
325
		if (f == openfile->current && openfile->current_x >
326
327
328
329
330
331
			indent_new) {
		    if (openfile->current_x <= indent_len)
			openfile->current_x = indent_new;
		    else
			openfile->current_x -= indent_shift;
		}
332

333
334
335
336
337
338
339
		/* We've unindented, so set indent_changed to TRUE. */
		if (!indent_changed)
		    indent_changed = TRUE;
	    }
	}
    }

340
    if (!unindent)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
341
	/* Clean up. */
342
	free(line_indent);
343
344
345
346
347
348
349
350
351
352

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

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

353
354
355
/* Indent the current line, or all lines covered by the mark if the mark
 * is on, tabsize columns. */
void do_indent_void(void)
356
{
357
    do_indent(tabsize);
358
359
}

360
361
362
/* Unindent the current line, or all lines covered by the mark if the
 * mark is on, tabsize columns. */
void do_unindent(void)
363
{
364
    do_indent(-tabsize);
365
}
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/* undo a cut, or re-do an uncut */
void undo_cut(undo *u)
{
	cutbuffer = copy_filestruct(u->cutbuffer);

        /* Compute cutbottom for the uncut using out copy */
	for (cutbottom = cutbuffer; cutbottom->next != NULL; cutbottom = cutbottom->next)
	    ;

	/* Get to where we need to uncut from */
	if (u->mark_set && u->mark_begin_lineno < u->lineno)
	    do_gotolinecolumn(u->mark_begin_lineno, u->mark_begin_x+1, FALSE, FALSE, FALSE, FALSE);
	else
	    do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE);

        copy_from_filestruct(cutbuffer, cutbottom);
	free_filestruct(cutbuffer);
	cutbuffer = NULL;

}

/* Re-do a cut, or undo an uncut */
void redo_cut(undo *u) {
    int i;
391
    filestruct *t, *c;
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407

	do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE);
	openfile->mark_set = u->mark_set;
	if (cutbuffer)
	    free(cutbuffer);
	cutbuffer = NULL;

	/* Move ahead the same # lines we had if a marked cut */
	if (u->mark_set) {
	    for (i = 1, t = openfile->fileage; i != u->mark_begin_lineno; i++)
		t = t->next;
	    openfile->mark_begin = t;
	} else if (!u->to_end) {
	    /* Here we have a regular old potentially multi-line ^K cut.  We'll
		need to trick nano into thinking it's a marked cut to cut more
		than one line again */
408
	    for (c = u->cutbuffer, t = openfile->current; c->next != NULL && t->next != NULL; ) {
409
410
411
412

#ifdef DEBUG
	    fprintf(stderr, "Advancing, lineno  = %d, data = \"%s\"\n", t->lineno, t->data);
#endif
413
		c = c->next;
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
		t = t->next;
	    }
	    openfile->mark_begin = t;
	    openfile->mark_begin_x = 0;
	    openfile->mark_set = TRUE;
	}

	openfile->mark_begin_x = u->mark_begin_x;
	do_cut_text(FALSE, u->to_end, TRUE);
	openfile->mark_set = FALSE;
        openfile->mark_begin = NULL;
        openfile->mark_begin_x = 0;
	edit_refresh();
}

429
430
431
432
/* Undo the last thing(s) we did */
void do_undo(void)
{
    undo *u = openfile->current_undo;
433
    filestruct *f = openfile->current, *t;
434
    int len = 0;
435
    char *undidmsg, *data;
436
    filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom;
437
438
439
440
441
442
443
444
445
446
447
448
449
450

    if (!u) {
	statusbar(_("Nothing in undo buffer!"));
	return;
    }


    if (u->lineno <= f->lineno)
        for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev)
	    ;
    else
        for (; f->next != NULL && f->lineno != u->lineno; f = f->next)
	    ;
    if (f->lineno != u->lineno) {
451
        statusbar(_("Internal error: can't match line %d.  Please save your work"), u->lineno);
452
453
454
455
456
457
458
	return;
    }
#ifdef DEBUG
    fprintf(stderr, "data we're about to undo = \"%s\"\n", f->data);
    fprintf(stderr, "Undo running for type %d\n", u->type);
#endif

459
    openfile->current_x = u->begin;
460
461
    switch(u->type) {
    case ADD:
462
	undidmsg = _("text add");
463
464
465
466
467
468
469
470
	len = strlen(f->data) - strlen(u->strdata) + 1;
        data = charalloc(len);
        strncpy(data, f->data, u->begin);
	strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
	free(f->data);
	f->data = data;
	break;
    case DEL:
471
	undidmsg = _("text delete");
472
473
474
475
476
477
478
479
	len = strlen(f->data) + strlen(u->strdata) + 1;
	data = charalloc(len);

	strncpy(data, f->data, u->begin);
	strcpy(&data[u->begin], u->strdata);
	strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
	free(f->data);
	f->data = data;
480
481
	if (u->xflags == UNDO_DEL_BACKSPACE)
	    openfile->current_x += strlen(u->strdata);
482
483
	break;
    case SPLIT:
484
	undidmsg = _("line split");
485
486
487
488
489
490
491
	free(f->data);
	f->data = mallocstrcpy(NULL, u->strdata);
	if (f->next != NULL) {
	    filestruct *tmp = f->next;
	    unlink_node(tmp);
	    delete_node(tmp);
	}
492
	renumber(f);
493
494
	break;
    case UNSPLIT:
495
	undidmsg = _("line join");
496
497
498
499
500
501
502
	t = make_new_node(f);
	t->data = mallocstrcpy(NULL, u->strdata);
	data = mallocstrncpy(NULL, f->data, u->begin);
	data[u->begin] = '\0';
	free(f->data);
	f->data = data;
	splice_node(f, t, f->next);
503
	renumber(f);
504
	break;
505
506
    case CUT:
	undidmsg = _("text cut");
507
508
509
510
511
        undo_cut(u);
	break;
    case UNCUT:
	undidmsg = _("text uncut");
	redo_cut(u);
512
	break;
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
    case INSERT:
	undidmsg = _("text insert");
	cutbuffer = NULL;
	cutbottom = NULL;
	/* When we updated mark_begin_lineno in update_undo, it was effectively how many line
	   were inserted due to being partitioned before read_file was called.  So we
	   add its value here */
	openfile->mark_begin = fsfromline(u->lineno + u->mark_begin_lineno - 1);
	openfile->mark_begin_x = 0;
	openfile->mark_set = TRUE;
	do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE);
	cut_marked();
	u->cutbuffer = cutbuffer;
	u->cutbottom = cutbottom;
	cutbuffer = oldcutbuffer;
	cutbottom = oldcutbottom;
	openfile->mark_set = FALSE;
	break;
531
532
533
534
535
536
    case REPLACE:
	undidmsg = _("text replace");
	data = u->strdata;
	u->strdata = f->data;
	f->data = data;
	break;
537
    default:
538
	undidmsg = _("Internal error: unknown type.  Please save your work");
539
540
541
	break;

    }
542
543
    do_gotolinecolumn(u->lineno, u->begin, FALSE, FALSE, FALSE, TRUE);
    statusbar(_("Undid action (%s)"), undidmsg);
544
    openfile->current_undo = openfile->current_undo->next;
545
    openfile->last_action = OTHER;
546
547
548
549
550
}

void do_redo(void)
{
    undo *u = openfile->undotop;
551
    filestruct *f = openfile->current, *t;
Chris Allegretta's avatar
Chris Allegretta committed
552
    filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom;
553
    int len = 0, i;
554
    char *undidmsg, *data;
555
556
557
558
559
560
561
562

    for (; u != NULL && u->next != openfile->current_undo; u = u->next)
	;
    if (!u) {
	statusbar(_("Nothing to re-do!"));
	return;
    }
    if (u->next != openfile->current_undo) {
563
	statusbar(_("Internal error: Redo setup failed.  Please save your work"));
564
565
566
567
568
569
570
571
572
573
	return;
    }

    if (u->lineno <= f->lineno)
        for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev)
	    ;
    else
        for (; f->next != NULL && f->lineno != u->lineno; f = f->next)
	    ;
    if (f->lineno != u->lineno) {
574
        statusbar(_("Internal error: can't match line %d.  Please save your work"), u->lineno);
575
576
577
578
579
580
581
582
583
	return;
    }
#ifdef DEBUG
    fprintf(stderr, "data we're about to redo = \"%s\"\n", f->data);
    fprintf(stderr, "Redo running for type %d\n", u->type);
#endif

    switch(u->type) {
    case ADD:
584
	undidmsg = _("text add");
585
586
	len = strlen(f->data) + strlen(u->strdata) + 1;
        data = charalloc(len);
587
	strncpy(data, f->data, u->begin);
588
589
590
591
592
593
	strcpy(&data[u->begin], u->strdata);
	strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
	free(f->data);
	f->data = data;
	break;
    case DEL:
594
	undidmsg = _("text delete");
595
596
597
598
599
600
601
602
	len = strlen(f->data) + strlen(u->strdata) + 1;
	data = charalloc(len);
        strncpy(data, f->data, u->begin);
	strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
	free(f->data);
	f->data = data;
	break;
    case SPLIT:
603
	undidmsg = _("line split");
604
	t = make_new_node(f);
605
	t->data = mallocstrcpy(NULL, &u->strdata[u->begin]);
606
607
608
609
610
	data = mallocstrncpy(NULL, f->data, u->begin);
	data[u->begin] = '\0';
	free(f->data);
	f->data = data;
	splice_node(f, t, f->next);
611
	renumber(f);
612
613
	break;
    case UNSPLIT:
614
	undidmsg = _("line join");
615
616
617
618
	len = strlen(f->data) + strlen(u->strdata + 1);
	data = charalloc(len);
	strcpy(data, f->data);
	strcat(data, u->strdata);
619
	free(f->data);
620
	f->data = data;
621
622
623
624
625
	if (f->next != NULL) {
	    filestruct *tmp = f->next;
	    unlink_node(tmp);
	    delete_node(tmp);
	}
626
	renumber(f);
627
	break;
628
    case CUT:
629
630
631
632
633
634
	undidmsg = _("text cut");
	redo_cut(u);
	break;
    case UNCUT:
	undidmsg = _("text uncut");
	undo_cut(u);
635
	break;
636
637
638
639
640
641
    case REPLACE:
	undidmsg = _("text replace");
	data = u->strdata;
	u->strdata = f->data;
	f->data = data;
	break;
642
    case INSERT:
Chris Allegretta's avatar
Chris Allegretta committed
643
644
	undidmsg = _("text insert");
	do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE);
645
646
        copy_from_filestruct(u->cutbuffer, u->cutbottom);
	openfile->placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
647
	break;
648
    default:
649
	undidmsg = _("Internal error: unknown type.  Please save your work");
650
651
652
	break;

    }
653
654
    do_gotolinecolumn(u->lineno, u->begin, FALSE, FALSE, FALSE, TRUE);
    statusbar(_("Redid action (%s)"), undidmsg);
655
656

    openfile->current_undo = u;
657
    openfile->last_action = OTHER;
658
659

}
660
661
#endif /* !NANO_TINY */

662
/* Someone hits Enter *gasp!* */
663
664
665
666
667
void do_enter(void)
{
    filestruct *newnode = make_new_node(openfile->current);
    size_t extra = 0;

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

670
#ifndef NANO_TINY
671
    update_undo(SPLIT);
672
673


674
675
676
677
678
679
680
681
682
683
684
685
686
687
    /* 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);
688
#ifndef NANO_TINY
689
690
691
692
693
694
    if (ISSET(AUTOINDENT)) {
	strncpy(newnode->data, openfile->current->data, extra);
	openfile->totsize += mbstrlen(newnode->data);
    }
#endif
    null_at(&openfile->current->data, openfile->current_x);
695
#ifndef NANO_TINY
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
    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();
715

716
    openfile->placewewant = xplustabs();
717
718

    edit_refresh();
719
720
}

721
#ifndef NANO_TINY
722
723
/* Send a SIGKILL (unconditional kill) to the forked process in
 * execute_command(). */
724
RETSIGTYPE cancel_command(int signal)
725
726
727
728
729
{
    if (kill(pid, SIGKILL) == -1)
	nperror("kill");
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
730
/* Execute command in a shell.  Return TRUE on success. */
731
732
733
734
bool execute_command(const char *command)
{
    int fd[2];
    FILE *f;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
735
    char *shellenv;
736
737
738
739
740
741
742
743
744
745
746
    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
747
    /* Check $SHELL for the shell to use.  If it isn't set, use
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
748
749
     * /bin/sh.  Note that $SHELL should contain only a path, with no
     * arguments. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
750
751
752
753
    shellenv = getenv("SHELL");
    if (shellenv == NULL)
	shellenv = "/bin/sh";

754
755
756
757
758
759
760
    /* 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. */
761
	execl(shellenv, tail(shellenv), "-c", command, NULL);
762
763
764
	exit(0);
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
765
    /* Continue as parent. */
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
    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");

799
    read_file(f, "stdin", TRUE);
800
801
802
803
804
805
806

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

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

807
808
    /* Restore the terminal to its previous state.  In the process,
     * disable interpretation of the special control keys so that we can
809
     * use Ctrl-C for other things. */
810
    terminal_init();
811
812
813

    return TRUE;
}
814

815
/* Add a new undo struct to the top of the current pile */
816
void add_undo(undo_type current_action)
817
{
818
    undo *u, *cutu;
819
    char *data;
820
    openfilestruct *fs = openfile;
821
    static undo *last_cutu = NULL; /* Last thing we cut to set up the undo for uncut */
822

823
824
825
    /* Ugh, if we were called while cutting not-to-end, non-marked and on the same lineno,
       we need to  abort here */
    u = fs->current_undo;
826
827
    if (current_action == CUT && u && u->type == CUT 
	&& !u->mark_set && u->lineno == fs->current->lineno)
828
829
	return;

830
    /* Blow away the old undo stack if we are starting from the middle */
831
832
    while (fs->undotop != NULL && fs->undotop != fs->current_undo) {
	undo *u2 = fs->undotop;
833
	fs->undotop = fs->undotop->next;
834
835
	if (u2->strdata != NULL)
	    free(u2->strdata);
836
	if (u2->cutbuffer)
Chris Allegretta's avatar
Chris Allegretta committed
837
	    free_filestruct(u2->cutbuffer);
838
	free(u2);
839
840
    }

841
842
    /* Allocate and initialize a new undo type */
    u = nmalloc(sizeof(undo));
843
844
845
846
847
848
    u->type = current_action;
    u->lineno = fs->current->lineno;
    u->begin = fs->current_x;
    u->next = fs->undotop;
    fs->undotop = u;
    fs->current_undo = u;
849
850
851
    u->strdata = NULL;
    u->cutbuffer = NULL;
    u->cutbottom  = NULL;
852
    u->mark_set = 0;
853
854
    u->mark_begin_lineno = 0;
    u->mark_begin_x = 0;
855
    u->xflags = 0;
856
    u->to_end = FALSE;
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881

    switch (u->type) {
    /* We need to start copying data into the undo buffer or we wont be able
       to restore it later */
    case ADD:
        data = charalloc(2);
        data[0] = fs->current->data[fs->current_x];
        data[1] = '\0';
        u->strdata = data;
	break;
    case DEL:
	if (u->begin != strlen(fs->current->data)) {
            data = mallocstrncpy(NULL, &fs->current->data[u->begin], 2);
            data[1] = '\0';
            u->strdata = data;
	    break;
	}
	/* Else purposely fall into unsplit code */
	current_action = u->type = UNSPLIT;
    case UNSPLIT:
	if (fs->current->next) {
	    data = mallocstrcpy(NULL, fs->current->next->data);
	    u->strdata = data;
	}
	break;
882
    case INSERT:
883
    case SPLIT:
884
    case REPLACE:
885
	data = mallocstrcpy(NULL, fs->current->data);
886
887
888
889
890
891
892
893
	u->strdata = data;
	break;
    case CUT:
	u->mark_set = openfile->mark_set;
	if (u->mark_set) {
	    u->mark_begin_lineno = openfile->mark_begin->lineno;
	    u->mark_begin_x = openfile->mark_begin_x;
	}
894
	u->to_end = (ISSET(CUT_TO_END)) ? TRUE : FALSE;
895
	last_cutu = u;
896
	break;
897
    case UNCUT:
898
899
900
901
902
903
	if (!last_cutu)
	    statusbar(_("Internal error: can't setup uncut.  Please save your work."));
	else if (last_cutu->type == CUT) {
	    u->cutbuffer = last_cutu->cutbuffer;
	    u->cutbottom = last_cutu->cutbottom;
	}
904
905
	break;
    case OTHER:
906
	statusbar(_("Internal error: unknown type.  Please save your work."));
907
	break;
908
909
910
911
912
913
914
915
916
917
    }

#ifdef DEBUG
    fprintf(stderr, "fs->current->data = \"%s\", current_x = %d, u->begin = %d, type = %d\n",
			fs->current->data,  fs->current_x, u->begin, current_action);
    fprintf(stderr, "left update_add...\n");
#endif
    fs->last_action = current_action;
}

918
919
920
921
922
/* Update an undo item, or determine whether a new one
   is really needed and bounce the data to add_undo
   instead.  The latter functionality just feels
   gimmicky and may just be more hassle than
   it's worth, so it should be axed if needed. */
923
void update_undo(undo_type action)
924
925
926
927
{
    undo *u;
    char *data;
    int len = 0;
928
    openfilestruct *fs = openfile;
929
930

#ifdef DEBUG
931
932
933
934
935
936
        fprintf(stderr, "action = %d, fs->last_action = %d,  openfile->current->lineno = %d",
		action, fs->last_action, openfile->current->lineno);
	if (fs->current_undo)
	    fprintf(stderr, "fs->current_undo->lineno = %d\n",  fs->current_undo->lineno);
	else
	    fprintf(stderr, "\n");
937
938
#endif

939
940
941
    /* Change to an add if we're not using the same undo struct
       that we should be using */
    if (action != fs->last_action
942
	|| (action != CUT && action != INSERT
943
	    && openfile->current->lineno != fs->current_undo->lineno)) {
944
        add_undo(action);
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
	return;
    }

    assert(fs->undotop != NULL);
    u = fs->undotop;

    switch (u->type) {
    case ADD:
#ifdef DEBUG
        fprintf(stderr, "fs->current->data = \"%s\", current_x = %d, u->begin = %d\n",
			fs->current->data,  fs->current_x, u->begin);
#endif
        len = strlen(u->strdata) + 2;
        data = nrealloc((void *) u->strdata, len * sizeof(char *));
        data[len-2] = fs->current->data[fs->current_x];
        data[len-1] = '\0';
        u->strdata = (char *) data;
#ifdef DEBUG
	fprintf(stderr, "current undo data now \"%s\"\n", u->strdata);
#endif
	break;
    case DEL:
	len = strlen(u->strdata) + 2;
	assert(len > 2);
        if (fs->current_x == u->begin) {
	    /* They're deleting */
971
972
973
	    if (!u->xflags)
		u->xflags = UNDO_DEL_DEL;
	    else if (u->xflags != UNDO_DEL_DEL) {
974
		add_undo(action);
975
976
		return;
	    }
977
978
979
980
981
982
983
984
	    data = charalloc(len);
	    strcpy(data, u->strdata);
	    data[len-2] = fs->current->data[fs->current_x];;
	    data[len-1] = '\0';
	    free(u->strdata);
	    u->strdata = data;
	} else if (fs->current_x == u->begin - 1) {
	    /* They're backspacing */
985
986
987
	    if (!u->xflags)
		u->xflags = UNDO_DEL_BACKSPACE;
	    else if (u->xflags != UNDO_DEL_BACKSPACE) {
988
		add_undo(action);
989
990
		return;
	    }
991
992
993
994
995
996
997
998
	    data = charalloc(len);
	    data[0] = fs->current->data[fs->current_x];
	    strcpy(&data[1], u->strdata);
	    free(u->strdata);
	    u->strdata = data;
	    u->begin--;
	} else {
	    /* They deleted something else on the line */
999
	    add_undo(DEL);
1000
1001
1002
	    return;
	}
#ifdef DEBUG
1003
	fprintf(stderr, "current undo data now \"%s\"\nu->begin = %d\n", u->strdata, u->begin);
1004
1005
#endif
	break;
1006
    case CUT:
1007
1008
	if (!cutbuffer)
	    break;
1009
1010
	if (u->cutbuffer)
	    free(u->cutbuffer);
1011
	u->cutbuffer = copy_filestruct(cutbuffer);
1012
1013
1014
        /* Compute cutbottom for the uncut using out copy */
        for (u->cutbottom = u->cutbuffer; u->cutbottom->next != NULL; u->cutbottom = u->cutbottom->next)
            ;
1015
	break;
1016
    case REPLACE:
1017
    case UNCUT:
1018
	add_undo(action);
1019
	break;
1020
1021
    case INSERT:
	u->mark_begin_lineno = openfile->current->lineno;
1022
1023
    case SPLIT:
    case UNSPLIT:
1024
1025
	/* These cases are handled by the earlier check for a new line and action */
    case OTHER:
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
	break;
    }

#ifdef DEBUG
    fprintf(stderr, "Done in udpate_undo (type was %d)\n", action);
#endif
    if (fs->last_action != action) {
#ifdef DEBUG
	fprintf(stderr, "Starting add_undo for new action as it does not match last_action\n");
#endif
1036
	add_undo(action);
1037
1038
1039
1040
    }
    fs->last_action = action;
}

1041
#endif /* !NANO_TINY */
1042
1043

#ifndef DISABLE_WRAPPING
1044
/* Unset the prepend_wrap flag.  We need to do this as soon as we do
1045
 * something other than type text. */
1046
1047
void wrap_reset(void)
{
1048
    prepend_wrap = FALSE;
1049
1050
1051
}

/* We wrap the given line.  Precondition: we assume the cursor has been
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1052
1053
 * moved forward since the last typed character.  Return TRUE if we
 * wrapped, and FALSE otherwise. */
1054
1055
1056
bool do_wrap(filestruct *line)
{
    size_t line_len;
1057
	/* The length of the line we wrap. */
1058
    ssize_t wrap_loc;
1059
	/* The index of line->data where we wrap. */
1060
#ifndef NANO_TINY
1061
1062
    const char *indent_string = NULL;
	/* Indentation to prepend to the new line. */
1063
1064
    size_t indent_len = 0;
	/* The length of indent_string. */
1065
#endif
1066
1067
1068
1069
    const char *after_break;
	/* The text after the wrap point. */
    size_t after_break_len;
	/* The length of after_break. */
1070
    bool prepending = FALSE;
1071
	/* Do we prepend to the next line? */
1072
1073
    const char *next_line = NULL;
	/* The next line, minus indentation. */
1074
1075
1076
1077
1078
1079
    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. */
1080
1081
1082
1083
1084
1085
1086

    /* 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
1087
1088
     * set wrap_loc to the location of the character after it, so that
     * the blank is preserved at the end of the line.
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
     *
     * 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. */
1100
1101
1102
1103
1104
    wrap_loc = break_line(line->data, fill
#ifndef DISABLE_HELP
	, FALSE
#endif
	);
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117

    /* 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;

1118
#ifndef NANO_TINY
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
    /* 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
1134
1135
     * and the text of the next line, if they can fit without wrapping,
     * the next line exists, and the prepend_wrap flag is set. */
1136
1137
1138
1139
1140
1141
1142

    /* 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);

1143
1144
1145
1146
    /* 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) {
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
	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) {
1166
	    prepending = TRUE;
1167
1168
1169
1170
1171
1172
1173
1174
1175
	    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;

1176
#ifndef NANO_TINY
1177
    if (ISSET(AUTOINDENT)) {
1178
1179
	if (prepending) {
	    /* If we're prepending, the indentation will come from the
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
	     * 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';

1197
#ifndef NANO_TINY
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
    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);

1212
1213
    if (prepending) {
	/* If we're prepending, copy the text from the next line, minus
1214
1215
1216
1217
1218
	 * the indentation that we already copied above. */
	strcat(new_line, next_line);

	free(line->next->data);
	line->next->data = new_line;
1219
1220
1221
1222
1223

	/* 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();
1224
1225
1226
1227
1228
1229
    } 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
1230
1231
	/* If the current line is the last line of the file, move the
	 * last line of the file down to the next line. */
1232
1233
1234
	if (openfile->filebot == openfile->current)
	    openfile->filebot = openfile->current->next;

1235
1236
1237
1238
1239
1240
1241
1242
	openfile->current->next->data = new_line;

	openfile->totsize++;
    }

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

1243
1244
1245
    /* Set the prepend_wrap flag, so that later wraps of this line will
     * be prepended to the next line. */
    prepend_wrap = TRUE;
1246

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1247
1248
    /* Each line knows its number.  We recalculate these if we inserted
     * a new line. */
1249
    if (!prepending)
1250
1251
1252
	renumber(line);

    /* If the cursor was after the break point, we must move it.  We
1253
     * also clear the prepend_wrap flag in this case. */
1254
    if (openfile->current_x > wrap_loc) {
1255
	prepend_wrap = FALSE;
1256

1257
1258
	openfile->current = openfile->current->next;
	openfile->current_x -= wrap_loc
1259
#ifndef NANO_TINY
1260
1261
1262
1263
1264
1265
		- indent_len
#endif
		;
	openfile->placewewant = xplustabs();
    }

1266
#ifndef NANO_TINY
1267
    /* If the mark was on this line after the wrap point, we move it
1268
     * down.  If it was on the next line and we prepended to that line,
1269
1270
1271
1272
1273
1274
     * 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;
1275
	} else if (prepending && openfile->mark_begin == line->next)
1276
1277
1278
1279
1280
1281
1282
1283
	    openfile->mark_begin_x += after_break_len;
    }
#endif

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

1284
#if !defined(DISABLE_HELP) || !defined(DISABLE_WRAPJUSTIFY)
1285
/* We are trying to break a chunk off line.  We find the last blank such
1286
1287
 * 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
1288
1289
 * blank in that group of blanks.  The terminating '\0' counts as a
 * blank, as does a '\n' if newline is TRUE. */
1290
1291
ssize_t break_line(const char *line, ssize_t goal
#ifndef DISABLE_HELP
1292
	, bool newln
1293
1294
#endif
	)
1295
{
1296
1297
1298
1299
1300
    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. */
1301
1302
    size_t cur_pos = 0;
	/* Current column position in line. */
1303
    int line_len;
1304

1305
    assert(line != NULL);
1306

1307
1308
    while (*line != '\0' && goal >= cur_pos) {
	line_len = parse_mbchar(line, NULL, &cur_pos);
1309

1310
1311
	if (is_blank_mbchar(line)
#ifndef DISABLE_HELP
1312
		|| (newln && *line == '\n')
1313
1314
#endif
		) {
1315
	    blank_loc = cur_loc;
1316

1317
#ifndef DISABLE_HELP
1318
	    if (newln && *line == '\n')
1319
		break;
1320
#endif
1321
1322
1323
1324
	}

	line += line_len;
	cur_loc += line_len;
1325
1326
    }

1327
    if (goal >= cur_pos)
1328
1329
	/* In fact, the whole line displays shorter than goal. */
	return cur_loc;
1330

1331
1332
1333
1334
1335
1336
1337
1338
1339
#ifndef DISABLE_HELP
    if (newln && blank_loc <= 0) {
       /* If blank was not found or was found only first character,
        * force line break. */
       cur_loc -= line_len;
       return cur_loc;
    }
#endif

1340
1341
1342
    if (blank_loc == -1) {
	/* No blank was found that was short enough. */
	bool found_blank = FALSE;
1343
	ssize_t found_blank_loc = 0;
1344

1345
	while (*line != '\0') {
1346
	    line_len = parse_mbchar(line, NULL, NULL);
1347

1348
1349
	    if (is_blank_mbchar(line)
#ifndef DISABLE_HELP
1350
		|| (newln && *line == '\n')
1351
1352
#endif
		) {
1353
1354
		if (!found_blank)
		    found_blank = TRUE;
1355
		found_blank_loc = cur_loc;
1356
	    } else if (found_blank)
1357
		return found_blank_loc;
1358

1359
1360
1361
	    line += line_len;
	    cur_loc += line_len;
	}
1362

1363
1364
	return -1;
    }
1365

1366
1367
1368
    /* Move to the last blank after blank_loc, if there is one. */
    line -= cur_loc;
    line += blank_loc;
1369
    line_len = parse_mbchar(line, NULL, NULL);
1370
    line += line_len;
1371

1372
1373
    while (*line != '\0' && (is_blank_mbchar(line)
#ifndef DISABLE_HELP
1374
	|| (newln && *line == '\n')
1375
1376
#endif
	)) {
1377
#ifndef DISABLE_HELP
1378
	if (newln && *line == '\n')
1379
1380
1381
	    break;
#endif

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

1384
1385
	line += line_len;
	blank_loc += line_len;
1386
1387
    }

1388
1389
    return blank_loc;
}
1390
#endif /* !DISABLE_HELP || !DISABLE_WRAPJUSTIFY */
1391

1392
#if !defined(NANO_TINY) || !defined(DISABLE_JUSTIFY)
1393
1394
1395
1396
1397
1398
1399
/* 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;
1400

1401
    assert(line != NULL);
1402

1403
    blank_mb = charalloc(mb_cur_max());
1404

1405
    while (*line != '\0') {
1406
	blank_mb_len = parse_mbchar(line, blank_mb, NULL);
1407

1408
1409
	if (!is_blank_mbchar(blank_mb))
	    break;
1410

1411
1412
1413
	line += blank_mb_len;
	len += blank_mb_len;
    }
1414

1415
1416
1417
    free(blank_mb);

    return len;
1418
}
1419
#endif /* !NANO_TINY || !DISABLE_JUSTIFY */
1420

1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
#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)
1433
{
1434
1435
    char *end, *new_end, *new_paragraph_data;
    size_t shift = 0;
1436
#ifndef NANO_TINY
1437
1438
    size_t mark_shift = 0;
#endif
1439

1440
1441
1442
1443
1444
    /* 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));
1445

1446
1447
1448
1449
    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;
1450

1451
    while (*end != '\0') {
1452
	int end_len;
1453

1454
1455
	/* If this character is blank, change it to a space if
	 * necessary, and skip over all blanks after it. */
1456
	if (is_blank_mbchar(end)) {
1457
1458
	    end_len = parse_mbchar(end, NULL, NULL);

1459
1460
1461
	    *new_end = ' ';
	    new_end++;
	    end += end_len;
1462

1463
	    while (*end != '\0' && is_blank_mbchar(end)) {
1464
		end_len = parse_mbchar(end, NULL, NULL);
1465

1466
1467
		end += end_len;
		shift += end_len;
1468

1469
#ifndef NANO_TINY
1470
1471
1472
1473
1474
1475
1476
1477
		/* 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
1478
1479
1480
	 * 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. */
1481
	} else if (mbstrchr(punct, end) != NULL) {
1482
1483
	    end_len = parse_mbchar(end, NULL, NULL);

1484
1485
1486
1487
1488
1489
	    while (end_len > 0) {
		*new_end = *end;
		new_end++;
		end++;
		end_len--;
	    }
1490

1491
	    if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
1492
		end_len = parse_mbchar(end, NULL, NULL);
1493

1494
1495
1496
1497
1498
1499
1500
		while (end_len > 0) {
		    *new_end = *end;
		    new_end++;
		    end++;
		    end_len--;
		}
	    }
1501

1502
	    if (*end != '\0' && is_blank_mbchar(end)) {
1503
		end_len = parse_mbchar(end, NULL, NULL);
1504

1505
1506
1507
1508
		*new_end = ' ';
		new_end++;
		end += end_len;
	    }
1509

1510
	    if (*end != '\0' && is_blank_mbchar(end)) {
1511
		end_len = parse_mbchar(end, NULL, NULL);
1512

1513
1514
1515
1516
		*new_end = ' ';
		new_end++;
		end += end_len;
	    }
1517

1518
	    while (*end != '\0' && is_blank_mbchar(end)) {
1519
		end_len = parse_mbchar(end, NULL, NULL);
1520

1521
1522
		end += end_len;
		shift += end_len;
1523

1524
#ifndef NANO_TINY
1525
1526
1527
1528
1529
1530
1531
1532
		/* 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
1533
	 * unchanged. */
1534
	} else {
1535
1536
	    end_len = parse_mbchar(end, NULL, NULL);

1537
1538
1539
1540
1541
1542
1543
	    while (end_len > 0) {
		*new_end = *end;
		new_end++;
		end++;
		end_len--;
	    }
	}
1544
1545
    }

1546
    assert(*end == '\0');
1547

1548
    *new_end = *end;
1549

1550
    /* If there are spaces at the end of the line, remove them. */
1551
1552
1553
1554
    while (new_end > new_paragraph_data + skip &&
	*(new_end - 1) == ' ') {
	new_end--;
	shift++;
1555
1556
    }

1557
1558
1559
1560
1561
    if (shift > 0) {
	openfile->totsize -= shift;
	null_at(&new_paragraph_data, new_end - new_paragraph_data);
	free(paragraph->data);
	paragraph->data = new_paragraph_data;
1562

1563
#ifndef NANO_TINY
1564
1565
1566
1567
1568
1569
	/* 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;
1570
	}
1571
1572
1573
1574
#endif
    } else
	free(new_paragraph_data);
}
1575

1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
/* 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);
1587

1588
1589
1590
1591
1592
1593
1594
    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;
1595

1596
1597
1598
1599
1600
1601
    /* Compute quote depth level. */
    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
	qdepth += quotelen;
    return qdepth;
#endif	/* !HAVE_REGEX_H */
}
1602

1603
1604
1605
1606
1607
1608
1609
1610
/* 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));
1611

1612
1613
1614
    return (a_quote == quote_length(b_line) &&
	strncmp(a_line, b_line, a_quote) == 0);
}
1615

1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
/* 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);
1626
1627
}

1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
/* 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)
1652
{
1653
1654
1655
1656
    size_t quote_len, indent_len, temp_id_len;

    if (foo == NULL)
	return FALSE;
1657

1658
    /* Case 1). */
1659
    if (foo == openfile->fileage)
1660
	return TRUE;
1661

1662
1663
    quote_len = quote_length(foo->data);
    indent_len = indent_length(foo->data + quote_len);
1664

1665
1666
1667
    /* Not part of a paragraph. */
    if (foo->data[quote_len + indent_len] == '\0')
	return FALSE;
1668

1669
1670
1671
    /* Case 3). */
    if (!quotes_match(foo->data, quote_len, foo->prev->data))
	return TRUE;
1672

1673
    temp_id_len = indent_length(foo->prev->data + quote_len);
1674

1675
1676
1677
    /* Case 2) or 5) or 4). */
    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
	(quote_len == 0 && indent_len > 0
1678
#ifndef NANO_TINY
1679
1680
1681
1682
1683
	&& !ISSET(AUTOINDENT)
#endif
	) || !indents_match(foo->prev->data + quote_len, temp_id_len,
	foo->data + quote_len, indent_len))
	return TRUE;
1684

1685
1686
    return FALSE;
}
1687

1688
1689
1690
1691
/* Is foo inside a paragraph? */
bool inpar(const filestruct *const foo)
{
    size_t quote_len;
1692

1693
1694
    if (foo == NULL)
	return FALSE;
1695

1696
    quote_len = quote_length(foo->data);
1697

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1698
1699
    return (foo->data[quote_len + indent_length(foo->data +
	quote_len)] != '\0');
1700
}
1701

1702
/* Move the next par_len lines, starting with first_line, into the
1703
1704
 * justify buffer, leaving copies of those lines in place.  Assume that
 * par_len is greater than zero, and that there are enough lines after
1705
1706
 * first_line. */
void backup_lines(filestruct *first_line, size_t par_len)
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
{
    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;
1718
#ifndef NANO_TINY
1719
1720
1721
    bool old_mark_set = openfile->mark_set;
    ssize_t mb_lineno_save = 0;
    size_t mark_begin_x_save = 0;
1722
1723

    if (old_mark_set) {
1724
1725
	mb_lineno_save = openfile->mark_begin->lineno;
	mark_begin_x_save = openfile->mark_begin_x;
1726
1727
1728
    }
#endif

1729
1730
1731
    /* par_len will be one greater than the number of lines between
     * current and filebot if filebot is the last line in the
     * paragraph. */
1732
    assert(par_len > 0 && openfile->current->lineno + par_len <=
1733
	openfile->filebot->lineno + 1);
1734

1735
1736
1737
    /* 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--)
1738
	bot = bot->next;
1739

1740
1741
    /* Move the paragraph from the current buffer's filestruct to the
     * justify buffer. */
1742
    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot,
1743
	(i == 1 && bot == openfile->filebot) ? strlen(bot->data) : 0);
1744

1745
1746
1747
    /* Copy the paragraph back to the current buffer's filestruct from
     * the justify buffer. */
    copy_from_filestruct(jusbuffer, jusbottom);
1748

1749
1750
1751
1752
    /* 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. */
1753
    if (openfile->current != openfile->fileage) {
1754
	top = openfile->current->prev;
1755
1756
1757
1758
1759
1760
1761
1762
#ifndef NANO_TINY
	if (old_mark_set &&
		openfile->current->lineno == mb_lineno_save) {
	    openfile->mark_begin = openfile->current;
	    openfile->mark_begin_x = mark_begin_x_save;
	}
#endif
    } else
1763
	top = openfile->current;
1764
    for (i = par_len; i > 0 && top != NULL; i--) {
1765
1766
1767
1768
1769
1770
	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;
1771
#ifndef NANO_TINY
1772
1773
1774
	if (old_mark_set && top->lineno == mb_lineno_save) {
	    openfile->mark_begin = top;
	    openfile->mark_begin_x = mark_begin_x_save;
1775
	}
1776
1777
1778
#endif
	top = top->prev;
    }
1779

1780
1781
1782
    /* Put current_x at the same place in the copied paragraph that it
     * had in the original paragraph. */
    openfile->current_x = current_x_save;
1783

1784
1785
    set_modified();
}
1786

1787
1788
1789
/* 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
1790
 * we found a paragraph, and FALSE if there was an error or we didn't
1791
 * find a paragraph.
1792
1793
1794
 *
 * See the comment at begpar() for more about when a line is the
 * beginning of a paragraph. */
1795
bool find_paragraph(size_t *const quote, size_t *const par)
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
{
    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. */
1807

1808
1809
1810
1811
#ifdef HAVE_REGEX_H
    if (quoterc != 0) {
	statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
	return FALSE;
1812
1813
1814
    }
#endif

1815
    assert(openfile->current != NULL);
1816

1817
1818
1819
1820
1821
1822
1823
    /* 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
1824
     * last line of the next paragraph, if any. */
1825
1826
    if (!inpar(openfile->current)) {
	do_para_end(FALSE);
1827

1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
	/* 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;
	}
1843
    }
1844

1845
    /* If the current line isn't the first line of the paragraph, move
1846
1847
     * back to the first line of the paragraph. */
    if (!begpar(openfile->current))
1848
	do_para_begin(FALSE);
1849

1850
1851
    /* 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
1852
     * of lines in this paragraph. */
1853
1854
1855
1856
1857
    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;
1858

1859
1860
1861
1862
    /* 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. */
1863
1864
    if (openfile->current_x > 0)
	par_len++;
1865
1866
    openfile->current = current_save;
    openfile->current_y = current_y_save;
1867

1868
1869
    /* Save the values of quote_len and par_len. */
    assert(quote != NULL && par != NULL);
1870

1871
1872
    *quote = quote_len;
    *par = par_len;
1873

1874
    return TRUE;
1875
1876
}

1877
1878
1879
/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
 * the current paragraph. */
void do_justify(bool full_justify)
1880
{
1881
    filestruct *first_par_line = NULL;
1882
1883
	/* Will be the first line of the justified paragraph(s), if any.
	 * For restoring after unjustify. */
1884
    filestruct *last_par_line = NULL;
1885
	/* Will be the line after the last line of the justified
1886
	 * paragraph(s), if any.  Also for restoring after unjustify. */
1887
    bool filebot_inpar = FALSE;
1888
1889
	/* Whether the text at filebot is part of the current
	 * paragraph. */
1890

1891
1892
    /* We save these variables to be restored if the user
     * unjustifies. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1893
1894
    filestruct *edittop_save = openfile->edittop;
    filestruct *current_save = openfile->current;
1895
1896
1897
    size_t current_x_save = openfile->current_x;
    size_t pww_save = openfile->placewewant;
    size_t totsize_save = openfile->totsize;
1898
#ifndef NANO_TINY
1899
1900
1901
    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
1902
1903
    bool modified_save = openfile->modified;

1904
1905
    int kbinput;
    bool meta_key, func_key, s_or_t, ran_func, finished;
1906
    const sc *s;
1907

1908
    /* Move to the beginning of the current line, so that justifying at
1909
1910
     * the end of the last line of the file, if that line isn't blank,
     * will work the first time through. */
1911
1912
    openfile->current_x = 0;

1913
1914
1915
    /* If we're justifying the entire file, start at the beginning. */
    if (full_justify)
	openfile->current = openfile->fileage;
1916

1917
1918
1919
    while (TRUE) {
	size_t i;
	    /* Generic loop variable. */
1920
1921
	filestruct *curr_first_par_line;
	    /* The first line of the current paragraph. */
1922
	size_t quote_len;
1923
1924
	    /* Length of the initial quotation of the current
	     * paragraph. */
1925
	size_t indent_len;
1926
1927
	    /* Length of the initial indentation of the current
	     * paragraph. */
1928
	size_t par_len;
1929
	    /* Number of lines in the current paragraph. */
1930
1931
1932
1933
	ssize_t break_pos;
	    /* Where we will break lines. */
	char *indent_string;
	    /* The first indentation that doesn't match the initial
1934
1935
1936
1937
1938
1939
1940
	     * 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. */
1941

1942
1943
1944
1945
1946
1947
1948
	/* 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
1949
1950
	 * justifying the whole file, and we've found at least one
	 * paragraph, it means that we should justify all the way to the
1951
1952
1953
1954
	 * 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. */
1955
	if (!find_paragraph(&quote_len, &par_len)) {
1956
	    if (full_justify && first_par_line != NULL) {
1957
		last_par_line = openfile->filebot;
1958
		break;
1959
1960
1961
1962
	    } else {
		edit_refresh();
		return;
	    }
1963
1964
	}

1965
1966
1967
1968
1969
1970
	/* 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);

1971
1972
1973
1974
1975
1976
	/* 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 ?
1977
1978
		openfile->filebot->lineno - openfile->current->lineno +
		((openfile->filebot->data[0] != '\0') ? 1 : 0) :
1979
		par_len);
1980
1981
	    first_par_line = openfile->current;
	}
1982

1983
1984
1985
1986
	/* Set curr_first_par_line to the first line of the current
	 * paragraph. */
	curr_first_par_line = openfile->current;

1987
1988
	/* Initialize indent_string to a blank string. */
	indent_string = mallocstrcpy(NULL, "");
1989

1990
	/* Find the first indentation in the paragraph that doesn't
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1991
	 * match the indentation of the first line, and save it in
1992
1993
1994
1995
1996
	 * 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;
1997

1998
1999
2000
	    for (i = 0; i < par_len; i++) {
		indent_len = quote_len +
			indent_length(indent_line->data + quote_len);
2001

2002
2003
2004
2005
		if (indent_len != strlen(indent_string)) {
		    indent_string = mallocstrncpy(indent_string,
			indent_line->data, indent_len + 1);
		    indent_string[indent_len] = '\0';
2006

2007
2008
2009
		    if (past_first_line)
			break;
		}
2010

2011
2012
		if (indent_line == openfile->current)
		    past_first_line = TRUE;
2013

2014
2015
2016
		indent_line = indent_line->next;
	    }
	}
2017

2018
2019
2020
2021
2022
2023
2024
	/* 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);
2025

2026
2027
2028
	    indent_len = quote_len +
		indent_length(openfile->current->next->data +
		quote_len);
2029

2030
2031
	    next_line_len -= indent_len;
	    openfile->totsize -= indent_len;
2032

2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
	    /* 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++;
	    }
2045

2046
2047
2048
2049
2050
	    openfile->current->data =
		charealloc(openfile->current->data, line_len +
		next_line_len + 1);
	    strcat(openfile->current->data, next_line->data +
		indent_len);
2051

2052
	    /* Don't destroy edittop or filebot! */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2053
	    if (next_line == openfile->edittop)
2054
		openfile->edittop = openfile->current;
2055
2056
	    if (next_line == openfile->filebot)
		openfile->filebot = openfile->current;
2057

2058
#ifndef NANO_TINY
2059
2060
2061
2062
2063
2064
2065
2066
	    /* 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
2067

2068
2069
	    unlink_node(next_line);
	    delete_node(next_line);
2070

2071
2072
2073
	    /* If we've removed the next line, we need to go through
	     * this line again. */
	    i--;
2074

2075
2076
2077
	    par_len--;
	    openfile->totsize--;
	}
2078

2079
2080
2081
2082
2083
	/* 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));
2084

2085
2086
	while (par_len > 0 && strlenpt(openfile->current->data) >
		fill) {
2087
	    size_t line_len = strlen(openfile->current->data);
2088

2089
	    indent_len = strlen(indent_string);
2090

2091
2092
	    /* If this line is too long, try to wrap it to the next line
	     * to make it short enough. */
2093
	    break_pos = break_line(openfile->current->data + indent_len,
2094
2095
2096
2097
2098
		fill - strnlenpt(openfile->current->data, indent_len)
#ifndef DISABLE_HELP
		, FALSE
#endif
		);
2099

2100
2101
2102
	    /* 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;
2103

2104
2105
2106
	    /* Move forward to the character after the indentation and
	     * just after the space. */
	    break_pos += indent_len + 1;
2107

2108
	    assert(break_pos <= line_len);
2109

2110
2111
2112
2113
2114
2115
	    /* 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);
2116

2117
2118
2119
2120
	    /* 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
2121
#ifndef NANO_TINY
2122
		&& !ISSET(AUTOINDENT)
2123
#endif
2124
2125
		)
		indent_len = 0;
2126

2127
2128
2129
2130
2131
2132
2133
2134
	    /* 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);
2135

2136
2137
	    par_len++;
	    openfile->totsize += indent_len + 1;
2138

2139
#ifndef NANO_TINY
2140
2141
2142
2143
2144
2145
2146
	    /* 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;
2147
	    }
2148
#endif
2149

2150
2151
	    /* Break the current line. */
	    null_at(&openfile->current->data, break_pos);
2152

2153
	    /* If the current line is the last line of the file, move
2154
	     * the last line of the file down to the next line. */
2155
2156
2157
	    if (openfile->filebot == openfile->current)
		openfile->filebot = openfile->filebot->next;

2158
2159
	    /* Go to the next line. */
	    par_len--;
2160
2161
	    openfile->current_y++;
	    openfile->current = openfile->current->next;
2162
	}
2163

2164
2165
2166
	/* We're done breaking lines, so we don't need indent_string
	 * anymore. */
	free(indent_string);
2167

2168
2169
	/* Go to the next line, if possible.  If there is no next line,
	 * move to the end of the current line. */
2170
2171
2172
2173
2174
	if (openfile->current != openfile->filebot) {
	    openfile->current_y++;
	    openfile->current = openfile->current->next;
	} else
	    openfile->current_x = strlen(openfile->current->data);
2175

2176
2177
2178
2179
	/* 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);
2180
2181
2182
2183
2184

	/* 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. */
2185
2186
	if (!full_justify)
	    break;
2187
2188
    }

2189
    /* We are now done justifying the paragraph or the file, so clean
2190
     * up.  current_y and totsize have been maintained above.  If we
2191
2192
2193
     * actually justified something, set last_par_line to the new end of
     * the paragraph. */
    if (first_par_line != NULL)
2194
	last_par_line = openfile->current;
2195

2196
    edit_refresh();
2197

2198
#ifndef NANO_TINY
2199
2200
2201
2202
    /* 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;

2203
    /* Return here after a SIGWINCH. */
2204
    sigsetjmp(jump_buf, 1);
2205
2206
#endif

2207
    statusbar(_("Can now UnJustify!"));
2208

2209
2210
2211
2212
    /* 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);
2213

2214
2215
2216
    /* Display the shortcut list with UnJustify. */
    shortcut_init(TRUE);
    display_main_list();
2217

2218
2219
2220
2221
    /* 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);
2222
    s = get_shortcut(currmenu, &kbinput, &meta_key, &func_key);
2223

2224
    if (s && s->scfunc == do_uncut_text) {
2225
2226
2227
2228
	/* Splice the justify buffer back into the file, but only if we
	 * actually justified something. */
	if (first_par_line != NULL) {
	    filestruct *top_save;
2229

2230
2231
2232
	    /* Partition the filestruct so that it contains only the
	     * text of the justified paragraph. */
	    filepart = partition_filestruct(first_par_line, 0,
2233
2234
		last_par_line, filebot_inpar ?
		strlen(last_par_line->data) : 0);
2235

2236
	    /* Remove the text of the justified paragraph, and
2237
	     * replace it with the text in the justify buffer. */
2238
2239
2240
	    free_filestruct(openfile->fileage);
	    openfile->fileage = jusbuffer;
	    openfile->filebot = jusbottom;
2241

2242
	    top_save = openfile->fileage;
2243

2244
2245
2246
2247
	    /* 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);
2248

2249
2250
2251
	     /* Renumber starting with the beginning line of the old
	      * partition. */
	    renumber(top_save);
2252

2253
2254
2255
2256
2257
	    /* 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;
2258
	    openfile->totsize = totsize_save;
2259
#ifndef NANO_TINY
2260
2261
2262
2263
2264
2265
	    if (openfile->mark_set) {
		openfile->mark_begin = mark_begin_save;
		openfile->mark_begin_x = mark_begin_x_save;
	    }
#endif
	    openfile->modified = modified_save;
2266

2267
2268
	    /* Clear the justify buffer. */
	    jusbuffer = NULL;
2269

2270
2271
2272
2273
2274
2275
	    if (!openfile->modified)
		titlebar(NULL);
	    edit_refresh();
	}
    } else {
	unget_kbinput(kbinput, meta_key, func_key);
2276

2277
2278
2279
2280
	/* Blow away the text in the justify buffer. */
	free_filestruct(jusbuffer);
	jusbuffer = NULL;
    }
2281

2282
2283
2284
2285
2286
    blank_statusbar();

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

2289
/* Justify the current paragraph. */
2290
void do_justify_void(void)
2291
{
2292
2293
    do_justify(FALSE);
}
2294

2295
/* Justify the entire file. */
2296
2297
2298
void do_full_justify(void)
{
    do_justify(TRUE);
2299
}
2300
#endif /* !DISABLE_JUSTIFY */
2301

2302
2303
2304
2305
#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)
2306
{
2307
2308
2309
    char *save_search, *save_replace;
    size_t match_len, current_x_save = openfile->current_x;
    size_t pww_save = openfile->placewewant;
2310
    bool meta_key = FALSE, func_key = FALSE;
2311
2312
2313
2314
2315
2316
    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);
2317
#ifndef NANO_TINY
2318
2319
2320
2321
2322
    bool backwards_search_set = ISSET(BACKWARDS_SEARCH);
#endif
#ifdef HAVE_REGEX_H
    bool regexp_set = ISSET(USE_REGEXP);
#endif
2323
#ifndef NANO_TINY
2324
    bool old_mark_set = openfile->mark_set;
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
    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);
2336

2337
#ifndef NANO_TINY
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
    /* 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);

2355
#ifndef NANO_TINY
2356
    if (old_mark_set) {
2357
	/* If the mark is on, partition the filestruct so that it
2358
2359
2360
2361
	 * 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. */
2362
2363
2364
	mark_order((const filestruct **)&top, &top_x,
	    (const filestruct **)&bot, &bot_x, &right_side_up);
	filepart = partition_filestruct(top, top_x, bot, bot_x);
2365
2366
	if (!ISSET(NO_NEWLINES))
	    added_magicline = (openfile->filebot->data[0] != '\0');
2367
	openfile->mark_set = FALSE;
2368
2369
2370
    }
#endif

2371
2372
2373
2374
2375
    /* Start from the top of the file. */
    openfile->edittop = openfile->fileage;
    openfile->current = openfile->fileage;
    openfile->current_x = (size_t)-1;
    openfile->placewewant = 0;
2376

2377
    /* Find the first whole occurrence of word. */
2378
    findnextstr_wrap_reset();
2379
    while (findnextstr(TRUE, FALSE, openfile->fileage, 0, word,
2380
2381
2382
2383
2384
2385
2386
	&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);
2387

2388
	    edit_refresh();
2389

2390
2391
2392
	    do_replace_highlight(TRUE, exp_word);

	    /* Allow all instances of the word to be corrected. */
2393
2394
2395
2396
	    canceled = (do_prompt(FALSE,
#ifndef DISABLE_TABCOMP
		TRUE,
#endif
2397
		MSPELL, word,
2398
		&meta_key, &func_key,
2399
#ifndef NANO_TINY
2400
		NULL,
2401
#endif
2402
		edit_refresh, _("Edit a replacement")) == -1);
2403

2404
	    do_replace_highlight(FALSE, exp_word);
2405

2406
	    free(exp_word);
2407

2408
2409
	    if (!canceled && strcmp(word, answer) != 0) {
		openfile->current_x--;
2410
2411
		do_replace_loop(TRUE, &canceled, openfile->current,
			&openfile->current_x, word);
2412
	    }
2413

2414
2415
	    break;
	}
2416
2417
    }

2418
#ifndef NANO_TINY
2419
    if (old_mark_set) {
2420
2421
2422
	/* 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)
2423
	    remove_magicline();
2424

2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
	/* 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;
	}
2436

2437
2438
2439
2440
	/* Unpartition the filestruct so that it contains all the text
	 * again, and turn the mark back on. */
	unpartition_filestruct(&filepart);
	openfile->mark_set = TRUE;
2441
    }
2442
#endif
2443

2444
2445
2446
2447
2448
2449
2450
2451
    /* 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;
2452
    openfile->current = current_save;
2453
2454
    openfile->current_x = current_x_save;
    openfile->placewewant = pww_save;
2455

2456
2457
2458
    /* Restore case sensitivity setting. */
    if (!case_sens_set)
	UNSET(CASE_SENSITIVE);
2459

2460
#ifndef NANO_TINY
2461
2462
2463
2464
2465
2466
2467
2468
2469
    /* 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
2470

2471
    return !canceled;
2472
2473
}

2474
2475
2476
/* 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. */
2477
const char *do_int_speller(const char *tempfile_name)
2478
{
2479
2480
2481
2482
2483
    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;
2484

2485
2486
2487
2488
    /* Create all three pipes up front. */
    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
	pipe(uniq_fd) == -1)
	return _("Could not create pipe");
2489

2490
    statusbar(_("Creating misspelled word list, please wait..."));
2491

2492
2493
    /* A new process to run spell in. */
    if ((pid_spell = fork()) == 0) {
2494
	/* Child continues (i.e. future spell process). */
2495
	close(spell_fd[0]);
2496

2497
2498
2499
	/* Replace the standard input with the temp file. */
	if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
	    goto close_pipes_and_exit;
2500

2501
2502
	if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;
2503

2504
	close(tempfile_fd);
2505

2506
2507
2508
	/* Send spell's standard output to the pipe. */
	if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
2509

2510
2511
	close(spell_fd[1]);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2512
	/* Start the spell program; we are using $PATH. */
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
	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) {
2524
	/* Child continues (i.e. future spell process).  Replace the
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
	 * 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);
    }
2544

2545
2546
    close(spell_fd[0]);
    close(sort_fd[1]);
2547

2548
2549
    /* A new process to run uniq in. */
    if ((pid_uniq = fork()) == 0) {
2550
	/* Child continues (i.e. future uniq process).  Replace the
2551
2552
2553
	 * standard input with the standard output of the old pipe. */
	if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;
2554

2555
	close(sort_fd[0]);
2556

2557
2558
2559
	/* Send uniq's standard output to the new pipe. */
	if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
2560

2561
	close(uniq_fd[1]);
2562

2563
2564
	/* Start the uniq program; we are using PATH. */
	execlp("uniq", "uniq", NULL);
2565

2566
2567
2568
	/* This should not be reached if uniq is found. */
	exit(1);
    }
2569

2570
2571
    close(sort_fd[0]);
    close(uniq_fd[1]);
2572

2573
2574
2575
2576
2577
    /* 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");
    }
2578

2579
2580
2581
2582
2583
    /* 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");
    }
2584

2585
2586
2587
2588
    /* 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);
2589

2590
2591
2592
2593
2594
2595
2596
2597
    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;
    }
2598

2599
2600
    *read_buff_ptr = '\0';
    close(uniq_fd[0]);
2601

2602
2603
    /* Process the spelling errors. */
    read_buff_word = read_buff_ptr = read_buff;
2604

2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
    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;
2615
	}
2616
2617
	read_buff_ptr++;
    }
2618

2619
2620
2621
    /* 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);
2622

2623
    free(read_buff);
2624
    search_replace_abort();
2625
    edit_refresh();
2626

2627
2628
2629
2630
    /* 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);
2631

2632
2633
    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
	return _("Error invoking \"spell\"");
2634

2635
2636
    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
	return _("Error invoking \"sort -f\"");
2637

2638
2639
    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
	return _("Error invoking \"uniq\"");
2640

2641
2642
    /* Otherwise... */
    return NULL;
2643

2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
  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);
}
2655

2656
2657
/* External (alternate) spell checking.  Return NULL for normal
 * termination, and the error string otherwise. */
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
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;
2669
#ifndef NANO_TINY
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
    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. */
2686

2687
2688
2689
2690
2691
2692
    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;
    }
2693
2694
#endif

2695
    endwin();
2696

2697
2698
2699
    /* Set up an argument list to pass execvp(). */
    if (spellargs == NULL) {
	spellargs = (char **)nmalloc(arglen * sizeof(char *));
2700

2701
2702
2703
2704
2705
2706
2707
2708
	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;
2709
    }
2710
    spellargs[arglen - 2] = tempfile_name;
2711

2712
2713
    /* Start a new process for the alternate speller. */
    if ((pid_spell = fork()) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2714
	/* Start alternate spell program; we are using $PATH. */
2715
2716
2717
2718
	execvp(spellargs[0], spellargs);

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

2721
2722
2723
    /* If we couldn't fork, get out. */
    if (pid_spell < 0)
	return _("Could not fork");
2724

2725
#ifndef NANO_TINY
2726
    /* Don't handle a pending SIGWINCH until the alternate spell checker
2727
     * is finished and we've loaded the spell-checked file back in. */
2728
2729
2730
2731
    allow_pending_sigwinch(FALSE);
#endif

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

2734
2735
    /* Reenter curses mode. */
    doupdate();
2736

2737
2738
    /* Restore the terminal to its previous state. */
    terminal_init();
2739

2740
2741
    /* Turn the cursor back on for sure. */
    curs_set(1);
2742

2743
2744
2745
2746
    /* The screen might have been resized.  If it has, reinitialize all
     * the windows based on the new screen dimensions. */
    window_init();

2747
2748
    if (!WIFEXITED(alt_spell_status) ||
		WEXITSTATUS(alt_spell_status) != 0) {
2749
	char *alt_spell_error;
2750
	char *invoke_error = _("Error invoking \"%s\"");
2751

2752
#ifndef NANO_TINY
2753
2754
2755
	/* Turn the mark back on if it was on before. */
	openfile->mark_set = old_mark_set;
#endif
2756

2757
	alt_spell_error =
2758
2759
		charalloc(strlen(invoke_error) +
		strlen(alt_speller) + 1);
2760
2761
	sprintf(alt_spell_error, invoke_error, alt_speller);
	return alt_spell_error;
2762
    }
2763

2764
#ifndef NANO_TINY
2765
    if (old_mark_set) {
2766
2767
2768
2769
2770
	/* 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. */
2771
2772
2773
	mark_order((const filestruct **)&top, &top_x,
		(const filestruct **)&bot, &bot_x, &right_side_up);
	filepart = partition_filestruct(top, top_x, bot, bot_x);
2774
2775
	if (!ISSET(NO_NEWLINES))
	    added_magicline = (openfile->filebot->data[0] != '\0');
2776

2777
2778
2779
2780
2781
	/* 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
2782

2783
2784
2785
    /* Replace the text of the current buffer with the spell-checked
     * text. */
    replace_buffer(tempfile_name);
2786

2787
#ifndef NANO_TINY
2788
2789
    if (old_mark_set) {
	filestruct *top_save = openfile->fileage;
2790

2791
2792
2793
	/* 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)
2794
	    remove_magicline();
2795

2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
	/* 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;
2806
2807
	}

2808
2809
2810
2811
2812
2813
2814
	/* 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
2815
2816
2817
	 * 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. */
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
	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;
2834
    }
2835
#endif
2836

2837
2838
2839
    /* 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();
2840

2841
#ifndef NANO_TINY
2842
2843
2844
2845
    /* Handle a pending SIGWINCH again. */
    allow_pending_sigwinch(TRUE);
#endif

2846
    return NULL;
2847
2848
}

2849
2850
/* Spell check the current file.  If an alternate spell checker is
 * specified, use it.  Otherwise, use the internal spell checker. */
2851
void do_spell(void)
2852
{
2853
    bool status;
2854
2855
2856
    FILE *temp_file;
    char *temp = safe_tempfile(&temp_file);
    const char *spell_msg;
2857

2858
    if (temp == NULL) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2859
	statusbar(_("Error writing temp file: %s"), strerror(errno));
2860
2861
2862
	return;
    }

2863
    status =
2864
#ifndef NANO_TINY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2865
	openfile->mark_set ? write_marked_file(temp, temp_file, TRUE,
2866
	OVERWRITE) :
2867
#endif
2868
	write_file(temp, temp_file, TRUE, OVERWRITE, FALSE);
2869

2870
    if (!status) {
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
	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);

2881
    currmenu = MMAIN;
2882

2883
2884
    /* If the spell-checker printed any error messages onscreen, make
     * sure that they're cleared off. */
2885
    total_refresh();
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895

    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"));
2896
}
2897
#endif /* !DISABLE_SPELLER */
2898

2899
#ifndef NANO_TINY
2900
2901
/* Our own version of "wc".  Note that its character counts are in
 * multibyte characters instead of single-byte characters. */
2902
void do_wordlinechar_count(void)
2903
{
2904
    size_t words = 0, chars = 0;
2905
    ssize_t nlines = 0;
2906
    size_t current_x_save = openfile->current_x;
2907
2908
2909
2910
2911
2912
2913
2914
    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
2915
	 * contains only the marked text, and turn the mark off. */
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
	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
2928
2929
2930
     * 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. */
2931
    while (openfile->current != openfile->filebot ||
2932
	openfile->current->data[openfile->current_x] != '\0') {
2933
2934
2935
2936
	if (do_next_word(TRUE, FALSE))
	    words++;
    }

2937
2938
    /* Get the total line and character counts, as "wc -l"  and "wc -c"
     * do, but get the latter in multibyte characters. */
2939
    if (old_mark_set) {
2940
	nlines = openfile->filebot->lineno -
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2941
		openfile->fileage->lineno + 1;
2942
2943
	chars = get_totsize(openfile->fileage, openfile->filebot);

2944
2945
2946
2947
	/* Unpartition the filestruct so that it contains all the text
	 * again, and turn the mark back on. */
	unpartition_filestruct(&filepart);
	openfile->mark_set = TRUE;
2948
    } else {
2949
	nlines = openfile->filebot->lineno;
2950
	chars = openfile->totsize;
2951
2952
2953
2954
2955
2956
2957
    }

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

2958
2959
    /* Display the total word, line, and character counts on the
     * statusbar. */
2960
    statusbar(_("%sWords: %lu  Lines: %ld  Chars: %lu"), old_mark_set ?
2961
	_("In Selection:  ") : "", (unsigned long)words, (long)nlines,
2962
	(unsigned long)chars);
2963
}
2964
#endif /* !NANO_TINY */
2965

2966
/* Get verbatim input. */
2967
2968
2969
2970
2971
2972
void do_verbatim_input(void)
{
    int *kbinput;
    size_t kbinput_len, i;
    char *output;

2973
2974
    /* TRANSLATORS: This is displayed when the next keystroke will be
     * inserted verbatim. */
2975
2976
2977
2978
2979
    statusbar(_("Verbatim Input"));

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

2980
2981
2982
2983
2984
2985
2986
2987
2988
    /* 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);
    }
2989

2990
2991
2992
2993
2994
2995
2996
2997
    /* 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';

2998
2999
    free(kbinput);

3000
3001
3002
3003
    do_output(output, kbinput_len, TRUE);

    free(output);
}
3004