nano.c 95.5 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   nano.c                                                               *
 *                                                                        *
5
 *   Copyright (C) 1999-2004 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   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 *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   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., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
Chris Allegretta's avatar
Chris Allegretta committed
34
#include <sys/wait.h>
Chris Allegretta's avatar
Chris Allegretta committed
35
36
37
#include <errno.h>
#include <ctype.h>
#include <locale.h>
38
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "proto.h"
#include "nano.h"

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#ifdef HAVE_TERMIO_H
#include <termio.h>
#endif

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

54
55
56
57
#ifndef NANO_SMALL
#include <setjmp.h>
#endif

58
#ifndef DISABLE_WRAPJUSTIFY
59
60
static ssize_t fill = 0;	/* Fill - where to wrap lines,
				   basically */
61
#endif
62
#ifndef DISABLE_WRAPPING
63
static bool same_line_wrap = FALSE;	/* Whether wrapped text should
64
65
					   be prepended to the next
					   line */
66
#endif
67

Chris Allegretta's avatar
Chris Allegretta committed
68
static struct termios oldterm;	/* The user's original term settings */
69
static struct sigaction act;	/* For all our fun signal handlers */
Chris Allegretta's avatar
Chris Allegretta committed
70

71
#ifndef NANO_SMALL
72
73
static sigjmp_buf jmpbuf;	/* Used to return to mainloop after
				   SIGWINCH */
74
75
76
77
static int pid;			/* The PID of the newly forked process
				 * in open_pipe().  It must be global
				 * because the signal handler needs
				 * it. */
78
#endif
79

80
81
/* What we do when we're all set to exit. */
void finish(void)
Chris Allegretta's avatar
Chris Allegretta committed
82
{
83
84
85
86
    if (!ISSET(NO_HELP))
	blank_bottombars();
    else
	blank_statusbar();
87

Chris Allegretta's avatar
Chris Allegretta committed
88
89
90
    wrefresh(bottomwin);
    endwin();

91
    /* Restore the old terminal settings. */
Chris Allegretta's avatar
Chris Allegretta committed
92
    tcsetattr(0, TCSANOW, &oldterm);
Chris Allegretta's avatar
Chris Allegretta committed
93

94
95
96
97
98
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
	save_history();
#endif

99
#ifdef DEBUG
100
    thanks_for_all_the_fish();
101
#endif
102

103
    exit(0);
Chris Allegretta's avatar
Chris Allegretta committed
104
105
}

106
/* Die (gracefully?). */
Chris Allegretta's avatar
Chris Allegretta committed
107
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
108
109
110
{
    va_list ap;

111
112
113
    endwin();
    curses_ended = TRUE;

114
    /* Restore the old terminal settings. */
115
116
    tcsetattr(0, TCSANOW, &oldterm);

Chris Allegretta's avatar
Chris Allegretta committed
117
118
119
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
120

121
122
123
    /* save the currently loaded file if it's been modified */
    if (ISSET(MODIFIED))
	die_save_file(filename);
124

125
#ifdef ENABLE_MULTIBUFFER
126
    /* then save all of the other modified loaded files, if any */
127
    if (open_files != NULL) {
128
	openfilestruct *tmp;
129
130
131

	tmp = open_files;

132
	while (open_files->prev != NULL)
133
134
	    open_files = open_files->prev;

135
	while (open_files->next != NULL) {
136

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
137
	    /* if we already saved the file above (i.e, if it was the
138
139
	       currently loaded file), don't save it again */
	    if (tmp != open_files) {
Chris Allegretta's avatar
Chris Allegretta committed
140
141
142
143
		/* make sure open_files->fileage and fileage, and
		   open_files->filebot and filebot, are in sync; they
		   might not be if lines have been cut from the top or
		   bottom of the file */
144
		fileage = open_files->fileage;
Chris Allegretta's avatar
Chris Allegretta committed
145
		filebot = open_files->filebot;
146
		/* save the file if it's been modified */
Chris Allegretta's avatar
Chris Allegretta committed
147
		if (open_files->file_flags & MODIFIED)
148
		    die_save_file(open_files->filename);
149
150
151
152
153
154
	    }
	    open_files = open_files->next;
	}
    }
#endif

Chris Allegretta's avatar
Chris Allegretta committed
155
    exit(1); /* We have a problem: exit w/ errorlevel(1) */
156
157
}

Chris Allegretta's avatar
Chris Allegretta committed
158
void die_save_file(const char *die_filename)
159
{
Chris Allegretta's avatar
Chris Allegretta committed
160
    char *ret;
161
    bool failed = TRUE;
162

163
164
165
    /* If we're using restricted mode, don't write any emergency backup
     * files, since that would allow reading from or writing to files
     * not specified on the command line. */
166
167
168
    if (ISSET(RESTRICTED))
	return;

Chris Allegretta's avatar
Chris Allegretta committed
169
170
171
    /* If we can't save, we have REAL bad problems, but we might as well
       TRY. */
    if (die_filename[0] == '\0')
172
173
174
	die_filename = "nano";

    ret = get_next_filename(die_filename);
Chris Allegretta's avatar
Chris Allegretta committed
175
    if (ret[0] != '\0')
176
	failed = (write_file(ret, TRUE, FALSE, TRUE) == -1);
Chris Allegretta's avatar
Chris Allegretta committed
177

178
    if (!failed)
Chris Allegretta's avatar
Chris Allegretta committed
179
	fprintf(stderr, _("\nBuffer written to %s\n"), ret);
180
    else
181
	fprintf(stderr, _("\nBuffer not written to %s (too many backup files?)\n"), ret);
182
183

    free(ret);
Chris Allegretta's avatar
Chris Allegretta committed
184
185
}

186
/* Die with an error message that the screen was too small if, well, the
Chris Allegretta's avatar
Chris Allegretta committed
187
 * screen is too small. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
188
void die_too_small(void)
189
{
Chris Allegretta's avatar
Chris Allegretta committed
190
    die(_("Window size is too small for nano...\n"));
191
192
}

Chris Allegretta's avatar
Chris Allegretta committed
193
194
195
196
197
void print_view_warning(void)
{
    statusbar(_("Key illegal in VIEW mode"));
}

198
/* Initialize global variables -- no better way for now.  If
199
200
 * save_cutbuffer is TRUE, don't set cutbuffer to NULL. */
void global_init(bool save_cutbuffer)
Chris Allegretta's avatar
Chris Allegretta committed
201
202
203
{
    current_x = 0;
    current_y = 0;
204

205
206
    editwinrows = LINES - 5 + no_help();
    if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
207
208
	die_too_small();

Chris Allegretta's avatar
Chris Allegretta committed
209
    fileage = NULL;
210
211
    if (!save_cutbuffer)
	cutbuffer = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
212
213
214
    current = NULL;
    edittop = NULL;
    totlines = 0;
215
    totsize = 0;
Chris Allegretta's avatar
Chris Allegretta committed
216
    placewewant = 0;
217

218
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
219
    fill = wrap_at;
220
    if (fill <= 0)
Chris Allegretta's avatar
Chris Allegretta committed
221
	fill += COLS;
222
223
    if (fill < 0)
	fill = 0;
224
#endif
225

226
    hblank = charalloc(COLS + 1);
227
    memset(hblank, ' ', COLS);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
228
    hblank[COLS] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
229
230
}

231
232
void window_init(void)
{
233
234
    editwinrows = LINES - 5 + no_help();
    if (editwinrows < MIN_EDITOR_ROWS)
235
236
	die_too_small();

237
238
    if (topwin != NULL)
	delwin(topwin);
239
240
    if (edit != NULL)
	delwin(edit);
241
242
243
    if (bottomwin != NULL)
	delwin(bottomwin);

244
    /* Set up the windows. */
245
    topwin = newwin(2, COLS, 0, 0);
246
    edit = newwin(editwinrows, COLS, 2, 0);
247
248
    bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);

249
    /* Turn the keypad back on. */
250
251
252
253
    keypad(edit, TRUE);
    keypad(bottomwin, TRUE);
}

254
#ifndef DISABLE_MOUSE
255
256
257
258
259
260
261
262
void mouse_init(void)
{
    if (ISSET(USE_MOUSE)) {
	mousemask(BUTTON1_RELEASED, NULL);
	mouseinterval(50);
    } else
	mousemask(0, NULL);
}
263
#endif
264
265
266
267
268
269

#ifndef DISABLE_HELP
/* This function allocates help_text, and stores the help string in it. 
 * help_text should be NULL initially. */
void help_init(void)
{
270
271
272
    size_t allocsize = 1;	/* Space needed for help_text. */
    const char *htx;		/* Untranslated help message. */
    char *ptr;
273
    const shortcut *s;
274
275
276
277
#ifndef NANO_SMALL
    const toggle *t;
#endif

278
    /* First, set up the initial help text for the current function. */
279
280
    if (currshortcut == whereis_list || currshortcut == replace_list
	     || currshortcut == replace_list_2)
281
	htx = N_("Search Command Help Text\n\n "
282
		"Enter the words or characters you would like to search "
283
		"for, then hit Enter.  If there is a match for the text you "
284
		"entered, the screen will be updated to the location of the "
285
286
287
288
289
290
291
		"nearest match for the search string.\n\n The previous "
		"search string will be shown in brackets after the search "
		"prompt.  Hitting Enter without entering any text will "
		"perform the previous search.  If you have selected text "
		"with the mark and then search to replace, only matches in "
		"the selected text will be replaced.\n\n The following "
		"function keys are available in Search mode:\n\n");
292
    else if (currshortcut == gotoline_list)
293
	htx = N_("Go To Line Help Text\n\n "
294
295
296
297
298
299
		"Enter the line number that you wish to go to and hit "
		"Enter.  If there are fewer lines of text than the "
		"number you entered, you will be brought to the last line "
		"of the file.\n\n The following function keys are "
		"available in Go To Line mode:\n\n");
    else if (currshortcut == insertfile_list)
300
	htx = N_("Insert File Help Text\n\n "
301
302
303
304
305
306
307
308
309
310
311
312
313
		"Type in the name of a file to be inserted into the current "
		"file buffer at the current cursor location.\n\n "
		"If you have compiled nano with multiple file buffer "
		"support, and enable multiple buffers with the -F "
		"or --multibuffer command line flags, the Meta-F toggle, or "
		"a nanorc file, inserting a file will cause it to be "
		"loaded into a separate buffer (use Meta-< and > to switch "
		"between file buffers).\n\n If you need another blank "
		"buffer, do not enter any filename, or type in a "
		"nonexistent filename at the prompt and press "
		"Enter.\n\n The following function keys are "
		"available in Insert File mode:\n\n");
    else if (currshortcut == writefile_list)
314
	htx = N_("Write File Help Text\n\n "
315
316
		"Type the name that you wish to save the current file "
		"as and hit Enter to save the file.\n\n If you have "
317
		"selected text with the mark, you will be prompted to "
318
319
320
321
322
323
324
		"save only the selected portion to a separate file.  To "
		"reduce the chance of overwriting the current file with "
		"just a portion of it, the current filename is not the "
		"default in this mode.\n\n The following function keys "
		"are available in Write File mode:\n\n");
#ifndef DISABLE_BROWSER
    else if (currshortcut == browser_list)
325
	htx = N_("File Browser Help Text\n\n "
326
327
328
329
330
331
332
333
334
335
		"The file browser is used to visually browse the "
		"directory structure to select a file for reading "
		"or writing.  You may use the arrow keys or Page Up/"
		"Down to browse through the files, and S or Enter to "
		"choose the selected file or enter the selected "
		"directory.  To move up one level, select the directory "
		"called \"..\" at the top of the file list.\n\n The "
		"following function keys are available in the file "
		"browser:\n\n");
    else if (currshortcut == gotodir_list)
336
	htx = N_("Browser Go To Directory Help Text\n\n "
337
338
		"Enter the name of the directory you would like to "
		"browse to.\n\n If tab completion has not been disabled, "
339
		"you can use the Tab key to (attempt to) automatically "
340
341
342
		"complete the directory name.\n\n The following function "
		"keys are available in Browser Go To Directory mode:\n\n");
#endif
343
#ifndef DISABLE_SPELLER
344
    else if (currshortcut == spell_list)
345
	htx = N_("Spell Check Help Text\n\n "
346
347
348
349
350
		"The spell checker checks the spelling of all text "
		"in the current file.  When an unknown word is "
		"encountered, it is highlighted and a replacement can "
		"be edited.  It will then prompt to replace every "
		"instance of the given misspelled word in the "
351
352
353
		"current file, or, if you have selected text with the "
		"mark, in the selected text.\n\n The following other "
		"functions are available in Spell Check mode:\n\n");
354
#endif
355
356
#ifndef NANO_SMALL
    else if (currshortcut == extcmd_list)
357
	htx = N_("External Command Help Text\n\n "
358
359
360
361
362
		"This menu allows you to insert the output of a command "
		"run by the shell into the current buffer (or a new "
		"buffer in multibuffer mode).\n\n The following keys are "
		"available in this mode:\n\n");
#endif
363
364
365
    else
	/* Default to the main help list. */
	htx = N_(" nano help text\n\n "
366
367
368
369
370
371
372
373
374
375
	  "The nano editor is designed to emulate the functionality and "
	  "ease-of-use of the UW Pico text editor.  There are four main "
	  "sections of the editor: The top line shows the program "
	  "version, the current filename being edited, and whether "
	  "or not the file has been modified.  Next is the main editor "
	  "window showing the file being edited.  The status line is "
	  "the third line from the bottom and shows important messages. "
	  "The bottom two lines show the most commonly used shortcuts "
	  "in the editor.\n\n "
	  "The notation for shortcuts is as follows: Control-key "
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
376
377
378
379
380
381
382
383
384
	  "sequences are notated with a caret (^) symbol and can be "
	  "entered either by using the Control (Ctrl) key or pressing the "
	  "Esc key twice.  Escape-key sequences are notated with the Meta "
	  "(M) symbol and can be entered using either the Esc, Alt or "
	  "Meta key depending on your keyboard setup.  Also, pressing Esc "
	  "twice and then typing a three-digit number from 000 to 255 "
	  "will enter the character with the corresponding ASCII code.  "
	  "The following keystrokes are available in the main editor "
	  "window.  Alternative keys are shown in parentheses:\n\n");
385

386
387
388
    htx = _(htx);

    allocsize += strlen(htx);
389
390
391

    /* The space needed for the shortcut lists, at most COLS characters,
     * plus '\n'. */
392
    allocsize += (COLS < 21 ? 21 : COLS + 1) * length_of_list(currshortcut);
393
394

#ifndef NANO_SMALL
395
    /* If we're on the main list, we also count the toggle help text.
396
397
     * Each line has "M-%c\t\t\t", which fills 24 columns, plus a space,
     * plus translated text, plus '\n'. */
398
399
400
    if (currshortcut == main_list) {
	size_t endislen = strlen(_("enable/disable"));

401
	for (t = toggles; t != NULL; t = t->next)
402
403
	    allocsize += 8 + strlen(t->desc) + endislen;
    }
404
405
406
407
408
409
#endif /* !NANO_SMALL */

    /* help_text has been freed and set to NULL unless the user resized
     * while in the help screen. */
    free(help_text);

410
    /* Allocate space for the help text. */
411
    help_text = charalloc(allocsize);
412

413
414
    /* Now add the text we want. */
    strcpy(help_text, htx);
415
416
    ptr = help_text + strlen(help_text);

417
418
419
420
421
    /* Now add our shortcut info.  Assume that each shortcut has, at the
     * very least, an equivalent control key, an equivalent primary meta
     * key sequence, or both.  Also assume that the meta key values are
     * not control characters.  We can display a maximum of 3 shortcut
     * entries. */
422
    for (s = currshortcut; s != NULL; s = s->next) {
423
	int entries = 0;
424

425
	/* Control key. */
426
	if (s->ctrlval != NANO_NO_KEY) {
427
	    entries++;
428
#ifndef NANO_SMALL
429
	    if (s->ctrlval == NANO_HISTORY_KEY)
430
		ptr += sprintf(ptr, "%.7s", _("Up"));
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
431
	    else
432
#endif
433
	    if (s->ctrlval == NANO_CONTROL_SPACE)
434
		ptr += sprintf(ptr, "^%.6s", _("Space"));
435
	    else if (s->ctrlval == NANO_CONTROL_8)
436
437
		ptr += sprintf(ptr, "^?");
	    else
438
		ptr += sprintf(ptr, "^%c", s->ctrlval + 64);
439
	    *(ptr++) = '\t';
440
	}
441

442
443
444
	/* Function key. */
	if (s->funcval != NANO_NO_KEY) {
	    entries++;
445
	    ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0);
446
447
	    *(ptr++) = '\t';
	}
448

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
	/* Primary meta key sequence. */
	if (s->metaval != NANO_NO_KEY) {
	    entries++;
	    /* If this is the last entry, put it at the end. */
	    if (entries == 2 && s->miscval == NANO_NO_KEY) {
		entries++;
		*(ptr++) = '\t';
	    }
	    /* If the primary meta key sequence is the first entry,
	     * don't put parentheses around it. */
	    if (entries == 1 && s->metaval == NANO_ALT_SPACE)
		ptr += sprintf(ptr, "M-%.5s", _("Space"));
	    else
		ptr += sprintf(ptr, entries == 1 ? "M-%c" : "(M-%c)",
			toupper(s->metaval));
	    *(ptr++) = '\t';
	}
466

467
468
469
470
471
472
473
474
	/* Miscellaneous meta key sequence. */
	if (entries < 3 && s->miscval != NANO_NO_KEY) {
	    entries++;
	    /* If this is the last entry, put it at the end. */
	    if (entries == 2) {
		entries++;
		*(ptr++) = '\t';
	    }
475
	    ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval));
476
477
	    *(ptr++) = '\t';
	}
478

479
480
481
482
483
	/* Make sure all the help text starts at the same place. */
	while (entries < 3) {
	    entries++;
	    *(ptr++) = '\t';
	}
484
485

	assert(s->help != NULL);
486
	ptr += sprintf(ptr, "%.*s\n", COLS > 24 ? COLS - 24 : 0, s->help);
487
488
489
490
    }

#ifndef NANO_SMALL
    /* And the toggles... */
491
    if (currshortcut == main_list) {
492
493
	for (t = toggles; t != NULL; t = t->next) {
	    assert(t->desc != NULL);
494
495
	    ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val),
		t->desc, _("enable/disable"));
496
	}
497
    }
498
499
500
#endif /* !NANO_SMALL */

    /* If all went well, we didn't overwrite the allocated space for
501
     * help_text. */
502
    assert(strlen(help_text) < allocsize);
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
}
#endif

/* Create a new filestruct node.  Note that we specifically do not set
 * prevnode->next equal to the new line. */
filestruct *make_new_node(filestruct *prevnode)
{
    filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));

    newnode->data = NULL;
    newnode->prev = prevnode;
    newnode->next = NULL;
    newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;

    return newnode;
}

520
/* Make a copy of a node to a pointer (space will be malloc()ed). */
Chris Allegretta's avatar
Chris Allegretta committed
521
filestruct *copy_node(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
522
{
Chris Allegretta's avatar
Chris Allegretta committed
523
    filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegretta's avatar
Chris Allegretta committed
524

Chris Allegretta's avatar
Chris Allegretta committed
525
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
526

Chris Allegretta's avatar
Chris Allegretta committed
527
    dst->data = charalloc(strlen(src->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
528
529
530
531
532
533
534
535
    dst->next = src->next;
    dst->prev = src->prev;
    strcpy(dst->data, src->data);
    dst->lineno = src->lineno;

    return dst;
}

536
/* Splice a node into an existing filestruct. */
537
538
void splice_node(filestruct *begin, filestruct *newnode, filestruct
	*end)
539
540
541
542
543
544
545
546
547
548
549
{
    if (newnode != NULL) {
	newnode->next = end;
	newnode->prev = begin;
    }
    if (begin != NULL)
	begin->next = newnode;
    if (end != NULL)
	end->prev = newnode;
}

550
/* Unlink a node from the rest of the filestruct. */
Chris Allegretta's avatar
Chris Allegretta committed
551
void unlink_node(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
552
{
Chris Allegretta's avatar
Chris Allegretta committed
553
    assert(fileptr != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
554

555
556
557
558
559
560
561
562
    if (fileptr->prev != NULL)
	fileptr->prev->next = fileptr->next;

    if (fileptr->next != NULL)
	fileptr->next->prev = fileptr->prev;
}

/* Delete a node from the filestruct. */
Chris Allegretta's avatar
Chris Allegretta committed
563
void delete_node(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
564
{
Chris Allegretta's avatar
Chris Allegretta committed
565
566
567
568
569
    if (fileptr != NULL) {
	if (fileptr->data != NULL)
	    free(fileptr->data);
	free(fileptr);
    }
570
571
572
}

/* Okay, now let's duplicate a whole struct! */
Chris Allegretta's avatar
Chris Allegretta committed
573
filestruct *copy_filestruct(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
574
{
Chris Allegretta's avatar
Chris Allegretta committed
575
576
    filestruct *head;	/* copy of src, top of the copied list */
    filestruct *prev;	/* temp that traverses the list */
Chris Allegretta's avatar
Chris Allegretta committed
577

Chris Allegretta's avatar
Chris Allegretta committed
578
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
579

Chris Allegretta's avatar
Chris Allegretta committed
580
581
582
583
584
585
586
587
    prev = copy_node(src);
    prev->prev = NULL;
    head = prev;
    src = src->next;
    while (src != NULL) {
	prev->next = copy_node(src);
	prev->next->prev = prev;
	prev = prev->next;
Chris Allegretta's avatar
Chris Allegretta committed
588

Chris Allegretta's avatar
Chris Allegretta committed
589
	src = src->next;
Chris Allegretta's avatar
Chris Allegretta committed
590
591
    }

Chris Allegretta's avatar
Chris Allegretta committed
592
    prev->next = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
593
594
595
    return head;
}

596
/* Frees a filestruct. */
Chris Allegretta's avatar
Chris Allegretta committed
597
void free_filestruct(filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
598
{
Chris Allegretta's avatar
Chris Allegretta committed
599
600
601
602
    if (src != NULL) {
	while (src->next != NULL) {
	    src = src->next;
	    delete_node(src->prev);
Chris Allegretta's avatar
Chris Allegretta committed
603
#ifdef DEBUG
604
	    fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_node()");
Chris Allegretta's avatar
Chris Allegretta committed
605
#endif
Chris Allegretta's avatar
Chris Allegretta committed
606
607
	}
	delete_node(src);
608
#ifdef DEBUG
609
	fprintf(stderr, "%s: free'd last node.\n", "delete_node()");
610
611
612
613
#endif
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
614
void renumber_all(void)
Chris Allegretta's avatar
Chris Allegretta committed
615
616
{
    filestruct *temp;
617
    int i = 1;
Chris Allegretta's avatar
Chris Allegretta committed
618

Chris Allegretta's avatar
Chris Allegretta committed
619
620
    assert(fileage == NULL || fileage != fileage->next);
    for (temp = fileage; temp != NULL; temp = temp->next)
Chris Allegretta's avatar
Chris Allegretta committed
621
622
623
	temp->lineno = i++;
}

Chris Allegretta's avatar
Chris Allegretta committed
624
void renumber(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
625
{
Chris Allegretta's avatar
Chris Allegretta committed
626
    if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
Chris Allegretta's avatar
Chris Allegretta committed
627
	renumber_all();
Chris Allegretta's avatar
Chris Allegretta committed
628
629
    else {
	int lineno = fileptr->prev->lineno;
630

Chris Allegretta's avatar
Chris Allegretta committed
631
632
633
634
	assert(fileptr != fileptr->next);
	for (; fileptr != NULL; fileptr = fileptr->next)
	    fileptr->lineno = ++lineno;
    }
Chris Allegretta's avatar
Chris Allegretta committed
635
636
}

637
638
/* Print one usage string to the screen.  This cuts down on duplicate
 * strings to translate and leaves out the parts that shouldn't be
Chris Allegretta's avatar
Chris Allegretta committed
639
 * translatable (the flag names). */
640
641
void print1opt(const char *shortflag, const char *longflag, const char
	*desc)
642
643
644
645
646
647
648
649
650
651
652
653
654
{
    printf(" %s\t", shortflag);
    if (strlen(shortflag) < 8)
	printf("\t");

#ifdef HAVE_GETOPT_LONG
    printf("%s\t", longflag);
    if (strlen(longflag) < 8)
	printf("\t\t");
    else if (strlen(longflag) < 16)
	printf("\t");
#endif

655
    printf("%s\n", _(desc));
656
657
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
658
void usage(void)
Chris Allegretta's avatar
Chris Allegretta committed
659
660
{
#ifdef HAVE_GETOPT_LONG
Chris Allegretta's avatar
Chris Allegretta committed
661
662
    printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n"));
    printf(_("Option\t\tLong option\t\tMeaning\n"));
Chris Allegretta's avatar
Chris Allegretta committed
663
#else
Chris Allegretta's avatar
Chris Allegretta committed
664
665
    printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
    printf(_("Option\t\tMeaning\n"));
666
#endif
667

668
669
    print1opt("-h, -?", "--help", N_("Show this message"));
    print1opt(_("+LINE"), "", N_("Start at line number LINE"));
670
#ifndef NANO_SMALL
671
672
673
    print1opt("-A", "--smarthome", N_("Enable smart home key"));
    print1opt("-B", "--backup", N_("Backup existing files on save"));
    print1opt(_("-E [dir]"), _("--backupdir=[dir]"), N_("Directory for writing backup files"));
674
#endif
675
#ifdef ENABLE_MULTIBUFFER
676
    print1opt("-F", "--multibuffer", N_("Enable multiple file buffers"));
Chris Allegretta's avatar
Chris Allegretta committed
677
678
#endif
#ifdef ENABLE_NANORC
679
#ifndef NANO_SMALL
680
    print1opt("-H", "--historylog", N_("Log & read search/replace string history"));
681
#endif
682
    print1opt("-I", "--ignorercfiles", N_("Don't look at nanorc files"));
Chris Allegretta's avatar
Chris Allegretta committed
683
684
#endif
#ifndef NANO_SMALL
685
    print1opt("-N", "--noconvert", N_("Don't convert files from DOS/Mac format"));
686
687
#endif
#ifndef DISABLE_JUSTIFY
688
    print1opt(_("-Q [str]"), _("--quotestr=[str]"), N_("Quoting string, default \"> \""));
689
#endif
690
#ifdef HAVE_REGEX_H
691
    print1opt("-R", "--regexp", N_("Do regular expression searches"));
692
#endif
693
#ifndef NANO_SMALL
694
    print1opt("-S", "--smooth", N_("Smooth scrolling"));
695
#endif
696
697
    print1opt(_("-T [#cols]"), _("--tabsize=[#cols]"), N_("Set width of a tab in cols to #cols"));
    print1opt("-V", "--version", N_("Print version information and exit"));
698
#ifdef ENABLE_COLOR
699
    print1opt(_("-Y [str]"), _("--syntax [str]"), N_("Syntax definition to use"));
700
#endif
701
702
    print1opt("-Z", "--restricted", N_("Restricted mode"));
    print1opt("-c", "--const", N_("Constantly show cursor position"));
703
#ifndef NANO_SMALL
704
705
706
    print1opt("-d", "--rebinddelete", N_("Fix Backspace/Delete confusion problem"));
    print1opt("-i", "--autoindent", N_("Automatically indent new lines"));
    print1opt("-k", "--cut", N_("Cut from cursor to end of line"));
707
#endif
708
    print1opt("-l", "--nofollow", N_("Don't follow symbolic links, overwrite"));
709
#ifndef DISABLE_MOUSE
710
    print1opt("-m", "--mouse", N_("Enable mouse"));
Chris Allegretta's avatar
Chris Allegretta committed
711
#endif
712
#ifndef DISABLE_OPERATINGDIR
713
    print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), N_("Set operating directory"));
Chris Allegretta's avatar
Chris Allegretta committed
714
#endif
715
    print1opt("-p", "--preserve", N_("Preserve XON (^Q) and XOFF (^S) keys"));
716
#ifndef DISABLE_WRAPJUSTIFY
717
    print1opt(_("-r [#cols]"), _("--fill=[#cols]"), N_("Set fill cols to (wrap lines at) #cols"));
718
#endif
719
#ifndef DISABLE_SPELLER
720
    print1opt(_("-s [prog]"), _("--speller=[prog]"), N_("Enable alternate speller"));
721
#endif
722
723
    print1opt("-t", "--tempfile", N_("Auto save on exit, don't prompt"));
    print1opt("-v", "--view", N_("View (read only) mode"));
724
#ifndef DISABLE_WRAPPING
725
    print1opt("-w", "--nowrap", N_("Don't wrap long lines"));
Chris Allegretta's avatar
Chris Allegretta committed
726
#endif
727
728
    print1opt("-x", "--nohelp", N_("Don't show help window"));
    print1opt("-z", "--suspend", N_("Enable suspend"));
Chris Allegretta's avatar
Chris Allegretta committed
729

730
    /* This is a special case. */
Chris Allegretta's avatar
Chris Allegretta committed
731
    printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
732

Chris Allegretta's avatar
Chris Allegretta committed
733
734
735
    exit(0);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
736
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
737
{
Chris Allegretta's avatar
Chris Allegretta committed
738
    printf(_(" GNU nano version %s (compiled %s, %s)\n"),
Chris Allegretta's avatar
Chris Allegretta committed
739
	   VERSION, __TIME__, __DATE__);
740
    printf(_
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
741
	   (" Email: nano@nano-editor.org	Web: http://www.nano-editor.org/"));
742
    printf(_("\n Compiled options:"));
743

744
745
746
#ifndef ENABLE_NLS
    printf(" --disable-nls");
#endif
747
748
749
#ifdef DEBUG
    printf(" --enable-debug");
#endif
750
751
#ifdef NANO_EXTRA
    printf(" --enable-extra");
752
#endif
753
754
755
#ifdef NANO_SMALL
    printf(" --enable-tiny");
#else
756
#ifdef DISABLE_BROWSER
757
    printf(" --disable-browser");
758
#endif
759
760
#ifdef DISABLE_HELP
    printf(" --disable-help");
761
762
#endif
#ifdef DISABLE_JUSTIFY
763
    printf(" --disable-justify");
764
#endif
765
#ifdef DISABLE_MOUSE
766
    printf(" --disable-mouse");
767
#endif
768
769
770
#ifdef DISABLE_OPERATINGDIR
    printf(" --disable-operatingdir");
#endif
771
772
773
774
775
776
777
778
779
780
#ifdef DISABLE_SPELLER
    printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
    printf(" --disable-tabcomp");
#endif
#endif /* NANO_SMALL */
#ifdef DISABLE_WRAPPING
    printf(" --disable-wrapping");
#endif
781
782
783
#ifdef DISABLE_ROOTWRAP
    printf(" --disable-wrapping-as-root");
#endif
784
785
786
787
788
789
790
791
792
#ifdef ENABLE_COLOR
    printf(" --enable-color");
#endif
#ifdef ENABLE_MULTIBUFFER
    printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
    printf(" --enable-nanorc");
#endif
793
794
795
796
#ifdef USE_SLANG
    printf(" --with-slang");
#endif
    printf("\n");
Chris Allegretta's avatar
Chris Allegretta committed
797
798
799
800
}

int no_help(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
801
    return ISSET(NO_HELP) ? 2 : 0;
Chris Allegretta's avatar
Chris Allegretta committed
802
803
}

804
void nano_disabled_msg(void)
805
{
Chris Allegretta's avatar
Chris Allegretta committed
806
    statusbar(_("Sorry, support for this function has been disabled"));
807
808
}

Chris Allegretta's avatar
Chris Allegretta committed
809
#ifndef NANO_SMALL
810
RETSIGTYPE cancel_fork(int signal)
Chris Allegretta's avatar
Chris Allegretta committed
811
{
812
813
    if (kill(pid, SIGKILL) == -1)
	nperror("kill");
Chris Allegretta's avatar
Chris Allegretta committed
814
815
}

816
817
/* Return TRUE on success. */
bool open_pipe(const char *command)
Chris Allegretta's avatar
Chris Allegretta committed
818
{
819
820
821
    int fd[2];
    FILE *f;
    struct sigaction oldaction, newaction;
822
823
			/* Original and temporary handlers for
			 * SIGINT. */
824
825
826
    bool sig_failed = FALSE;
    /* sig_failed means that sigaction() failed without changing the
     * signal handlers.
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
827
     *
828
829
     * We use this variable since it is important to put things back
     * when we finish, even if we get errors. */
Chris Allegretta's avatar
Chris Allegretta committed
830

831
    /* Make our pipes. */
832

833
834
    if (pipe(fd) == -1) {
	statusbar(_("Could not pipe"));
835
	return FALSE;
836
    }
837

838
    /* Fork a child. */
839

840
841
842
843
844
    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. */
845

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
846
	execl("/bin/sh", "sh", "-c", command, 0);
847
848
	exit(0);
    }
849

850
851
852
853
854
855
856
    /* Else continue as parent. */

    close(fd[1]);

    if (pid == -1) {
	close(fd[0]);
	statusbar(_("Could not fork"));
857
	return FALSE;
858
    }
Chris Allegretta's avatar
Chris Allegretta committed
859

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
867
    if (sigaction(SIGINT, NULL, &newaction) == -1) {
868
	sig_failed = TRUE;
869
	nperror("sigaction");
Chris Allegretta's avatar
Chris Allegretta committed
870
    } else {
871
	newaction.sa_handler = cancel_fork;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
872
	if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
873
	    sig_failed = TRUE;
874
875
	    nperror("sigaction");
	}
Chris Allegretta's avatar
Chris Allegretta committed
876
    }
877
878
    /* Note that now oldaction is the previous SIGINT signal handler,
     * to be restored later. */
879

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

884
    read_file(f, "stdin");
885
886
    /* If multibuffer mode is on, we could be here in view mode.  If so,
     * don't set the modification flag. */
887
888
889
890
891
892
    if (!ISSET(VIEW_MODE))
	set_modified();

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

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

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

900
    return TRUE;
901
}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
902
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
903

904
/* The user typed a character; add it to the edit buffer. */
905
906
907
908
void do_char(char ch)
{
    size_t current_len = strlen(current->data);
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
909
    bool do_refresh = FALSE;
910
	/* Do we have to call edit_refresh(), or can we get away with
911
912
	 * update_line()? */
#endif
913

914
915
916
917
918
919
920
921
922
923
924
    if (ch == '\0')		/* Null to newline, if needed. */
	ch = '\n';
    else if (ch == '\n') {	/* Newline to Enter, if needed. */
	do_enter();
	return;
    }

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

    /* When a character is inserted on the current magicline, it means
     * we need a new one! */
925
    if (filebot == current)
926
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
927

928
    /* More dangerousness fun =) */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
929
    current->data = charealloc(current->data, current_len + 2);
930
    assert(current_x <= current_len);
931
932
    charmove(&current->data[current_x + 1], &current->data[current_x],
	current_len - current_x + 1);
933
934
935
    current->data[current_x] = ch;
    totsize++;
    set_modified();
936

Chris Allegretta's avatar
Chris Allegretta committed
937
#ifndef NANO_SMALL
938
    /* Note that current_x has not yet been incremented. */
939
940
    if (current == mark_beginbuf && current_x < mark_beginx)
	mark_beginx++;
Chris Allegretta's avatar
Chris Allegretta committed
941
#endif
942

943
    do_right(FALSE);
944

945
#ifndef DISABLE_WRAPPING
946
    /* If we're wrapping text, we need to call edit_refresh(). */
947
    if (!ISSET(NO_WRAP) && ch != '\t')
948
	do_refresh = do_wrap(current);
949
#endif
Chris Allegretta's avatar
Chris Allegretta committed
950

951
#ifdef ENABLE_COLOR
952
953
    /* If color syntaxes are turned on, we need to call
     * edit_refresh(). */
954
    if (ISSET(COLOR_SYNTAX))
955
	do_refresh = TRUE;
956
#endif
Chris Allegretta's avatar
Chris Allegretta committed
957

958
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
959
    if (do_refresh)
960
	edit_refresh();
961
    else
962
#endif
963
	update_line(current, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
964
965
}

966
void do_verbatim_input(void)
967
{
968
969
    int *v_kbinput = NULL;	/* Used to hold verbatim input. */
    size_t v_len;		/* Length of verbatim input. */
970
    size_t i;
971
972

    statusbar(_("Verbatim input"));
973

974
    v_kbinput = get_verbatim_kbinput(edit, ERR, v_kbinput, &v_len, TRUE);
975
976
977
978
979

    /* Turn on DISABLE_CURPOS while inserting character(s) and turn it
     * off afterwards, so that if constant cursor position display is
     * on, it will be updated properly. */
    SET(DISABLE_CURPOS);
980
981
    for (i = 0; i < v_len; i++)
	do_char((char)v_kbinput[i]);
982
983
    UNSET(DISABLE_CURPOS);

984
    free(v_kbinput);
985
986
}

987
void do_backspace(void)
Chris Allegretta's avatar
Chris Allegretta committed
988
{
989
    if (current != fileage || current_x > 0) {
990
	do_left(FALSE);
991
	do_delete();
Chris Allegretta's avatar
Chris Allegretta committed
992
993
994
    }
}

995
void do_delete(void)
Chris Allegretta's avatar
Chris Allegretta committed
996
{
997
    bool do_refresh = FALSE;
998
999
1000
	/* Do we have to call edit_refresh(), or can we get away with
	 * update_line()? */

1001
1002
    assert(current != NULL && current->data != NULL && current_x <=
	strlen(current->data));
Chris Allegretta's avatar
Chris Allegretta committed
1003

1004
    placewewant = xplustabs();
1005

1006
1007
    if (current->data[current_x] != '\0') {
	size_t linelen = strlen(current->data + current_x);
1008

1009
	assert(current_x < strlen(current->data));
Chris Allegretta's avatar
Chris Allegretta committed
1010

1011
	/* Let's get dangerous. */
1012
	charmove(&current->data[current_x], &current->data[current_x + 1],
1013
		linelen);
Chris Allegretta's avatar
Chris Allegretta committed
1014

1015
1016
1017
1018
	null_at(&current->data, linelen + current_x - 1);
#ifndef NANO_SMALL
	if (current_x < mark_beginx && mark_beginbuf == current)
	    mark_beginx--;
Chris Allegretta's avatar
Chris Allegretta committed
1019
#endif
1020
1021
    } else if (current != filebot && (current->next != filebot ||
	current->data[0] == '\0')) {
1022
	/* We can delete the line before filebot only if it is blank: it
1023
	 * becomes the new magicline then. */
1024
	filestruct *foo = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
1025

1026
	assert(current_x == strlen(current->data));
1027
1028
1029
1030
1031
1032

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

1033
1034
1035
1036
1037
1038
1039
1040
1041
	current->data = charealloc(current->data, current_x +
		strlen(foo->data) + 1);
	strcpy(current->data + current_x, foo->data);
#ifndef NANO_SMALL
	if (mark_beginbuf == current->next) {
	    mark_beginx += current_x;
	    mark_beginbuf = current;
	}
#endif
1042
	if (filebot == foo)
Chris Allegretta's avatar
Chris Allegretta committed
1043
1044
1045
1046
1047
1048
	    filebot = current;

	unlink_node(foo);
	delete_node(foo);
	renumber(current);
	totlines--;
1049
#ifndef DISABLE_WRAPPING
1050
	wrap_reset();
1051
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1052
    } else
1053
	return;
Chris Allegretta's avatar
Chris Allegretta committed
1054
1055
1056

    totsize--;
    set_modified();
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068

#ifdef ENABLE_COLOR
    /* If color syntaxes are turned on, we need to call
     * edit_refresh(). */
    if (ISSET(COLOR_SYNTAX))
	do_refresh = TRUE;
#endif

    if (do_refresh)
	edit_refresh();
    else
	update_line(current, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
1069
1070
}

1071
void do_tab(void)
Chris Allegretta's avatar
Chris Allegretta committed
1072
{
1073
    do_char('\t');
Chris Allegretta's avatar
Chris Allegretta committed
1074
1075
}

1076
/* Someone hits return *gasp!* */
1077
void do_enter(void)
Chris Allegretta's avatar
Chris Allegretta committed
1078
{
1079
1080
    filestruct *newnode = make_new_node(current);
    size_t extra = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1081

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1084
#ifndef NANO_SMALL
1085
1086
    /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
    if (ISSET(AUTOINDENT)) {
1087
1088
1089
1090
1091
	/* 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(current->data);
	if (extra > current_x)
1092
1093
	    extra = current_x;
	totsize += extra;
1094
1095
1096
1097
1098
1099
1100
    }
#endif
    newnode->data = charalloc(strlen(current->data + current_x) +
	extra + 1);
    strcpy(&newnode->data[extra], current->data + current_x);
#ifndef NANO_SMALL
    if (ISSET(AUTOINDENT))
1101
1102
	strncpy(newnode->data, current->data, extra);
#endif
1103
1104
1105
1106
1107
    null_at(&current->data, current_x);
#ifndef NANO_SMALL
    if (current == mark_beginbuf && current_x < mark_beginx) {
	mark_beginbuf = newnode;
	mark_beginx += extra - current_x;
1108
    }
1109
1110
#endif
    current_x = extra;
Chris Allegretta's avatar
Chris Allegretta committed
1111

1112
    if (current == filebot)
1113
1114
	filebot = newnode;
    splice_node(current, newnode, current->next);
1115

1116
1117
1118
    totsize++;
    renumber(current);
    current = newnode;
1119
1120

    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
1121

1122
1123
1124
    totlines++;
    set_modified();
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1125
1126
}

1127
#ifndef NANO_SMALL
1128
void do_next_word(void)
Chris Allegretta's avatar
Chris Allegretta committed
1129
{
1130
    size_t old_pww = placewewant;
1131
    const filestruct *old_current = current;
1132
    assert(current != NULL && current->data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1133

1134
1135
    /* Skip letters in this word first. */
    while (current->data[current_x] != '\0' &&
1136
	isalnum(current->data[current_x]))
1137
	current_x++;
Chris Allegretta's avatar
Chris Allegretta committed
1138

1139
1140
    for (; current != NULL; current = current->next) {
	while (current->data[current_x] != '\0' &&
1141
		!isalnum(current->data[current_x]))
1142
	    current_x++;
Chris Allegretta's avatar
Chris Allegretta committed
1143

1144
1145
	if (current->data[current_x] != '\0')
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1146

1147
1148
1149
1150
	current_x = 0;
    }
    if (current == NULL)
	current = filebot;
Chris Allegretta's avatar
Chris Allegretta committed
1151

1152
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1153

1154
1155
    /* Update the screen. */
    edit_redraw(old_current, old_pww);
1156
}
Chris Allegretta's avatar
Chris Allegretta committed
1157

1158
/* The same thing for backwards. */
1159
void do_prev_word(void)
1160
{
1161
    size_t old_pww = placewewant;
1162
    const filestruct *old_current = current;
1163
    assert(current != NULL && current->data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1164

1165
    /* Skip letters in this word first. */
1166
    while (current_x >= 0 && isalnum(current->data[current_x]))
1167
	current_x--;
Chris Allegretta's avatar
Chris Allegretta committed
1168

1169
    for (; current != NULL; current = current->prev) {
1170
	while (current_x >= 0 && !isalnum(current->data[current_x]))
1171
	    current_x--;
Chris Allegretta's avatar
Chris Allegretta committed
1172

1173
1174
	if (current_x >= 0)
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1175

1176
1177
	if (current->prev != NULL)
	    current_x = strlen(current->prev->data);
Chris Allegretta's avatar
Chris Allegretta committed
1178
1179
    }

1180
    if (current == NULL) {
1181
1182
	current = fileage;
	current_x = 0;
1183
    } else {
1184
	while (current_x > 0 && isalnum(current->data[current_x - 1]))
1185
	    current_x--;
1186
    }
Chris Allegretta's avatar
Chris Allegretta committed
1187

1188
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1189

1190
1191
    /* Update the screen. */
    edit_redraw(old_current, old_pww);
1192
}
Chris Allegretta's avatar
Chris Allegretta committed
1193

1194
void do_mark(void)
1195
{
1196
1197
    TOGGLE(MARK_ISSET);
    if (ISSET(MARK_ISSET)) {
1198
1199
1200
1201
1202
1203
1204
1205
	statusbar(_("Mark Set"));
	mark_beginbuf = current;
	mark_beginx = current_x;
    } else {
	statusbar(_("Mark UNset"));
	edit_refresh();
    }
}
1206
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
1207

1208
#ifndef DISABLE_WRAPPING
1209
1210
void wrap_reset(void)
{
1211
    same_line_wrap = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1212
}
1213
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1214

1215
#ifndef DISABLE_WRAPPING
1216
1217
1218
/* We wrap the given line.  Precondition: we assume the cursor has been
 * moved forward since the last typed character.  Return value: whether
 * we wrapped. */
1219
bool do_wrap(filestruct *inptr)
Chris Allegretta's avatar
Chris Allegretta committed
1220
{
1221
1222
1223
1224
1225
1226
    size_t len = strlen(inptr->data);
	/* Length of the line we wrap. */
    size_t i = 0;
	/* Generic loop variable. */
    int wrap_loc = -1;
	/* Index of inptr->data where we wrap. */
1227
    int word_back = -1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1228
#ifndef NANO_SMALL
1229
    const char *indentation = NULL;
1230
	/* Indentation to prepend to the new line. */
1231
    size_t indent_len = 0;	/* strlen(indentation) */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1232
#endif
1233
1234
    const char *after_break;	/* Text after the wrap point. */
    size_t after_break_len;	/* strlen(after_break) */
1235
    bool wrapping = FALSE;	/* Do we prepend to the next line? */
1236
    const char *wrap_line = NULL;
1237
	/* The next line, minus indentation. */
1238
1239
1240
    size_t wrap_line_len = 0;	/* strlen(wrap_line) */
    char *newline = NULL;	/* The line we create. */
    size_t new_line_len = 0;	/* Eventual length of newline. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1241

1242
1243
/* There are three steps.  First, we decide where to wrap.  Then, we
 * create the new wrap line.  Finally, we clean up. */
Chris Allegretta's avatar
Chris Allegretta committed
1244

1245
/* Step 1, finding where to wrap.  We are going to add a new line
1246
 * after a whitespace character.  In this step, we set wrap_loc as the
1247
1248
1249
1250
1251
1252
 * location of this replacement.
 *
 * Where should we break the line?  We need the last "legal wrap point"
 * such that the last word before it ended at or before fill.  If there
 * is no such point, we settle for the first legal wrap point.
 *
1253
1254
 * A "legal wrap point" is a whitespace character that is not followed
 * by whitespace.
1255
1256
1257
1258
1259
1260
1261
 *
 * If there is no legal wrap point or we found the last character of the
 * line, we should return without wrapping.
 *
 * Note that the initial indentation does not count as a legal wrap
 * point if we are going to auto-indent!
 *
1262
1263
 * Note that the code below could be optimized, by not calling
 * strnlenpt() so often. */
1264

1265
1266
1267
1268
1269
#ifndef NANO_SMALL
    if (ISSET(AUTOINDENT))
	i = indent_length(inptr->data);
#endif
    wrap_line = inptr->data + i;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1270
    for (; i < len; i++, wrap_line++) {
1271
	/* Record where the last word ended. */
1272
	if (!isblank(*wrap_line))
1273
	    word_back = i;
1274
1275
	/* If we have found a "legal wrap point" and the current word
	 * extends too far, then we stop. */
1276
1277
	if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
	    break;
1278
	/* We record the latest "legal wrap point". */
1279
	if (word_back != i && !isblank(wrap_line[1]))
1280
	    wrap_loc = i;
1281
    }
1282
1283
    if (i == len)
	return FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1284

1285
1286
1287
    /* Step 2, making the new wrap line.  It will consist of indentation
     * + after_break + " " + wrap_line (although indentation and
     * wrap_line are conditional on flags and #defines). */
Chris Allegretta's avatar
Chris Allegretta committed
1288

1289
1290
1291
1292
    /* after_break is the text that will be moved to the next line. */
    after_break = inptr->data + wrap_loc + 1;
    after_break_len = len - wrap_loc - 1;
    assert(after_break_len == strlen(after_break));
Chris Allegretta's avatar
Chris Allegretta committed
1293

1294
1295
1296
    /* new_line_len will later be increased by the lengths of indentation
     * and wrap_line. */
    new_line_len = after_break_len;
Chris Allegretta's avatar
Chris Allegretta committed
1297

1298
1299
1300
    /* We prepend the wrapped text to the next line, if the flag is set,
     * and there is a next line, and prepending would not make the line
     * too long. */
1301
    if (same_line_wrap && inptr->next) {
1302
1303
	wrap_line = inptr->next->data;
	wrap_line_len = strlen(wrap_line);
Chris Allegretta's avatar
Chris Allegretta committed
1304

1305
	/* +1 for the space between after_break and wrap_line. */
1306
	if ((new_line_len + 1 + wrap_line_len) <= fill) {
1307
	    wrapping = TRUE;
1308
1309
1310
	    new_line_len += (1 + wrap_line_len);
	}
    }
1311

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1312
#ifndef NANO_SMALL
1313
    if (ISSET(AUTOINDENT)) {
1314
1315
	/* Indentation comes from the next line if wrapping, else from
	 * this line. */
1316
1317
1318
	indentation = (wrapping ? wrap_line : inptr->data);
	indent_len = indent_length(indentation);
	if (wrapping)
1319
1320
	    /* The wrap_line text should not duplicate indentation.
	     * Note in this case we need not increase new_line_len. */
1321
1322
1323
	    wrap_line += indent_len;
	else
	    new_line_len += indent_len;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1324
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1325
1326
#endif

1327
1328
    /* Now we allocate the new line and copy into it. */
    newline = charalloc(new_line_len + 1);  /* +1 for \0 */
1329
    new_line_len = 0;
1330
    *newline = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1331

1332
1333
1334
1335
#ifndef NANO_SMALL
    if (ISSET(AUTOINDENT)) {
	strncpy(newline, indentation, indent_len);
	newline[indent_len] = '\0';
1336
	new_line_len = indent_len;
1337
    }
1338
#endif
1339
    strcat(newline, after_break);
1340
1341
1342
    new_line_len += after_break_len;
    /* We end the old line after wrap_loc.  Note that this does not eat
     * the space. */
1343
1344
    null_at(&inptr->data, wrap_loc + 1);
    totsize++;
1345
    if (wrapping) {
1346
	/* In this case, totsize increases by 1 since we add a space
1347
	 * between after_break and wrap_line.  If the line already ends
1348
1349
	 * in a tab or a space, we don't add a space and decrement
	 * totsize to account for that. */
1350
	if (!isblank(newline[new_line_len - 1]))
1351
1352
1353
	    strcat(newline, " ");
	else
	    totsize--;
1354
1355
1356
1357
1358
	strcat(newline, wrap_line);
	free(inptr->next->data);
	inptr->next->data = newline;
    } else {
	filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
1359

1360
1361
	/* In this case, the file size changes by +1 for the new line,
	 * and +indent_len for the new indentation. */
1362
1363
1364
1365
1366
1367
1368
1369
#ifndef NANO_SMALL
	totsize += indent_len;
#endif
	totlines++;
	temp->data = newline;
	temp->prev = inptr;
	temp->next = inptr->next;
	temp->prev->next = temp;
1370
1371
1372
	/* If temp->next is NULL, then temp is the last line of the
	 * file, so we must set filebot. */
	if (temp->next != NULL)
1373
1374
1375
1376
	    temp->next->prev = temp;
	else
	    filebot = temp;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1377

1378
1379
    /* Step 3, clean up.  Here we reposition the cursor and mark, and do
     * some other sundry things. */
Chris Allegretta's avatar
Chris Allegretta committed
1380

1381
    /* Later wraps of this line will be prepended to the next line. */
1382
    same_line_wrap = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
1383

1384
1385
1386
1387
    /* Each line knows its line number.  We recalculate these if we
     * inserted a new line. */
    if (!wrapping)
	renumber(inptr);
Chris Allegretta's avatar
Chris Allegretta committed
1388

1389
1390
1391
1392
1393
1394
    /* If the cursor was after the break point, we must move it. */
    if (current_x > wrap_loc) {
	current = current->next;
	current_x -=
#ifndef NANO_SMALL
		-indent_len +
1395
#endif
1396
1397
1398
1399
		wrap_loc + 1;
	wrap_reset();
	placewewant = xplustabs();
    }
Chris Allegretta's avatar
Chris Allegretta committed
1400

1401
#ifndef NANO_SMALL
1402
1403
    /* If the mark was on this line after the wrap point, we move it
     * down.  If it was on the next line and we wrapped, we move it
1404
1405
1406
1407
1408
1409
1410
     * right. */
    if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
	mark_beginbuf = inptr->next;
	mark_beginx -= wrap_loc - indent_len + 1;
    } else if (wrapping && mark_beginbuf == inptr->next)
	mark_beginx += after_break_len;
#endif /* !NANO_SMALL */
1411

1412
    return TRUE;
1413
}
1414
#endif /* !DISABLE_WRAPPING */
1415

1416
#ifndef DISABLE_SPELLER
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1417
/* A word is misspelled in the file.  Let the user replace it.  We
1418
1419
 * return FALSE if the user cancels. */
bool do_int_spell_fix(const char *word)
1420
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1421
    char *save_search, *save_replace;
1422
    size_t current_x_save = current_x, pww_save = placewewant;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1423
    filestruct *edittop_save = edittop, *current_save = current;
1424
	/* Save where we are. */
1425
    bool canceled = FALSE;
1426
	/* The return value. */
1427
    bool case_sens_set = ISSET(CASE_SENSITIVE);
1428
#ifndef NANO_SMALL
1429
1430
    bool reverse_search_set = ISSET(REVERSE_SEARCH);
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1431
#ifdef HAVE_REGEX_H
1432
1433
    bool regexp_set = ISSET(USE_REGEXP);
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1434
1435
1436
#ifndef NANO_SMALL
    bool old_mark_set = ISSET(MARK_ISSET);
#endif
1437

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

1441
#ifndef NANO_SMALL
1442
1443
    /* Make sure spell-check goes forward only. */
    UNSET(REVERSE_SEARCH);
1444
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1445
#ifdef HAVE_REGEX_H
1446
1447
1448
    /* Make sure spell-check doesn't use regular expressions. */
    UNSET(USE_REGEXP);
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1449
1450
1451
1452
#ifndef NANO_SMALL
    /* Make sure the marking highlight is off during spell-check. */
    UNSET(MARK_ISSET);
#endif
1453

1454
    /* Save the current search/replace strings. */
1455
1456
1457
    search_init_globals();
    save_search = last_search;
    save_replace = last_replace;
1458

1459
    /* Set search/replace strings to misspelled word. */
1460
1461
    last_search = mallocstrcpy(NULL, word);
    last_replace = mallocstrcpy(NULL, word);
1462

1463
    /* Start from the top of the file. */
1464
    edittop = fileage;
1465
    current = fileage;
1466
    current_x = -1;
1467
    placewewant = 0;
1468

1469
    /* Find the first whole-word occurrence of word. */
1470
    while (findnextstr(TRUE, TRUE, FALSE, fileage, 0, word, NULL)) {
1471
1472
	if (is_whole_word(current_x, current->data, word)) {
	    edit_refresh();
1473

1474
	    do_replace_highlight(TRUE, word);
1475

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1476
	    /* Allow all instances of the word to be corrected. */
1477
	    canceled = (statusq(FALSE, spell_list, word,
Chris Allegretta's avatar
Chris Allegretta committed
1478
#ifndef NANO_SMALL
1479
			NULL,
Chris Allegretta's avatar
Chris Allegretta committed
1480
#endif
1481
			 _("Edit a replacement")) == -1);
1482

1483
	    do_replace_highlight(FALSE, word);
1484

1485
	    if (!canceled && strcmp(word, answer) != 0) {
1486
		current_x--;
1487
1488
		do_replace_loop(word, current, &current_x, TRUE,
			&canceled);
1489
	    }
1490
1491

	    break;
1492
	}
1493
    }
Chris Allegretta's avatar
Chris Allegretta committed
1494

1495
1496
1497
1498
1499
    /* Restore the search/replace strings. */
    free(last_search);
    last_search = save_search;
    free(last_replace);
    last_replace = save_replace;
Chris Allegretta's avatar
Chris Allegretta committed
1500

1501
    /* Restore where we were. */
1502
    edittop = edittop_save;
1503
1504
    current = current_save;
    current_x = current_x_save;
1505
    placewewant = pww_save;
Chris Allegretta's avatar
Chris Allegretta committed
1506

1507
    /* Restore case sensitivity setting. */
1508
1509
1510
    if (!case_sens_set)
	UNSET(CASE_SENSITIVE);

1511
#ifndef NANO_SMALL
1512
1513
1514
    /* Restore search/replace direction. */
    if (reverse_search_set)
	SET(REVERSE_SEARCH);
1515
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1516
#ifdef HAVE_REGEX_H
1517
1518
1519
1520
    /* Restore regular expression usage setting. */
    if (regexp_set)
	SET(USE_REGEXP);
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1521
1522
1523
1524
1525
#ifndef NANO_SMALL
    /* Restore marking highlight. */
    if (old_mark_set)
	SET(MARK_ISSET);
#endif
1526

1527
    return !canceled;
Chris Allegretta's avatar
Chris Allegretta committed
1528
1529
}

1530
1531
/* Integrated spell checking using 'spell' program.  Return value: NULL
 * for normal termination, otherwise the error string. */
1532
const char *do_int_speller(const char *tempfile_name)
Chris Allegretta's avatar
Chris Allegretta committed
1533
{
1534
1535
    char *read_buff, *read_buff_ptr, *read_buff_word;
    size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
1536
    int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
1537
1538
    pid_t pid_spell, pid_sort, pid_uniq;
    int spell_status, sort_status, uniq_status;
Chris Allegretta's avatar
Chris Allegretta committed
1539

1540
    /* Create all three pipes up front. */
1541
1542
    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1)
	return _("Could not create pipe");
Chris Allegretta's avatar
Chris Allegretta committed
1543

1544
    statusbar(_("Creating misspelled word list, please wait..."));
1545

1546
    /* A new process to run spell in. */
1547
    if ((pid_spell = fork()) == 0) {
1548

1549
	/* Child continues (i.e, future spell process). */
1550

1551
	close(spell_fd[0]);
1552

1553
	/* Replace the standard input with the temp file. */
1554
1555
1556
1557
1558
1559
	if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
	    goto close_pipes_and_exit;

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

1560
	close(tempfile_fd);
1561

1562
	/* Send spell's standard output to the pipe. */
1563
1564
	if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
Chris Allegretta's avatar
Chris Allegretta committed
1565

1566
	close(spell_fd[1]);
1567

1568
	/* Start spell program; we are using PATH. */
1569
	execlp("spell", "spell", NULL);
1570

1571
	/* Should not be reached, if spell is found. */
1572
	exit(1);
1573
    }
Chris Allegretta's avatar
Chris Allegretta committed
1574

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

1578
    /* A new process to run sort in. */
1579
1580
    if ((pid_sort = fork()) == 0) {

1581
1582
	/* Child continues (i.e, future spell process).  Replace the
	 * standard input with the standard output of the old pipe. */
1583
1584
1585
	if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;

1586
1587
	close(spell_fd[0]);

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

	close(sort_fd[1]);

1594
1595
1596
	/* Start sort program.  Use -f to remove mixed case without
	 * having to have ANOTHER pipe for tr.  If this isn't portable,
	 * let me know. */
1597
1598
	execlp("sort", "sort", "-f", NULL);

1599
	/* Should not be reached, if sort is found. */
1600
1601
1602
	exit(1);
    }

1603
    close(spell_fd[0]);
1604
1605
    close(sort_fd[1]);

1606
    /* A new process to run uniq in. */
1607
1608
    if ((pid_uniq = fork()) == 0) {

1609
1610
	/* Child continues (i.e, future uniq process).  Replace the
	 * standard input with the standard output of the old pipe. */
1611
1612
1613
	if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
	    goto close_pipes_and_exit;

1614
1615
	close(sort_fd[0]);

1616
	/* Send uniq's standard output to the new pipe. */
1617
1618
	if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
	    goto close_pipes_and_exit;
1619
1620
1621

	close(uniq_fd[1]);

1622
	/* Start uniq program; we are using PATH. */
1623
1624
	execlp("uniq", "uniq", NULL);

1625
	/* Should not be reached, if uniq is found. */
1626
1627
1628
	exit(1);
    }

1629
    close(sort_fd[0]);
1630
    close(uniq_fd[1]);
1631

1632
    /* Child process was not forked successfully. */
1633
1634
    if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
	close(uniq_fd[0]);
1635
	return _("Could not fork");
1636
    }
1637

1638
    /* Get system pipe buffer size. */
1639
1640
    if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
	close(uniq_fd[0]);
1641
	return _("Could not get size of pipe buffer");
1642
    }
Chris Allegretta's avatar
Chris Allegretta committed
1643

1644
    /* Read in the returned spelling errors. */
1645
1646
1647
    read_buff_read = 0;
    read_buff_size = pipe_buff_size + 1;
    read_buff = read_buff_ptr = charalloc(read_buff_size);
Chris Allegretta's avatar
Chris Allegretta committed
1648

1649
    while ((bytesread = read(uniq_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
1650
1651
	read_buff_read += bytesread;
	read_buff_size += pipe_buff_size;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1652
	read_buff = read_buff_ptr = charealloc(read_buff, read_buff_size);
1653
	read_buff_ptr += read_buff_read;
1654

1655
    }
Chris Allegretta's avatar
Chris Allegretta committed
1656

1657
    *read_buff_ptr = (char)NULL;
1658
    close(uniq_fd[0]);
1659

1660
    /* Process the spelling errors. */
1661
    read_buff_word = read_buff_ptr = read_buff;
Chris Allegretta's avatar
Chris Allegretta committed
1662

1663
    while (*read_buff_ptr != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
1664

1665
	if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
1666
	    *read_buff_ptr = (char)NULL;
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
	    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;
	}
	read_buff_ptr++;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1677

1678
    /* Special case where last word doesn't end with \n or \r. */
1679
1680
    if (read_buff_word != read_buff_ptr)
	do_int_spell_fix(read_buff_word);
1681

1682
1683
    free(read_buff);
    replace_abort();
1684
    edit_refresh();
1685

1686
    /* Process end of spell process. */
1687
1688
1689
    waitpid(pid_spell, &spell_status, 0);
    waitpid(pid_sort, &sort_status, 0);
    waitpid(pid_uniq, &uniq_status, 0);
1690

1691
1692
    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
	return _("Error invoking \"spell\"");
1693

1694
1695
1696
1697
1698
1699
1700
1701
    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
	return _("Error invoking \"sort -f\"");

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

    /* Otherwise... */
    return NULL;
1702

1703
  close_pipes_and_exit:
1704

1705
    /* Don't leak any handles. */
1706
1707
1708
1709
1710
1711
1712
1713
    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);
Chris Allegretta's avatar
Chris Allegretta committed
1714
1715
}

1716
1717
/* External spell checking.  Return value: NULL for normal termination,
 * otherwise the error string. */
1718
const char *do_alt_speller(char *tempfile_name)
1719
{
1720
    int alt_spell_status, lineno_cur = current->lineno;
1721
1722
    int x_cur = current_x, y_cur = current_y;
    size_t pww_cur = placewewant;
1723
1724
1725
    pid_t pid_spell;
    char *ptr;
    static int arglen = 3;
1726
1727
    static char **spellargs = NULL;
    FILE *f;
1728
#ifndef NANO_SMALL
1729
    bool old_mark_set = ISSET(MARK_ISSET);
1730
1731
    int mbb_lineno_cur = 0;
	/* We're going to close the current file, and open the output of
1732
1733
1734
	 * the alternate spell command.  The line that mark_beginbuf
	 * points to will be freed, so we save the line number and
	 * restore afterwards. */
1735

1736
    if (old_mark_set) {
1737
1738
1739
	mbb_lineno_cur = mark_beginbuf->lineno;
	UNSET(MARK_ISSET);
    }
1740
#endif
1741

1742
    endwin();
1743

1744
    /* Set up an argument list to pass execvp(). */
1745
    if (spellargs == NULL) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1746
	spellargs = (char **)nmalloc(arglen * sizeof(char *));
1747

1748
1749
1750
	spellargs[0] = strtok(alt_speller, " ");
	while ((ptr = strtok(NULL, " ")) != NULL) {
	    arglen++;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1751
	    spellargs = (char **)nrealloc(spellargs, arglen * sizeof(char *));
1752
1753
1754
1755
1756
	    spellargs[arglen - 3] = ptr;
	}
	spellargs[arglen - 1] = NULL;
    }
    spellargs[arglen - 2] = tempfile_name;
Chris Allegretta's avatar
Chris Allegretta committed
1757

1758
    /* Start a new process for the alternate speller. */
1759
    if ((pid_spell = fork()) == 0) {
1760
	/* Start alternate spell program; we are using PATH. */
1761
	execvp(spellargs[0], spellargs);
Chris Allegretta's avatar
Chris Allegretta committed
1762

1763
1764
1765
	/* Should not be reached, if alternate speller is found!!! */
	exit(1);
    }
Chris Allegretta's avatar
Chris Allegretta committed
1766

1767
1768
    /* Could not fork?? */
    if (pid_spell < 0)
1769
	return _("Could not fork");
1770

1771
    /* Wait for alternate speller to complete. */
1772
    wait(&alt_spell_status);
1773

1774
1775
1776
1777
1778
1779
1780
1781
1782
    if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0) {
	char *altspell_error = NULL;
	char *invoke_error = _("Could not invoke \"%s\"");
	int msglen = strlen(invoke_error) + strlen(alt_speller) + 2;

	altspell_error = charalloc(msglen);
	snprintf(altspell_error, msglen, invoke_error, alt_speller);
	return altspell_error;
    }
1783
1784

    refresh();
1785

1786
1787
1788
    /* Restore the terminal to its previous state. */
    terminal_init();

1789
#ifndef NANO_SMALL
1790
    if (old_mark_set) {
1791
1792
	do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
	mark_beginbuf = current;
1793
	/* In case the line got shorter, assign mark_beginx. */
1794
1795
	mark_beginx = current_x;
	SET(MARK_ISSET);
1796
1797
1798
1799
1800
    } else {
#endif
	/* Only reload the temp file if it isn't a marked selection. */
	free_filestruct(fileage);
	global_init(TRUE);
1801
1802
1803
1804
1805
1806
1807

	/* Do what load_buffer() would do, except for making a new
	 * buffer for the temp file if multibuffer support is
	 * available. */
	open_file(tempfile_name, FALSE, &f);
	read_file(f, tempfile_name);
	current = fileage;
1808
#ifndef NANO_SMALL
1809
1810
    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1811

1812
1813
    /* Go back to the old position, mark the file as modified, and make
     * sure that the titlebar is refreshed. */
1814
1815
1816
1817
    do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
    set_modified();
    clearok(topwin, FALSE);
    titlebar(NULL);
1818

1819
    return NULL;
1820
}
1821

1822
void do_spell(void)
1823
{
1824
    int i;
1825
1826
    char *temp = safe_tempnam(0, "nano.");
    const char *spell_msg;
1827

1828
1829
    if (temp == NULL) {
	statusbar(_("Could not create temp file: %s"), strerror(errno));
1830
	return;
1831
    }
1832

1833
1834
#ifndef NANO_SMALL
    if (ISSET(MARK_ISSET))
1835
	i = write_marked(temp, TRUE, FALSE);
1836
1837
    else
#endif
1838
	i = write_file(temp, TRUE, FALSE, FALSE);
1839
1840

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

1846
#ifdef ENABLE_MULTIBUFFER
1847
1848
    /* Update the current open_files entry before spell-checking, in
     * case any problems occur. */
1849
    add_open_file(TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
1850
#endif
1851

1852
1853
    spell_msg = alt_speller != NULL ? do_alt_speller(temp) :
	do_int_speller(temp);
1854
    unlink(temp);
1855
    free(temp);
1856

1857
    if (spell_msg != NULL)
1858
1859
	statusbar(_("Spell checking failed: %s: %s"), spell_msg,
		strerror(errno));
1860
    else
1861
	statusbar(_("Finished checking spelling"));
1862
}
1863
#endif /* !DISABLE_SPELLER */
1864

1865
#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
1866
1867
/* The "indentation" of a line is the whitespace between the quote part
 * and the non-whitespace of the line. */
1868
1869
size_t indent_length(const char *line)
{
Chris Allegretta's avatar
Chris Allegretta committed
1870
    size_t len = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1871

Chris Allegretta's avatar
Chris Allegretta committed
1872
    assert(line != NULL);
1873
    while (isblank(*line)) {
Chris Allegretta's avatar
Chris Allegretta committed
1874
1875
	line++;
	len++;
Chris Allegretta's avatar
Chris Allegretta committed
1876
    }
Chris Allegretta's avatar
Chris Allegretta committed
1877
    return len;
Chris Allegretta's avatar
Chris Allegretta committed
1878
}
1879
#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
Chris Allegretta's avatar
Chris Allegretta committed
1880

Chris Allegretta's avatar
Chris Allegretta committed
1881
#ifndef DISABLE_JUSTIFY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1882
/* justify_format() replaces Tab by Space and multiple spaces by 1
1883
1884
1885
 * (except it maintains 2 after a non-repeated character in punct
 * followed by a character in brackets).  Note that the terminating \0
 * counts as a space.
Chris Allegretta's avatar
Chris Allegretta committed
1886
 *
1887
1888
 * justify_format() might make line->data shorter, and change the actual
 * pointer with null_at().
Chris Allegretta's avatar
Chris Allegretta committed
1889
 *
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1890
 * justify_format() will not look at the first skip characters of line.
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1891
1892
 * skip should be at most strlen(line->data).  The character at
 * line[skip + 1] must not be whitespace. */
1893
void justify_format(filestruct *line, size_t skip)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1894
{
Chris Allegretta's avatar
Chris Allegretta committed
1895
1896
1897
1898
1899
    char *back, *front;

    /* These four asserts are assumptions about the input data. */
    assert(line != NULL);
    assert(line->data != NULL);
1900
    assert(skip < strlen(line->data));
1901
    assert(!isblank(line->data[skip]));
Chris Allegretta's avatar
Chris Allegretta committed
1902
1903

    back = line->data + skip;
1904
    for (front = back; ; front++) {
1905
	bool remove_space = FALSE;
1906
1907
	    /* Do we want to remove this space? */

1908
	if (*front == '\t')
Chris Allegretta's avatar
Chris Allegretta committed
1909
	    *front = ' ';
1910

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1911
1912
	/* These tests are safe since line->data + skip is not a
	 * space. */
1913
	if ((*front == '\0' || *front == ' ') && *(front - 1) == ' ') {
1914
	    const char *bob = back - 2;
1915

1916
	    remove_space = TRUE;
1917
	    for (; bob >= line->data + skip; bob--) {
1918
		if (strchr(punct, *bob) != NULL) {
1919
1920
1921
1922
1923
		    /* If this character is in punct, don't remove the
		     * space unless this character and the character
		     * before it are the same. */
		    remove_space = (bob > line->data + skip &&
			*bob == *(bob - 1));
1924
1925
1926
1927
1928
1929
1930
1931
		    break;
		}
		if (strchr(brackets, *bob) == NULL)
		    break;
	    }
	}

	if (remove_space) {
Chris Allegretta's avatar
Chris Allegretta committed
1932
	    /* Now *front is a space we want to remove.  We do that by
1933
	     * simply failing to assign it to *back. */
Chris Allegretta's avatar
Chris Allegretta committed
1934
1935
1936
1937
#ifndef NANO_SMALL
	    if (mark_beginbuf == line && back - line->data < mark_beginx)
		mark_beginx--;
#endif
1938
1939
	    if (*front == '\0')
		*(back - 1) = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1940
1941
1942
1943
	} else {
	    *back = *front;
	    back++;
	}
1944
1945
	if (*front == '\0')
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1946
1947
    }

1948
    back--;
1949
    assert(*back == '\0' && *front == '\0');
Chris Allegretta's avatar
Chris Allegretta committed
1950

Chris Allegretta's avatar
Chris Allegretta committed
1951
1952
    /* Now back is the new end of line->data. */
    if (back != front) {
1953
	totsize -= front - back;
Chris Allegretta's avatar
Chris Allegretta committed
1954
1955
1956
1957
1958
	null_at(&line->data, back - line->data);
#ifndef NANO_SMALL
	if (mark_beginbuf == line && back - line->data < mark_beginx)
	    mark_beginx = back - line->data;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1959
    }
Chris Allegretta's avatar
Chris Allegretta committed
1960
}
Chris Allegretta's avatar
Chris Allegretta committed
1961

Chris Allegretta's avatar
Chris Allegretta committed
1962
1963
1964
1965
1966
1967
/* 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. */
1968
size_t quote_length(const char *line)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1969
{
1970
#ifdef HAVE_REGEX_H
Chris Allegretta's avatar
Chris Allegretta committed
1971
    regmatch_t matches;
1972
    int rc = regexec(&quotereg, line, 1, &matches, 0);
Chris Allegretta's avatar
Chris Allegretta committed
1973

1974
    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
Chris Allegretta's avatar
Chris Allegretta committed
1975
	return 0;
1976
1977
    /* matches.rm_so should be 0, since the quote string should start
     * with the caret ^. */
Chris Allegretta's avatar
Chris Allegretta committed
1978
1979
1980
    return matches.rm_eo;
#else	/* !HAVE_REGEX_H */
    size_t qdepth = 0;
1981

1982
    /* Compute quote depth level. */
1983
    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
1984
	qdepth += quotelen;
Chris Allegretta's avatar
Chris Allegretta committed
1985
1986
    return qdepth;
#endif	/* !HAVE_REGEX_H */
1987
}
Chris Allegretta's avatar
Chris Allegretta committed
1988

Chris Allegretta's avatar
Chris Allegretta committed
1989
1990
1991
/* 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. */
1992
1993
bool quotes_match(const char *a_line, size_t a_quote, const char
	*b_line)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1994
{
Chris Allegretta's avatar
Chris Allegretta committed
1995
    /* Here is the assumption about a_quote: */
1996
1997
    assert(a_quote == quote_length(a_line));
    return a_quote == quote_length(b_line) &&
1998
	strncmp(a_line, b_line, a_quote) == 0;
Chris Allegretta's avatar
Chris Allegretta committed
1999
}
2000

2001
2002
/* We assume a_line and b_line have no quote part.  Then, we return
 * whether b_line could follow a_line in a paragraph. */
2003
bool indents_match(const char *a_line, size_t a_indent, const char
2004
	*b_line, size_t b_indent)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2005
{
Chris Allegretta's avatar
Chris Allegretta committed
2006
2007
    assert(a_indent == indent_length(a_line));
    assert(b_indent == indent_length(b_line));
Chris Allegretta's avatar
Chris Allegretta committed
2008

2009
2010
    return b_indent <= a_indent &&
	strncmp(a_line, b_line, b_indent) == 0;
Chris Allegretta's avatar
Chris Allegretta committed
2011
}
Chris Allegretta's avatar
Chris Allegretta committed
2012

2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
/* 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 is not set.
 *   The reason for number 5) is that if AUTOINDENT is not set, then an
 *   indented line is expected to start a paragraph, like in books.
 *   Thus, nano can justify an indented paragraph only if AUTOINDENT is
 *   turned on. */
bool begpar(const filestruct *const foo)
{
    size_t quote_len;
    size_t indent_len;
    size_t temp_id_len;

    /* Case 1). */
    if (foo->prev == NULL)
	return TRUE;

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

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

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

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

    /* Case 2) or 5) or 4). */
    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
	(quote_len == 0 && indent_len > 0
#ifndef NANO_SMALL
	&& !ISSET(AUTOINDENT)
#endif
	) || !indents_match(foo->prev->data + quote_len, temp_id_len,
	foo->data + quote_len, indent_len))
	return TRUE;

    return FALSE;
}

/* We find the last beginning-of-paragraph line before the current
 * line. */
void do_para_begin(void)
{
    const filestruct *old_current = current;
    const size_t old_pww = placewewant;

    current_x = 0;
    placewewant = 0;

    if (current->prev != NULL) {
	do {
	    current = current->prev;
2085
	    current_y--;
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
	} while (!begpar(current));
    }

    edit_redraw(old_current, old_pww);
}

bool inpar(const char *str)
{
    size_t quote_len = quote_length(str);

    return str[quote_len + indent_length(str + quote_len)] != '\0';
}

/* A line is the last line of a paragraph if it is in a paragraph, and
 * the next line isn't, or is the beginning of a paragraph.  We move
 * down to the end of a paragraph, then one line farther. */
void do_para_end(void)
{
    const filestruct *const old_current = current;
    const size_t old_pww = placewewant;

    current_x = 0;
    placewewant = 0;

    while (current->next != NULL && !inpar(current->data))
	current = current->next;

    while (current->next != NULL && inpar(current->next->data) &&
2114
	    !begpar(current->next)) {
2115
	current = current->next;
2116
2117
	current_y++;
    }
2118
2119
2120
2121
2122
2123
2124

    if (current->next != NULL)
	current = current->next;

    edit_redraw(old_current, old_pww);
}

Chris Allegretta's avatar
Chris Allegretta committed
2125
/* Put the next par_len lines, starting with first_line, in the cut
2126
2127
2128
 * buffer, not allowing them to be concatenated.  We assume there are
 * enough lines after first_line.  We leave copies of the lines in
 * place, too.  We return the new copy of first_line. */
2129
2130
filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
	quote_len)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2131
{
2132
2133
2134
    /* We put the original lines, not copies, into the cutbuffer, just
     * out of a misguided sense of consistency, so if you uncut, you get
     * the actual same paragraph back, not a copy. */
2135
    filestruct *alice = first_line;
Chris Allegretta's avatar
Chris Allegretta committed
2136
2137

    set_modified();
2138
    cutbuffer = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2139
    for (; par_len > 0; par_len--) {
2140
	filestruct *bob = copy_node(alice);
Chris Allegretta's avatar
Chris Allegretta committed
2141

2142
	if (alice == first_line)
Chris Allegretta's avatar
Chris Allegretta committed
2143
	    first_line = bob;
2144
	if (alice == current)
Chris Allegretta's avatar
Chris Allegretta committed
2145
	    current = bob;
2146
	if (alice == edittop)
Chris Allegretta's avatar
Chris Allegretta committed
2147
2148
	    edittop = bob;
#ifndef NANO_SMALL
2149
	if (alice == mark_beginbuf)
Chris Allegretta's avatar
Chris Allegretta committed
2150
2151
2152
	    mark_beginbuf = bob;
#endif

2153
	assert(alice != NULL && bob != NULL);
2154
	add_to_cutbuffer(alice, FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2155
	splice_node(bob->prev, bob, bob->next);
2156
	alice = bob->next;
Chris Allegretta's avatar
Chris Allegretta committed
2157
2158
2159
2160
    }
    return first_line;
}

Chris Allegretta's avatar
Chris Allegretta committed
2161
/* Is it possible to break line at or before goal? */
2162
bool breakable(const char *line, ssize_t goal)
Chris Allegretta's avatar
Chris Allegretta committed
2163
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2164
    for (; *line != '\0' && goal >= 0; line++) {
2165
	if (isblank(*line))
Chris Allegretta's avatar
Chris Allegretta committed
2166
2167
	    return TRUE;

2168
	if (is_cntrl_char(*line))
Chris Allegretta's avatar
Chris Allegretta committed
2169
2170
2171
2172
	    goal -= 2;
	else
	    goal -= 1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
2173
2174
2175
    /* If goal is not negative, the whole line (one word) was short
     * enough. */
    return goal >= 0;
Chris Allegretta's avatar
Chris Allegretta committed
2176
2177
}

Chris Allegretta's avatar
Chris Allegretta committed
2178
/* We are trying to break a chunk off line.  We find the last space such
2179
 * that the display length to there is at most goal + 1.  If there is no
2180
2181
2182
 * such space, and force is TRUE, then we find the first space.  Anyway,
 * we then take the last space in that group of spaces.  The terminating
 * '\0' counts as a space. */
2183
int break_line(const char *line, ssize_t goal, bool force)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2184
{
2185
    ssize_t space_loc = -1;
Chris Allegretta's avatar
Chris Allegretta committed
2186
2187
	/* Current tentative return value.  Index of the last space we
	 * found with short enough display width.  */
2188
    ssize_t cur_loc = 0;
2189
	/* Current index in line. */
Chris Allegretta's avatar
Chris Allegretta committed
2190
2191

    assert(line != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2192
    for (; *line != '\0' && goal >= 0; line++, cur_loc++) {
Chris Allegretta's avatar
Chris Allegretta committed
2193
2194
2195
2196
	if (*line == ' ')
	    space_loc = cur_loc;
	assert(*line != '\t');

Chris Allegretta's avatar
Chris Allegretta committed
2197
	if (is_cntrl_char(*line))
Chris Allegretta's avatar
Chris Allegretta committed
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
	    goal -= 2;
	else
	    goal--;
    }
    if (goal >= 0)
	/* In fact, the whole line displays shorter than goal. */
	return cur_loc;
    if (space_loc == -1) {
	/* No space found short enough. */
	if (force)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2208
	    for (; *line != '\0'; line++, cur_loc++)
2209
		if (*line == ' ' && *(line + 1) != ' ' && *(line + 1) != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
2210
2211
2212
2213
		    return cur_loc;
	return -1;
    }
    /* Perhaps the character after space_loc is a space.  But because
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2214
     * of justify_format(), there can be only two adjacent. */
Chris Allegretta's avatar
Chris Allegretta committed
2215
2216
2217
2218
2219
2220
    if (*(line - cur_loc + space_loc + 1) == ' ' ||
	*(line - cur_loc + space_loc + 1) == '\0')
	space_loc++;
    return space_loc;
}

2221
2222
2223
2224
2225
/* 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 FALSE
 * if we found a paragraph, or TRUE if there was an error or we didn't
 * find a paragraph.
Chris Allegretta's avatar
Chris Allegretta committed
2226
 *
2227
2228
2229
 * See the comment at begpar() for more about when a line is the
 * beginning of a paragraph. */
bool do_para_search(size_t *const quote, size_t *const par)
2230
{
Chris Allegretta's avatar
Chris Allegretta committed
2231
    size_t quote_len;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2232
	/* Length of the initial quotation of the paragraph we
2233
	 * search. */
Chris Allegretta's avatar
Chris Allegretta committed
2234
2235
    size_t par_len;
	/* Number of lines in that paragraph. */
2236
2237
2238
2239
    size_t indent_len;
	/* Generic indentation length. */
    filestruct *line;
	/* Generic line of text. */
2240

Chris Allegretta's avatar
Chris Allegretta committed
2241
#ifdef HAVE_REGEX_H
2242
2243
2244
    if (quoterc != 0) {
	statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
	return TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
2245
    }
Chris Allegretta's avatar
Chris Allegretta committed
2246
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2247

Chris Allegretta's avatar
Chris Allegretta committed
2248
2249
    /* Here is an assumption that is always true anyway. */
    assert(current != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
2250

2251
2252
    current_x = 0;

2253
    quote_len = quote_length(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
2254
    indent_len = indent_length(current->data + quote_len);
Robert Siemborski's avatar
Robert Siemborski committed
2255

2256
2257
2258
2259
    /* Here we find the first line of the paragraph to search.  If the
     * current line is in a paragraph, then we move back to the first
     * line of the paragraph.  Otherwise, we move to the first line that
     * is in a paragraph. */
Chris Allegretta's avatar
Chris Allegretta committed
2260
2261
    if (current->data[quote_len + indent_len] != '\0') {
	/* This line is part of a paragraph.  So we must search back to
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2262
2263
	 * the first line of this paragraph.  First we check items 1)
	 * and 3) above. */
2264
2265
	while (current->prev != NULL &&	quotes_match(current->data,
		quote_len, current->prev->data)) {
2266
	    size_t temp_id_len =
2267
		indent_length(current->prev->data + quote_len);
2268
		/* The indentation length of the previous line. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2269

2270
	    /* Is this line the beginning of a paragraph, according to
2271
	     * items 2), 5), or 4) above?  If so, stop. */
2272
	    if (current->prev->data[quote_len + temp_id_len] == '\0' ||
2273
		(quote_len == 0 && indent_len > 0
2274
#ifndef NANO_SMALL
2275
		&& !ISSET(AUTOINDENT)
2276
#endif
2277
2278
		) || !indents_match(current->prev->data + quote_len,
		temp_id_len, current->data + quote_len, indent_len))
2279
2280
2281
2282
		break;
	    indent_len = temp_id_len;
	    current = current->prev;
	    current_y--;
Chris Allegretta's avatar
Chris Allegretta committed
2283
	}
Chris Allegretta's avatar
Chris Allegretta committed
2284
    } else {
Chris Allegretta's avatar
Chris Allegretta committed
2285
2286
	/* This line is not part of a paragraph.  Move down until we get
	 * to a non "blank" line. */
Chris Allegretta's avatar
Chris Allegretta committed
2287
	do {
2288
	    /* There is no next paragraph, so nothing to move to. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2289
2290
	    if (current->next == NULL) {
		placewewant = 0;
2291
		return TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2292
	    }
Chris Allegretta's avatar
Chris Allegretta committed
2293
	    current = current->next;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2294
	    current_y++;
2295
	    quote_len = quote_length(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
2296
2297
2298
	    indent_len = indent_length(current->data + quote_len);
	} while (current->data[quote_len + indent_len] == '\0');
    }
2299

2300
2301
    /* Now current is the first line of the paragraph, and quote_len is
     * the quotation length of that line. */
Chris Allegretta's avatar
Chris Allegretta committed
2302

2303
2304
    /* Next step, compute par_len, the number of lines in this
     * paragraph. */
Chris Allegretta's avatar
Chris Allegretta committed
2305
2306
2307
2308
    line = current;
    par_len = 1;
    indent_len = indent_length(line->data + quote_len);

2309
2310
2311
    while (line->next != NULL &&
	    quotes_match(current->data, quote_len, line->next->data)) {
	size_t temp_id_len = indent_length(line->next->data + quote_len);
2312

2313
	if (!indents_match(line->data + quote_len, indent_len,
2314
		line->next->data + quote_len, temp_id_len) ||
Chris Allegretta's avatar
Chris Allegretta committed
2315
2316
2317
		line->next->data[quote_len + temp_id_len] == '\0' ||
		(quote_len == 0 && temp_id_len > 0
#ifndef NANO_SMALL
2318
		&& !ISSET(AUTOINDENT)
Chris Allegretta's avatar
Chris Allegretta committed
2319
2320
#endif
		))
2321
2322
2323
2324
	    break;
	indent_len = temp_id_len;
	line = line->next;
	par_len++;
2325
2326
    }

2327
2328
    /* Now par_len is the number of lines in this paragraph.  We should
     * never call quotes_match() or quote_length() again. */
2329

2330
2331
2332
2333
    /* Save the values of quote_len and par_len. */
    assert(quote != NULL && par != NULL);
    *quote = quote_len;
    *par = par_len;
2334

2335
    return FALSE;
2336
2337
}

2338
2339
/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
 * the current paragraph. */
2340
void do_justify(bool full_justify)
2341
{
2342
2343
2344
    filestruct *first_par_line = NULL;
	/* Will be the first line of the resulting justified paragraph.
	 * For restoring after uncut. */
2345
    filestruct *last_par_line;
2346
2347
2348
	/* Will be the last line of the result, also for uncut. */
    filestruct *cutbuffer_save = cutbuffer;
	/* When the paragraph gets modified, all lines from the changed
2349
	 * one down are stored in the cutbuffer.  We back up the
2350
	 * original to restore it later. */
2351
    bool allow_respacing;
2352
	/* Whether we should change the spacing at the end of a line
2353
	 * after justifying it.  This should be TRUE whenever we move
2354
	 * to the next line after justifying the current line. */
2355
2356
2357
2358
2359

    /* We save these global variables to be restored if the user
     * unjustifies.  Note we don't need to save totlines. */
    int current_x_save = current_x;
    int current_y_save = current_y;
2360
    long flags_save = flags;
2361
    long totsize_save = totsize;
2362
    filestruct *current_save = current;
2363
2364
2365
2366
2367
    filestruct *edittop_save = edittop;
#ifndef NANO_SMALL
    filestruct *mark_beginbuf_save = mark_beginbuf;
    int mark_beginx_save = mark_beginx;
#endif
2368
    int kbinput;
2369
    bool meta_key, func_key;
2370

2371
2372
    /* If we're justifying the entire file, start at the beginning. */
    if (full_justify)
2373
	current = fileage;
2374
2375

    last_par_line = current;
2376
2377

    while (TRUE) {
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
	size_t quote_len;
	    /* Length of the initial quotation of the paragraph we
	     * justify. */
	size_t par_len;
	    /* Number of lines in that paragraph. */

	/* 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
	 * and we're justifying the whole file, move the last line of
	 * the text we're justifying to just before the magicline, which
	 * is where it'll be anyway if we've searched the entire file,
	 * and break out of the loop; otherwise, refresh the screen and
	 * get out. */
	if (do_para_search(&quote_len, &par_len)) {
2395
2396
2397
2398
2399
2400
2401
2402
	    if (full_justify) {
		/* This should be safe in the event of filebot->prev's
		 * being NULL, since only last_par_line->next is used if
		 * we eventually unjustify. */
		last_par_line = filebot->prev;
		break;
	    } else {
		edit_refresh();
2403
		return;
2404
2405
	    }
	}
2406

2407
2408
2409
	/* Next step, we loop through the lines of this paragraph,
	 * justifying each one individually. */
	for (; par_len > 0; current_y++, par_len--) {
2410
	    size_t indent_len;	/* Generic indentation length. */
2411
2412
2413
	    size_t line_len;
	    size_t display_len;
		/* The width of current in screen columns. */
2414
	    ssize_t break_pos;
2415
2416
		/* Where we will break the line. */

2417
2418
2419
2420
2421
	    /* We'll be moving to the next line after justifying the
	     * current line in almost all cases, so allow changing the
	     * spacing at the ends of justified lines by default. */
	    allow_respacing = TRUE;

2422
2423
	    indent_len = quote_len + indent_length(current->data +
		quote_len);
2424

2425
2426
	    /* If we haven't already done it, copy the original
	     * paragraph to the cutbuffer for unjustification. */
2427
2428
	    if (first_par_line == NULL)
		first_par_line = backup_lines(current, full_justify ?
2429
2430
			filebot->lineno - current->lineno : par_len, quote_len);

2431
2432
2433
2434
2435
2436
	    /* Now we call justify_format() on the current line of the
	     * paragraph, which will remove excess spaces from it and
	     * change tabs to spaces. */
	    justify_format(current, quote_len +
		indent_length(current->data + quote_len));

2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
	    line_len = strlen(current->data);
	    display_len = strlenpt(current->data);

	    if (display_len > fill) {
		/* The line is too long.  Try to wrap it to the next. */
	        break_pos = break_line(current->data + indent_len,
			fill - strnlenpt(current->data, indent_len), TRUE);
		if (break_pos == -1 || break_pos + indent_len == line_len)
		    /* We can't break the line, or don't need to, so
		     * just go on to the next. */
		    goto continue_loc;
		break_pos += indent_len;
		assert(break_pos < line_len);
		if (par_len == 1) {
		    /* There is no next line in this paragraph.  We make
		     * a new line and copy text after break_pos into
		     * it. */
		    splice_node(current, make_new_node(current), current->next);
		    /* In a non-quoted paragraph, we copy the indent
		     * only if AUTOINDENT is turned on. */
2457
		    if (quote_len == 0
Chris Allegretta's avatar
Chris Allegretta committed
2458
#ifndef NANO_SMALL
2459
			&& !ISSET(AUTOINDENT)
2460
#endif
2461
			)
2462
2463
2464
2465
2466
			    indent_len = 0;
		    current->next->data = charalloc(indent_len + line_len -
			break_pos);
		    strncpy(current->next->data, current->data, indent_len);
		    strcpy(current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2467
			current->data + break_pos + 1);
2468
		    assert(strlen(current->next->data) ==
Chris Allegretta's avatar
Chris Allegretta committed
2469
			indent_len + line_len - break_pos - 1);
2470
2471
2472
2473
2474
		    totlines++;
		    totsize += indent_len;
		    par_len++;
		} else {
		    size_t next_line_len = strlen(current->next->data);
Chris Allegretta's avatar
Chris Allegretta committed
2475

2476
		    indent_len = quote_len +
Chris Allegretta's avatar
Chris Allegretta committed
2477
			indent_length(current->next->data + quote_len);
2478
		    current->next->data = charealloc(current->next->data,
Chris Allegretta's avatar
Chris Allegretta committed
2479
			next_line_len + line_len - break_pos + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2480

2481
2482
		    charmove(current->next->data + indent_len + line_len -
			break_pos, current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2483
			next_line_len - indent_len + 1);
2484
		    strcpy(current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2485
			current->data + break_pos + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2486
2487
		    current->next->data[indent_len + line_len -
			break_pos - 1] = ' ';
Chris Allegretta's avatar
Chris Allegretta committed
2488
#ifndef NANO_SMALL
2489
2490
2491
2492
2493
		    if (mark_beginbuf == current->next) {
			if (mark_beginx < indent_len)
			    mark_beginx = indent_len;
			mark_beginx += line_len - break_pos;
		    }
Chris Allegretta's avatar
Chris Allegretta committed
2494
#endif
2495
		}
Chris Allegretta's avatar
Chris Allegretta committed
2496
#ifndef NANO_SMALL
2497
2498
2499
2500
		if (mark_beginbuf == current && mark_beginx > break_pos) {
		    mark_beginbuf = current->next;
		    mark_beginx -= break_pos + 1 - indent_len;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2501
#endif
2502
		null_at(&current->data, break_pos);
2503
2504

		/* Go to the next line. */
2505
2506
2507
		current = current->next;
	    } else if (display_len < fill && par_len > 1) {
		size_t next_line_len;
Chris Allegretta's avatar
Chris Allegretta committed
2508

2509
		indent_len = quote_len +
Chris Allegretta's avatar
Chris Allegretta committed
2510
			indent_length(current->next->data + quote_len);
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
		/* If we can't pull a word from the next line up to this
		 * one, just go on. */
		if (!breakable(current->next->data + indent_len,
			fill - display_len - 1))
		    goto continue_loc;

		break_pos = break_line(current->next->data + indent_len,
			fill - display_len - 1, FALSE);
		assert(break_pos != -1);

		current->data = charealloc(current->data,
			line_len + break_pos + 2);
		current->data[line_len] = ' ';
		strncpy(current->data + line_len + 1,
Chris Allegretta's avatar
Chris Allegretta committed
2525
			current->next->data + indent_len, break_pos);
2526
		current->data[line_len + break_pos + 1] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
2527
#ifndef NANO_SMALL
2528
2529
		if (mark_beginbuf == current->next) {
		    if (mark_beginx < indent_len + break_pos) {
2530
			mark_beginbuf = current;
2531
2532
2533
			if (mark_beginx <= indent_len)
			    mark_beginx = line_len + 1;
			else
2534
2535
			    mark_beginx = line_len + 1 + mark_beginx -
				indent_len;
2536
2537
2538
		    } else
			mark_beginx -= break_pos + 1;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2539
#endif
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
		next_line_len = strlen(current->next->data);
		if (indent_len + break_pos == next_line_len) {
		    filestruct *line = current->next;

		    /* Don't destroy edittop! */
		    if (line == edittop)
			edittop = current;

		    unlink_node(line);
		    delete_node(line);
		    totlines--;
		    totsize -= indent_len;
		    current_y--;
2553

2554
2555
		    /* Don't go to the next line.  Accordingly, don't
		     * allow changing the spacing at the end of the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2556
2557
		     * previous justified line, so that we don't end up
		     * doing it more than once on the same line. */
2558
		    allow_respacing = FALSE;
2559
2560
		} else {
		    charmove(current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2561
2562
			current->next->data + indent_len + break_pos + 1,
			next_line_len - break_pos - indent_len);
2563
		    null_at(&current->next->data, next_line_len - break_pos);
2564
2565

		    /* Go to the next line. */
2566
2567
2568
		    current = current->next;
		}
	    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2569
  continue_loc:
2570
		/* Go to the next line. */
2571
		current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
2572

2573
	    /* We've moved to the next line after justifying the
2574
2575
2576
2577
2578
2579
2580
	     * current line.  If the justified line was not the last
	     * line of the paragraph, add a space to the end of it to
	     * replace the one removed or left out by justify_format().
	     * If it was the last line of the paragraph, and
	     * justify_format() left a space on the end of it, remove
	     * the space. */
	    if (allow_respacing) {
2581
		size_t prev_line_len = strlen(current->prev->data);
2582
2583
2584

		if (par_len > 1) {
		    current->prev->data = charealloc(current->prev->data,
2585
			prev_line_len + 2);
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
		    current->prev->data[prev_line_len] = ' ';
		    current->prev->data[prev_line_len + 1] = '\0';
		    totsize++;
		} else if (par_len == 1 &&
			current->prev->data[prev_line_len - 1] == ' ') {
		    current->prev->data = charealloc(current->prev->data,
			prev_line_len);
		    current->prev->data[prev_line_len - 1] = '\0';
		    totsize--;
		}
2596
	    }
2597
2598
	}

2599
2600
2601
	/* We've just justified a 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. */
2602
2603
2604
2605
	if (!full_justify)
	    break;

    } /* while (TRUE) */
2606

2607
2608
2609
    /* We are now done justifying the paragraph or the file, so clean
     * up.  totlines, totsize, and current_y have been maintained above.
     * Set last_par_line to the new end of the paragraph, update
2610
2611
2612
     * fileage, and renumber() since edit_refresh() needs the line
     * numbers to be right (but only do the last two if we actually
     * justified something). */
2613
    last_par_line = current->prev;
2614
2615
2616
2617
2618
    if (first_par_line != NULL) {
	if (first_par_line->prev == NULL)
	    fileage = first_par_line;
	renumber(first_par_line);
    }
2619

2620
    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
2621

2622
    statusbar(_("Can now UnJustify!"));
2623

2624
    /* Display the shortcut list with UnJustify. */
2625
    shortcut_init(TRUE);
2626
    display_main_list();
2627

Chris Allegretta's avatar
Chris Allegretta committed
2628
    /* Now get a keystroke and see if it's unjustify; if not, unget the
2629
     * keystroke and return. */
2630
    kbinput = get_edit_input(&meta_key, &func_key, FALSE);
2631

2632
    if (!meta_key && !func_key && kbinput == NANO_UNJUSTIFY_KEY) {
2633
	/* Restore the justify we just did (ungrateful user!). */
2634
2635
	filestruct *cutbottom = get_cutbottom();

Chris Allegretta's avatar
Chris Allegretta committed
2636
2637
2638
2639
2640
	current = current_save;
	current_x = current_x_save;
	current_y = current_y_save;
	edittop = edittop_save;

2641
2642
2643
2644
2645
	/* Splice the cutbuffer back into the file, but only if we
	 * actually justified something. */
	if (first_par_line != NULL) {
	    cutbottom->next = last_par_line->next;
	    cutbottom->next->prev = cutbottom;
2646
2647
	    /* The line numbers after the end of the paragraph have been
	     * changed, so we change them back. */
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
	    renumber(cutbottom->next);
	    if (first_par_line->prev != NULL) {
		cutbuffer->prev = first_par_line->prev;
		cutbuffer->prev->next = cutbuffer;
	    } else
		fileage = cutbuffer;

	    last_par_line->next = NULL;
	    free_filestruct(first_par_line);
	}
2658
2659
2660
2661

	/* Restore global variables from before the justify. */
	totsize = totsize_save;
	totlines = filebot->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2662
#ifndef NANO_SMALL
2663
2664
	mark_beginbuf = mark_beginbuf_save;
	mark_beginx = mark_beginx_save;
Chris Allegretta's avatar
Chris Allegretta committed
2665
#endif
2666
	flags = flags_save;
2667
	if (!ISSET(MODIFIED))
2668
	    titlebar(NULL);
Chris Allegretta's avatar
Chris Allegretta committed
2669
	edit_refresh();
2670
2671
    } else {
	placewewant = 0;
2672
	unget_kbinput(kbinput, meta_key, func_key);
2673
    }
2674

Chris Allegretta's avatar
Chris Allegretta committed
2675
    cutbuffer = cutbuffer_save;
2676
2677
    /* Note that now cutbottom is invalid, but that's okay. */
    blank_statusbar();
2678

2679
    /* Display the shortcut list with UnCut. */
2680
    shortcut_init(FALSE);
2681
    display_main_list();
Chris Allegretta's avatar
Chris Allegretta committed
2682
}
2683

2684
void do_justify_void(void)
2685
{
2686
    do_justify(FALSE);
2687
2688
}

2689
void do_full_justify(void)
2690
{
2691
    do_justify(TRUE);
2692
}
2693
#endif /* !DISABLE_JUSTIFY */
Chris Allegretta's avatar
Chris Allegretta committed
2694

2695
void do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
2696
{
2697
2698
    int i;

2699
2700
    if (!ISSET(MODIFIED))
	i = 0;		/* Pretend the user chose not to save. */
2701
    else if (ISSET(TEMP_FILE))
2702
	i = 1;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2703
    else
2704
2705
2706
	i = do_yesno(FALSE,
		_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));

2707
2708
#ifdef DEBUG
    dump_buffer(fileage);
2709
#endif
2710

2711
    if (i == 0 || (i == 1 && do_writeout(TRUE) > 0)) {
2712
#ifdef ENABLE_MULTIBUFFER
2713
	/* Exit only if there are no more open buffers. */
2714
	if (!close_open_file())
2715
#endif
2716
	    finish();
2717
    } else if (i != 1)
2718
2719
2720
2721
2722
2723
2724
	statusbar(_("Cancelled"));

    display_main_list();
}

void signal_init(void)
{
2725
2726
    /* Trap SIGINT and SIGQUIT because we want them to do useful
     * things. */
2727
2728
2729
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
2730
    sigaction(SIGQUIT, &act, NULL);
2731

2732
    /* Trap SIGHUP and SIGTERM because we want to write the file out. */
2733
    act.sa_handler = handle_hupterm;
2734
    sigaction(SIGHUP, &act, NULL);
2735
    sigaction(SIGTERM, &act, NULL);
2736

2737
#ifndef NANO_SMALL
2738
    /* Trap SIGWINCH because we want to handle window resizes. */
2739
2740
    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);
2741
    allow_pending_sigwinch(FALSE);
2742
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2743

2744
    /* Trap normal suspend (^Z) so we can handle it ourselves. */
2745
2746
2747
2748
    if (!ISSET(SUSPEND)) {
	act.sa_handler = SIG_IGN;
	sigaction(SIGTSTP, &act, NULL);
    } else {
2749
2750
	/* Block all other signals in the suspend and continue handlers.
	 * If we don't do this, other stuff interrupts them! */
2751
	sigfillset(&act.sa_mask);
Chris Allegretta's avatar
Chris Allegretta committed
2752

2753
2754
	act.sa_handler = do_suspend;
	sigaction(SIGTSTP, &act, NULL);
2755

2756
2757
2758
2759
	act.sa_handler = do_cont;
	sigaction(SIGCONT, &act, NULL);
    }
}
2760

2761
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
2762
RETSIGTYPE handle_hupterm(int signal)
2763
{
2764
    die(_("Received SIGHUP or SIGTERM\n"));
2765
}
2766

2767
/* Handler for SIGTSTP (suspend). */
2768
2769
2770
RETSIGTYPE do_suspend(int signal)
{
    endwin();
2771
    printf("\n\n\n\n\n%s\n", _("Use \"fg\" to return to nano"));
2772
    fflush(stdout);
2773

2774
    /* Restore the old terminal settings. */
2775
    tcsetattr(0, TCSANOW, &oldterm);
2776

2777
    /* Trap SIGHUP and SIGTERM so we can properly deal with them while
2778
     * suspended. */
2779
2780
2781
2782
    act.sa_handler = handle_hupterm;
    sigaction(SIGHUP, &act, NULL);
    sigaction(SIGTERM, &act, NULL);

2783
    /* Do what mutt does: send ourselves a SIGSTOP. */
2784
2785
    kill(0, SIGSTOP);
}
2786

2787
/* Handler for SIGCONT (continue after suspend). */
2788
2789
RETSIGTYPE do_cont(int signal)
{
2790
#ifndef NANO_SMALL
2791
2792
    /* Perhaps the user resized the window while we slept.  Handle it
     * and update the screen in the process. */
2793
    handle_sigwinch(0);
2794
#else
2795
2796
    /* Just update the screen. */
    doupdate();
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
#endif
}

#ifndef NANO_SMALL
void handle_sigwinch(int s)
{
    const char *tty = ttyname(0);
    int fd;
    int result = 0;
    struct winsize win;

2808
    if (tty == NULL)
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
	return;
    fd = open(tty, O_RDWR);
    if (fd == -1)
	return;
    result = ioctl(fd, TIOCGWINSZ, &win);
    close(fd);
    if (result == -1)
	return;

    /* Could check whether the COLS or LINES changed, and return
     * otherwise.  EXCEPT, that COLS and LINES are ncurses global
     * variables, and in some cases ncurses has already updated them. 
     * But not in all cases, argh. */
    COLS = win.ws_col;
    LINES = win.ws_row;
2824
2825
    editwinrows = LINES - 5 + no_help();
    if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
2826
2827
2828
2829
2830
2831
	die_too_small();

#ifndef DISABLE_WRAPJUSTIFY
    fill = wrap_at;
    if (fill <= 0)
	fill += COLS;
2832
2833
    if (fill < 0)
	fill = 0;
2834
2835
#endif

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2836
    hblank = charealloc(hblank, COLS + 1);
2837
2838
2839
    memset(hblank, ' ', COLS);
    hblank[COLS] = '\0';

2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 1: If we just do what
     * curses does here, it'll only work properly if the resize made the
     * window smaller.  Do what mutt does: Leave and immediately reenter
     * Slang screen management mode. */
    SLsmg_reset_smg();
    SLsmg_init_smg();
#else
    /* Do the equivalent of what Minimum Profit does: Leave and
     * immediately reenter curses mode. */
    endwin();
    refresh();
#endif
2853

2854
2855
2856
    /* Restore the terminal to its previous state. */
    terminal_init();

2857
2858
2859
2860
2861
2862
    /* Do the equivalent of what both mutt and Minimum Profit do:
     * Reinitialize all the windows based on the new screen
     * dimensions. */
    window_init();

    /* Redraw the contents of the windows that need it. */
2863
    blank_statusbar();
2864
    display_main_list();
2865
2866
    total_refresh();

2867
    /* Turn cursor back on for sure. */
2868
2869
    curs_set(1);

2870
2871
2872
    /* Reset all the input routines that rely on character sequences. */
    reset_kbinput();

2873
    /* Jump back to the main loop. */
2874
2875
    siglongjmp(jmpbuf, 1);
}
2876

2877
void allow_pending_sigwinch(bool allow)
2878
2879
2880
2881
2882
2883
2884
2885
2886
{
    sigset_t winch;
    sigemptyset(&winch);
    sigaddset(&winch, SIGWINCH);
    if (allow)
	sigprocmask(SIG_UNBLOCK, &winch, NULL);
    else
	sigprocmask(SIG_BLOCK, &winch, NULL);
}
2887
#endif /* !NANO_SMALL */
2888

2889
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2890
void do_toggle(const toggle *which)
2891
{
2892
    bool enabled;
2893

2894
    /* Even easier! */
2895
    TOGGLE(which->flag);
Chris Allegretta's avatar
Chris Allegretta committed
2896

2897
    switch (which->val) {
2898
2899
2900
    case TOGGLE_SUSPEND_KEY:
	signal_init();
	break;
2901
#ifndef DISABLE_MOUSE
2902
2903
2904
    case TOGGLE_MOUSE_KEY:
	mouse_init();
	break;
2905
#endif
2906
    case TOGGLE_NOHELP_KEY:
2907
2908
	blank_statusbar();
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
2909
2910
2911
2912
	wrefresh(bottomwin);
	window_init();
	edit_refresh();
	display_main_list();
2913
	break;
2914
#ifdef ENABLE_COLOR
2915
2916
    case TOGGLE_SYNTAX_KEY:
	edit_refresh();
2917
	break;
2918
2919
2920
2921
2922
#endif
#ifdef ENABLE_NANORC
    case TOGGLE_WHITESPACE_KEY:
	edit_refresh();
	break;
2923
#endif
2924
    }
Chris Allegretta's avatar
Chris Allegretta committed
2925

Chris Allegretta's avatar
Chris Allegretta committed
2926
2927
2928
2929
2930
2931
2932
    /* We are assuming here that shortcut_init() above didn't free and
     * reallocate the toggles. */
    enabled = ISSET(which->flag);
    if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY)
	enabled = !enabled;
    statusbar("%s %s", which->desc,
		enabled ? _("enabled") : _("disabled"));
Chris Allegretta's avatar
Chris Allegretta committed
2933
}
2934
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2935

2936
2937
2938
2939
2940
2941
2942
2943
2944
void disable_extended_input(void)
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag &= ~IEXTEN;
    tcsetattr(0, TCSANOW, &term);
}

2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
void disable_signals(void)
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag &= ~ISIG;
    tcsetattr(0, TCSANOW, &term);
}

#ifndef NANO_SMALL
void enable_signals(void)
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag |= ISIG;
    tcsetattr(0, TCSANOW, &term);
}
#endif

void disable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_iflag &= ~(IXON|IXOFF);
    tcsetattr(0, TCSANOW, &term);
}

void enable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_iflag |= (IXON|IXOFF);
    tcsetattr(0, TCSANOW, &term);
}

2983
2984
2985
2986
/* Set up the terminal state.  Put the terminal in cbreak mode (read one
 * character at a time and interpret the special control keys), disable
 * translation of carriage return (^M) into newline (^J) so that we can
 * tell the difference between the Enter key and Ctrl-J, and disable
2987
2988
2989
2990
 * echoing of characters as they're typed.  Finally, disable extended
 * input processing, disable interpretation of the special control keys,
 * and if we're not in preserve mode, disable interpretation of the flow
 * control characters too. */
2991
2992
2993
2994
2995
void terminal_init(void)
{
    cbreak();
    nonl();
    noecho();
2996
    disable_extended_input();
2997
2998
2999
3000
3001
    disable_signals();
    if (!ISSET(PRESERVE))
	disable_flow_control();
}

3002
int main(int argc, char **argv)
Chris Allegretta's avatar
Chris Allegretta committed
3003
3004
{
    int optchr;
3005
3006
    int startline = 0;
	/* Line to try and start at. */
3007
#ifndef DISABLE_WRAPJUSTIFY
3008
3009
    bool fill_flag_used = FALSE;
	/* Was the fill option used? */
3010
#endif
3011
3012
3013
3014
3015
3016
3017
#ifdef ENABLE_MULTIBUFFER
    bool old_multibuffer;
	/* The old value of the multibuffer option, restored after we
	 * load all files on the command line. */
#endif
    int kbinput;
	/* Input from keyboard. */
3018
    bool meta_key, func_key;
Chris Allegretta's avatar
Chris Allegretta committed
3019
#ifdef HAVE_GETOPT_LONG
3020
    const struct option long_options[] = {
3021
3022
3023
	{"help", 0, 0, 'h'},
#ifdef ENABLE_MULTIBUFFER
	{"multibuffer", 0, 0, 'F'},
Chris Allegretta's avatar
Chris Allegretta committed
3024
3025
#endif
#ifdef ENABLE_NANORC
3026
#ifndef NANO_SMALL
3027
	{"historylog", 0, 0, 'H'},
3028
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3029
	{"ignorercfiles", 0, 0, 'I'},
3030
3031
3032
3033
#endif
#ifndef DISABLE_JUSTIFY
	{"quotestr", 1, 0, 'Q'},
#endif
3034
#ifdef HAVE_REGEX_H
3035
	{"regexp", 0, 0, 'R'},
3036
#endif
3037
	{"tabsize", 1, 0, 'T'},
Chris Allegretta's avatar
Chris Allegretta committed
3038
	{"version", 0, 0, 'V'},
3039
3040
#ifdef ENABLE_COLOR
	{"syntax", 1, 0, 'Y'},
3041
#endif
3042
	{"const", 0, 0, 'c'},
3043
	{"rebinddelete", 0, 0, 'd'},
3044
	{"nofollow", 0, 0, 'l'},
3045
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
3046
	{"mouse", 0, 0, 'm'},
3047
#endif
3048
3049
3050
#ifndef DISABLE_OPERATINGDIR
	{"operatingdir", 1, 0, 'o'},
#endif
3051
	{"preserve", 0, 0, 'p'},
3052
3053
3054
3055
3056
#ifndef DISABLE_WRAPJUSTIFY
	{"fill", 1, 0, 'r'},
#endif
#ifndef DISABLE_SPELLER
	{"speller", 1, 0, 's'},
3057
#endif
3058
3059
	{"tempfile", 0, 0, 't'},
	{"view", 0, 0, 'v'},
3060
#ifndef DISABLE_WRAPPING
3061
	{"nowrap", 0, 0, 'w'},
3062
#endif
3063
3064
	{"nohelp", 0, 0, 'x'},
	{"suspend", 0, 0, 'z'},
3065
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3066
	{"smarthome", 0, 0, 'A'},
3067
	{"backup", 0, 0, 'B'},
3068
	{"backupdir", 1, 0, 'E'},
3069
	{"noconvert", 0, 0, 'N'},
3070
	{"smooth", 0, 0, 'S'},
3071
	{"restricted", 0, 0, 'Z'},
3072
3073
	{"autoindent", 0, 0, 'i'},
	{"cut", 0, 0, 'k'},
3074
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3075
3076
3077
3078
3079
	{0, 0, 0, 0}
    };
#endif

    setlocale(LC_ALL, "");
3080
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
3081
3082
3083
3084
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3085
#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
3086
3087
    /* If we don't have rcfile support, we're root, and
     * --disable-wrapping-as-root is used, turn wrapping off. */
3088
    if (geteuid() == NANO_ROOT_UID)
3089
3090
	SET(NO_WRAP);
#endif
3091

3092
    while ((optchr =
Chris Allegretta's avatar
Chris Allegretta committed
3093
#ifdef HAVE_GETOPT_LONG
3094
	getopt_long(argc, argv, "h?ABE:FHINQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz", long_options, NULL)
Chris Allegretta's avatar
Chris Allegretta committed
3095
#else
3096
	getopt(argc, argv, "h?ABE:FHINQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz")
Chris Allegretta's avatar
Chris Allegretta committed
3097
#endif
3098
		) != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
3099
3100

	switch (optchr) {
3101
3102
3103
3104
3105
3106
3107
3108
	    case 'a':
	    case 'b':
	    case 'e':
	    case 'f':
	    case 'g':
	    case 'j':
		/* Pico compatibility flags. */
		break;
3109
#ifndef NANO_SMALL
3110
3111
3112
3113
3114
3115
3116
3117
3118
	    case 'A':
		SET(SMART_HOME);
		break;
	    case 'B':
		SET(BACKUP_FILE);
		break;
	    case 'E':
		backup_dir = mallocstrcpy(backup_dir, optarg);
		break;
3119
#endif
3120
#ifdef ENABLE_MULTIBUFFER
3121
3122
3123
	    case 'F':
		SET(MULTIBUFFER);
		break;
Chris Allegretta's avatar
Chris Allegretta committed
3124
3125
#endif
#ifdef ENABLE_NANORC
3126
#ifndef NANO_SMALL
3127
3128
3129
	    case 'H':
		SET(HISTORYLOG);
		break;
3130
#endif
3131
3132
3133
	    case 'I':
		SET(NO_RCFILE);
		break;
3134
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3135
#ifndef NANO_SMALL
3136
3137
3138
	    case 'N':
		SET(NO_CONVERT);
		break;
3139
3140
#endif
#ifndef DISABLE_JUSTIFY
3141
3142
3143
	    case 'Q':
		quotestr = mallocstrcpy(quotestr, optarg);
		break;
3144
#endif
3145
#ifdef HAVE_REGEX_H
3146
3147
3148
	    case 'R':
		SET(USE_REGEXP);
		break;
3149
3150
#endif
#ifndef NANO_SMALL
3151
3152
3153
	    case 'S':
		SET(SMOOTHSCROLL);
		break;
3154
#endif
3155
3156
	    case 'T':
		if (!parse_num(optarg, &tabsize) || tabsize <= 0) {
3157
3158
		    fprintf(stderr, _("Requested tab size %s invalid"), optarg);
		    fprintf(stderr, "\n");
3159
3160
3161
3162
3163
3164
		    exit(1);
		}
		break;
	    case 'V':
		version();
		exit(0);
3165
#ifdef ENABLE_COLOR
3166
3167
3168
	    case 'Y':
		syntaxstr = mallocstrcpy(syntaxstr, optarg);
		break;
3169
#endif
3170
3171
3172
3173
3174
3175
3176
3177
3178
	    case 'Z':
		SET(RESTRICTED);
		break;
	    case 'c':
		SET(CONSTUPDATE);
		break;
	    case 'd':
		SET(REBIND_DELETE);
		break;
3179
#ifndef NANO_SMALL
3180
3181
3182
3183
3184
3185
	    case 'i':
		SET(AUTOINDENT);
		break;
	    case 'k':
		SET(CUT_TO_END);
		break;
3186
#endif
3187
3188
3189
	    case 'l':
		SET(NOFOLLOW_SYMLINKS);
		break;
3190
#ifndef DISABLE_MOUSE
3191
3192
3193
	    case 'm':
		SET(USE_MOUSE);
		break;
3194
#endif
3195
#ifndef DISABLE_OPERATINGDIR
3196
3197
3198
	    case 'o':
		operating_dir = mallocstrcpy(operating_dir, optarg);
		break;
3199
#endif
3200
3201
3202
	    case 'p':
		SET(PRESERVE);
		break;
3203
#ifndef DISABLE_WRAPJUSTIFY
3204
3205
	    case 'r':
		if (!parse_num(optarg, &wrap_at)) {
3206
3207
		    fprintf(stderr, _("Requested fill size %s invalid"), optarg);
		    fprintf(stderr, "\n");
3208
3209
3210
3211
		    exit(1);
		}
		fill_flag_used = TRUE;
		break;
3212
#endif
3213
#ifndef DISABLE_SPELLER
3214
3215
3216
	    case 's':
		alt_speller = mallocstrcpy(alt_speller, optarg);
		break;
3217
#endif
3218
3219
3220
3221
3222
3223
	    case 't':
		SET(TEMP_FILE);
		break;
	    case 'v':
		SET(VIEW_MODE);
		break;
3224
#ifndef DISABLE_WRAPPING
3225
3226
3227
	    case 'w':
		SET(NO_WRAP);
		break;
3228
#endif
3229
3230
3231
3232
3233
3234
3235
3236
	    case 'x':
		SET(NO_HELP);
		break;
	    case 'z':
		SET(SUSPEND);
		break;
	    default:
		usage();
Chris Allegretta's avatar
Chris Allegretta committed
3237
3238
3239
	}
    }

3240
3241
    /* If the executable filename starts with 'r', we use restricted
     * mode. */
3242
3243
3244
    if (*(tail(argv[0])) == 'r')
	SET(RESTRICTED);

3245
3246
3247
    /* If we're using restricted mode, disable suspending, backups, and
     * reading rcfiles, since they all would allow reading from or
     * writing to files not specified on the command line. */
3248
3249
3250
3251
3252
3253
    if (ISSET(RESTRICTED)) {
	UNSET(SUSPEND);
	UNSET(BACKUP_FILE);
	SET(NO_RCFILE);
    }

Chris Allegretta's avatar
Chris Allegretta committed
3254
/* We've read through the command line options.  Now back up the flags
3255
3256
 * and values that are set, and read the rcfile(s).  If the values
 * haven't changed afterward, restore the backed-up values. */
Chris Allegretta's avatar
Chris Allegretta committed
3257
3258
3259
3260
3261
#ifdef ENABLE_NANORC
    if (!ISSET(NO_RCFILE)) {
#ifndef DISABLE_OPERATINGDIR
	char *operating_dir_cpy = operating_dir;
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3262
#ifndef DISABLE_WRAPJUSTIFY
3263
	ssize_t wrap_at_cpy = wrap_at;
Chris Allegretta's avatar
Chris Allegretta committed
3264
#endif
3265
3266
3267
#ifndef NANO_SMALL
	char *backup_dir_cpy = backup_dir;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3268
3269
3270
3271
3272
3273
#ifndef DISABLE_JUSTIFY
	char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
	char *alt_speller_cpy = alt_speller;
#endif
3274
	ssize_t tabsize_cpy = tabsize;
Chris Allegretta's avatar
Chris Allegretta committed
3275
3276
	long flags_cpy = flags;

3277
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
3278
	operating_dir = NULL;
3279
#endif
3280
3281
3282
#ifndef NANO_SMALL
	backup_dir = NULL;
#endif
3283
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
3284
	quotestr = NULL;
3285
3286
#endif
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
3287
	alt_speller = NULL;
3288
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3289
3290
3291
3292
3293
3294
3295
3296
3297

	do_rcfile();

#ifndef DISABLE_OPERATINGDIR
	if (operating_dir_cpy != NULL) {
	    free(operating_dir);
	    operating_dir = operating_dir_cpy;
	}
#endif
3298
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
3299
3300
3301
	if (fill_flag_used)
	    wrap_at = wrap_at_cpy;
#endif
3302
3303
3304
3305
3306
3307
#ifndef NANO_SMALL
	if (backup_dir_cpy != NULL) {
	    free(backup_dir);
	    backup_dir = backup_dir_cpy;
	}
#endif	
Chris Allegretta's avatar
Chris Allegretta committed
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
#ifndef DISABLE_JUSTIFY
	if (quotestr_cpy != NULL) {
	    free(quotestr);
	    quotestr = quotestr_cpy;
	}
#endif
#ifndef DISABLE_SPELLER
	if (alt_speller_cpy != NULL) {
	    free(alt_speller);
	    alt_speller = alt_speller_cpy;
	}
#endif
3320
	if (tabsize_cpy != -1)
Chris Allegretta's avatar
Chris Allegretta committed
3321
3322
3323
3324
	    tabsize = tabsize_cpy;
	flags |= flags_cpy;
    }
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
3325
    else if (geteuid() == NANO_ROOT_UID)
Chris Allegretta's avatar
Chris Allegretta committed
3326
3327
3328
3329
	SET(NO_WRAP);
#endif
#endif /* ENABLE_NANORC */

3330
3331
3332
3333
3334
3335
3336
3337
#ifndef NANO_SMALL
    history_init();
#ifdef ENABLE_NANORC
    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
	load_history();
#endif
#endif

3338
#ifndef NANO_SMALL
3339
    /* Set up the backup directory (unless we're using restricted mode,
3340
3341
3342
3343
     * in which case backups are disabled, since they would allow
     * reading from or writing to files not specified on the command
     * line).  This entails making sure it exists and is a directory, so
     * that backup files will be saved there. */
3344
3345
    if (!ISSET(RESTRICTED))
	init_backup_dir();
3346
3347
#endif

3348
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
3349
    /* Set up the operating directory.  This entails chdir()ing there,
3350
     * so that file reads and writes will be based there. */
3351
3352
3353
    init_operating_dir();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3354
#ifndef DISABLE_JUSTIFY
3355
    if (punct == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3356
	punct = mallocstrcpy(punct, ".?!");
3357
3358

    if (brackets == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3359
	brackets = mallocstrcpy(brackets, "'\")}]>");
3360

Chris Allegretta's avatar
Chris Allegretta committed
3361
    if (quotestr == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3362
	quotestr = mallocstrcpy(NULL,
Chris Allegretta's avatar
Chris Allegretta committed
3363
#ifdef HAVE_REGEX_H
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3364
		"^([ \t]*[|>:}#])+"
Chris Allegretta's avatar
Chris Allegretta committed
3365
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3366
		"> "
Chris Allegretta's avatar
Chris Allegretta committed
3367
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3368
		);
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
#ifdef HAVE_REGEX_H
    quoterc = regcomp(&quotereg, quotestr, REG_EXTENDED);

    if (quoterc == 0) {
	/* We no longer need quotestr, just quotereg. */
	free(quotestr);
	quotestr = NULL;
    } else {
	size_t size = regerror(quoterc, &quotereg, NULL, 0);

	quoteerr = charalloc(size);
	regerror(quoterc, &quotereg, quoteerr, size);
    }
#else
    quotelen = strlen(quotestr);
#endif /* !HAVE_REGEX_H */
Chris Allegretta's avatar
Chris Allegretta committed
3385
#endif /* !DISABLE_JUSTIFY */
3386

3387
3388
#ifndef DISABLE_SPELLER
    /* If we don't have an alternative spell checker after reading the
3389
     * command line and/or rcfile(s), check $SPELL for one, as Pico
3390
     * does (unless we're using restricted mode, in which case spell
3391
3392
     * checking is disabled, since it would allow reading from or
     * writing to files not specified on the command line). */
3393
    if (!ISSET(RESTRICTED) && alt_speller == NULL) {
3394
3395
3396
3397
3398
3399
	char *spellenv = getenv("SPELL");
	if (spellenv != NULL)
	    alt_speller = mallocstrcpy(NULL, spellenv);
    }
#endif

3400
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
3401
    /* If whitespace wasn't specified, set its default value. */
3402
3403
3404
3405
    if (whitespace == NULL)
	whitespace = mallocstrcpy(NULL, "  ");
#endif

3406
    /* If tabsize wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
3407
    if (tabsize == -1)
3408
	tabsize = WIDTH_OF_TAB;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3409

3410
    /* Back up the old terminal settings so that they can be restored. */
3411
    tcgetattr(0, &oldterm);
3412

3413
3414
    /* Curses initialization stuff: Start curses and set up the
     * terminal state. */
Chris Allegretta's avatar
Chris Allegretta committed
3415
    initscr();
3416
    terminal_init();
3417

3418
    /* Set up the global variables and the shortcuts. */
3419
3420
    global_init(FALSE);
    shortcut_init(FALSE);
3421
3422

    /* Set up the signal handlers. */
3423
    signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
3424
3425

#ifdef DEBUG
3426
    fprintf(stderr, "Main: set up windows\n");
Chris Allegretta's avatar
Chris Allegretta committed
3427
3428
#endif

3429
    window_init();
3430
#ifndef DISABLE_MOUSE
3431
    mouse_init();
3432
#endif
3433

Chris Allegretta's avatar
Chris Allegretta committed
3434
#ifdef DEBUG
3435
    fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
3436
#endif
3437

3438
3439
3440
3441
3442
3443
3444
3445
    /* If there's a +LINE flag here, it is the first non-option
     * argument, and it is followed by at least one other argument, the
     * filename it applies to. */
    if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
	startline = atoi(&argv[optind][1]);
	optind++;
    }

Chris Allegretta's avatar
Chris Allegretta committed
3446
#ifdef ENABLE_MULTIBUFFER
3447
3448
3449
3450
3451
3452
    old_multibuffer = ISSET(MULTIBUFFER);
    SET(MULTIBUFFER);

    /* Read all the files after the first one on the command line into
     * new buffers. */
    {
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
	int i = optind + 1, iline = 0;
	for (; i < argc; i++) {
	    /* If there's a +LINE flag here, it is followed by at least
	     * one other argument, the filename it applies to. */
	    if (i < argc - 1 && argv[i][0] == '+' && iline == 0) {
		iline = atoi(&argv[i][1]);
	    } else {
		load_buffer(argv[i]);
		if (iline > 0) {
		    do_gotoline(iline, FALSE);
		    iline = 0;
		}
	    }
	}
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
    }
#endif

    /* Read the first file on the command line into either the current
     * buffer or a new buffer, depending on whether multibuffer mode is
     * enabled. */
    if (optind < argc)
	load_buffer(argv[optind]);

    /* We didn't open any files if all the command line arguments were
     * invalid files like directories or if there were no command line
     * arguments given.  In this case, we have to load a blank buffer.
     * Also, we unset view mode to allow editing. */
    if (filename == NULL) {
	filename = mallocstrcpy(NULL, "");
	new_file();
	UNSET(VIEW_MODE);

	/* Add this new entry to the open_files structure if we have
        * multibuffer support, or to the main filestruct if we don't. */
	load_file();
Chris Allegretta's avatar
Chris Allegretta committed
3488
    }
3489
3490
3491
3492

#ifdef ENABLE_MULTIBUFFER
    if (!old_multibuffer)
	UNSET(MULTIBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
3493
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3494

3495
3496
3497
3498
3499
3500
3501
#ifdef DEBUG
    fprintf(stderr, "Main: top and bottom win\n");
#endif

    titlebar(NULL);
    display_main_list();

Chris Allegretta's avatar
Chris Allegretta committed
3502
    if (startline > 0)
3503
	do_gotoline(startline, FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
3504

3505
3506
#ifndef NANO_SMALL
    /* Return here after a SIGWINCH. */
3507
    sigsetjmp(jmpbuf, 1);
3508
#endif
3509

Robert Siemborski's avatar
Robert Siemborski committed
3510
3511
    edit_refresh();

3512
    while (TRUE) {
3513
	reset_cursor();
3514
	if (ISSET(CONSTUPDATE))
3515
	    do_cursorpos(TRUE);
3516

3517
#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
3518
	currshortcut = main_list;
3519
#endif
3520

3521
	kbinput = get_edit_input(&meta_key, &func_key, TRUE);
3522

3523
	/* Last gasp, stuff that's not in the main lists. */
3524
	if (kbinput != ERR && !is_cntrl_char(kbinput)) {
3525
3526
	    /* Don't stop unhandled printable sequences, so that people
	     * with odd character sets can type. */
3527
3528
3529
3530
	    if (ISSET(VIEW_MODE))
		print_view_warning();
	    else
		do_char(kbinput);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3531
	}
Chris Allegretta's avatar
Chris Allegretta committed
3532
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3533
    assert(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
3534
}