nano.c 96.7 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
35
#include <sys/types.h>
#include <sys/wait.h>
Chris Allegretta's avatar
Chris Allegretta committed
36
37
38
39
#include <errno.h>
#include <ctype.h>
#include <locale.h>
#include <limits.h>
40
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#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

56
57
58
59
#ifndef NANO_SMALL
#include <setjmp.h>
#endif

60
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
61
static int fill = 0;	/* Fill - where to wrap lines, basically */
62
#endif
63
64
65
66
#ifndef DISABLE_WRAPPING
static int same_line_wrap = 0;	/* Whether wrapped text should be
				   prepended to the next line */
#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
static sigjmp_buf jmpbuf;	/* Used to return to mainloop after SIGWINCH */
73
#endif
74

Chris Allegretta's avatar
Chris Allegretta committed
75
/* What we do when we're all set to exit */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
76
RETSIGTYPE finish(int sigage)
Chris Allegretta's avatar
Chris Allegretta committed
77
{
78
79
80
81
    if (!ISSET(NO_HELP))
	blank_bottombars();
    else
	blank_statusbar();
82

Chris Allegretta's avatar
Chris Allegretta committed
83
84
85
    wrefresh(bottomwin);
    endwin();

86
    /* Restore the old terminal settings. */
Chris Allegretta's avatar
Chris Allegretta committed
87
    tcsetattr(0, TCSANOW, &oldterm);
Chris Allegretta's avatar
Chris Allegretta committed
88

89
90
91
92
93
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
	save_history();
#endif

94
#ifdef DEBUG
95
    thanks_for_all_the_fish();
96
#endif
97

Chris Allegretta's avatar
Chris Allegretta committed
98
99
100
101
    exit(sigage);
}

/* Die (gracefully?) */
Chris Allegretta's avatar
Chris Allegretta committed
102
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
103
104
105
{
    va_list ap;

106
107
108
    endwin();
    curses_ended = TRUE;

109
    /* Restore the old terminal settings. */
110
111
    tcsetattr(0, TCSANOW, &oldterm);

Chris Allegretta's avatar
Chris Allegretta committed
112
113
114
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
115

116
117
118
    /* save the currently loaded file if it's been modified */
    if (ISSET(MODIFIED))
	die_save_file(filename);
119

120
#ifdef ENABLE_MULTIBUFFER
121
    /* then save all of the other modified loaded files, if any */
122
    if (open_files != NULL) {
123
	openfilestruct *tmp;
124
125
126

	tmp = open_files;

127
	while (open_files->prev != NULL)
128
129
	    open_files = open_files->prev;

130
	while (open_files->next != NULL) {
131

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
132
	    /* if we already saved the file above (i.e, if it was the
133
134
	       currently loaded file), don't save it again */
	    if (tmp != open_files) {
Chris Allegretta's avatar
Chris Allegretta committed
135
136
137
138
		/* 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 */
139
		fileage = open_files->fileage;
Chris Allegretta's avatar
Chris Allegretta committed
140
		filebot = open_files->filebot;
141
		/* save the file if it's been modified */
Chris Allegretta's avatar
Chris Allegretta committed
142
		if (open_files->file_flags & MODIFIED)
143
		    die_save_file(open_files->filename);
144
145
146
147
148
149
	    }
	    open_files = open_files->next;
	}
    }
#endif

Chris Allegretta's avatar
Chris Allegretta committed
150
    exit(1); /* We have a problem: exit w/ errorlevel(1) */
151
152
}

Chris Allegretta's avatar
Chris Allegretta committed
153
void die_save_file(const char *die_filename)
154
{
Chris Allegretta's avatar
Chris Allegretta committed
155
    char *ret;
156
    int i = -1;
157

158
159
160
161
    /* No emergency files in restricted mode! */
    if (ISSET(RESTRICTED))
	return;

Chris Allegretta's avatar
Chris Allegretta committed
162
163
164
165
    /* If we can't save, we have REAL bad problems, but we might as well
       TRY. */
    if (die_filename[0] == '\0')
	ret = get_next_filename("nano.save");
166
    else {
167
	char *buf = charalloc(strlen(die_filename) + 6);
Chris Allegretta's avatar
Chris Allegretta committed
168

169
	strcpy(buf, die_filename);
170
	strcat(buf, ".save");
171
	ret = get_next_filename(buf);
Chris Allegretta's avatar
Chris Allegretta committed
172
	free(buf);
173
    }
Chris Allegretta's avatar
Chris Allegretta committed
174
175
    if (ret[0] != '\0')
	i = write_file(ret, 1, 0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
176

177
    if (i != -1)
Chris Allegretta's avatar
Chris Allegretta committed
178
	fprintf(stderr, _("\nBuffer written to %s\n"), ret);
179
    else
Chris Allegretta's avatar
Chris Allegretta committed
180
	fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret);
181
182

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

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

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

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

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

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

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

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

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

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

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

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

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

#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)
{
269
    size_t allocsize = 1;	/* space needed for help_text */
270
    char *ptr = NULL;
271
    const shortcut *s;
272
273
274
275
276
277
278
279
280
281
282
283
#ifndef NANO_SMALL
    const toggle *t;
#endif

    /* First set up the initial help text for the current function */
    if (currshortcut == whereis_list || currshortcut == replace_list
	     || currshortcut == replace_list_2)
	ptr = _("Search Command Help Text\n\n "
		"Enter the words or characters you would like to search "
		"for, then hit enter.  If there is a match for the text you "
		"entered, the screen will be updated to the location of the "
		"nearest match for the search string.\n\n "
284
285
286
287
		"The previous search string will be shown in brackets after "
		"the Search: prompt.  Hitting Enter without entering any text "
		"will perform the previous search.\n\n The following function "
		"keys are available in Search mode:\n\n");
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    else if (currshortcut == goto_list)
	ptr = _("Go To Line Help Text\n\n "
		"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)
	ptr = _("Insert File Help Text\n\n "
		"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)
	ptr = _("Write File Help Text\n\n "
		"Type the name that you wish to save the current file "
		"as and hit Enter to save the file.\n\n If you have "
		"selected text with Ctrl-^, you will be prompted to "
		"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)
	ptr = _("File Browser Help Text\n\n "
		"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)
	ptr = _("Browser Go To Directory Help Text\n\n "
		"Enter the name of the directory you would like to "
		"browse to.\n\n If tab completion has not been disabled, "
		"you can use the TAB key to (attempt to) automatically "
		"complete the directory name.\n\n The following function "
		"keys are available in Browser Go To Directory mode:\n\n");
#endif
339
#ifndef DISABLE_SPELLER
340
341
342
343
344
345
346
347
348
    else if (currshortcut == spell_list)
	ptr = _("Spell Check Help Text\n\n "
		"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 "
		"current file.\n\n The following other functions are "
		"available in Spell Check mode:\n\n");
349
#endif
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#ifndef NANO_SMALL
    else if (currshortcut == extcmd_list)
	ptr = _("External Command Help Text\n\n "
		"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
    else /* Default to the main help list */
	ptr = _(" nano help text\n\n "
	  "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
370
371
372
373
374
375
376
377
378
	  "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");
379

380
    allocsize += strlen(ptr);
381
382
383

    /* The space needed for the shortcut lists, at most COLS characters,
     * plus '\n'. */
384
    allocsize += (COLS + 1) * length_of_list(currshortcut);
385
386
387
388
389

#ifndef NANO_SMALL
    /* If we're on the main list, we also count the toggle help text. 
     * Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
     * COLS - 24 characters, plus '\n'.*/
390
391
392
    if (currshortcut == main_list) {
	size_t endislen = strlen(_("enable/disable"));

393
	for (t = toggles; t != NULL; t = t->next)
394
395
	    allocsize += 8 + strlen(t->desc) + endislen;
    }
396
397
398
399
400
401
402
#endif /* !NANO_SMALL */

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

    /* Allocate space for the help text */
403
    help_text = charalloc(allocsize);
404
405
406
407
408
409
410

    /* Now add the text we want */
    strcpy(help_text, ptr);
    ptr = help_text + strlen(help_text);

    /* Now add our shortcut info */
    for (s = currshortcut; s != NULL; s = s->next) {
411
	/* true if the character in s->metaval is shown in first column */
412
413
	int meta_shortcut = 0;

414
	if (s->ctrlval != NANO_NO_KEY) {
415
#ifndef NANO_SMALL
416
	    if (s->ctrlval == NANO_HISTORY_KEY)
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
417
418
		ptr += sprintf(ptr, "%.2s", _("Up"));
	    else
419
#endif
420
	    if (s->ctrlval == NANO_CONTROL_SPACE)
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
421
		ptr += sprintf(ptr, "^%.5s", _("Space"));
422
	    else if (s->ctrlval == NANO_CONTROL_8)
423
424
		ptr += sprintf(ptr, "^?");
	    else
425
		ptr += sprintf(ptr, "^%c", s->ctrlval + 64);
426
427
	}
#ifndef NANO_SMALL
428
	else if (s->metaval != NANO_NO_KEY) {
429
	    meta_shortcut = 1;
430
	    if (s->metaval == NANO_ALT_SPACE)
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
431
432
		ptr += snprintf(ptr, 8, "M-%.5s", _("Space"));
	    else
433
		ptr += sprintf(ptr, "M-%c", toupper(s->metaval));
434
	}
435
436
437
438
#endif

	*(ptr++) = '\t';

439
440
	if (s->funcval != NANO_NO_KEY)
	    ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0);
441
442
443

	*(ptr++) = '\t';

444
445
	if (!meta_shortcut && s->metaval != NANO_NO_KEY)
	    ptr += sprintf(ptr, "(M-%c)", toupper(s->metaval));
446
447
	else if (meta_shortcut && s->miscval != NANO_NO_KEY)
	    ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval));
448
449
450
451

	*(ptr++) = '\t';

	assert(s->help != NULL);
452
	ptr += sprintf(ptr, "%.*s\n", COLS > 24 ? COLS - 24 : 0, s->help);
453
454
455
456
    }

#ifndef NANO_SMALL
    /* And the toggles... */
457
    if (currshortcut == main_list) {
458
459
	for (t = toggles; t != NULL; t = t->next) {
	    assert(t->desc != NULL);
460
	    ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val), t->desc,
461
				_("enable/disable"));
462
	}
463
    }
464
465
466
467
#endif /* !NANO_SMALL */

    /* If all went well, we didn't overwrite the allocated space for
       help_text. */
468
    assert(strlen(help_text) < allocsize);
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
}
#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;
}

486
/* Make a copy of a node to a pointer (space will be malloc()ed). */
Chris Allegretta's avatar
Chris Allegretta committed
487
filestruct *copy_node(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
488
{
Chris Allegretta's avatar
Chris Allegretta committed
489
    filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegretta's avatar
Chris Allegretta committed
490

Chris Allegretta's avatar
Chris Allegretta committed
491
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
492

Chris Allegretta's avatar
Chris Allegretta committed
493
    dst->data = charalloc(strlen(src->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
494
495
496
497
498
499
500
501
    dst->next = src->next;
    dst->prev = src->prev;
    strcpy(dst->data, src->data);
    dst->lineno = src->lineno;

    return dst;
}

502
503
504
505
506
507
508
509
510
511
512
513
514
/* Splice a node into an existing filestruct. */
void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
{
    if (newnode != NULL) {
	newnode->next = end;
	newnode->prev = begin;
    }
    if (begin != NULL)
	begin->next = newnode;
    if (end != NULL)
	end->prev = newnode;
}

515
/* Unlink a node from the rest of the filestruct. */
Chris Allegretta's avatar
Chris Allegretta committed
516
void unlink_node(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
517
{
Chris Allegretta's avatar
Chris Allegretta committed
518
    assert(fileptr != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
519

520
521
522
523
524
525
526
527
    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
528
void delete_node(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
529
{
Chris Allegretta's avatar
Chris Allegretta committed
530
531
532
533
534
    if (fileptr != NULL) {
	if (fileptr->data != NULL)
	    free(fileptr->data);
	free(fileptr);
    }
535
536
537
}

/* Okay, now let's duplicate a whole struct! */
Chris Allegretta's avatar
Chris Allegretta committed
538
filestruct *copy_filestruct(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
539
{
Chris Allegretta's avatar
Chris Allegretta committed
540
541
    filestruct *head;	/* copy of src, top of the copied list */
    filestruct *prev;	/* temp that traverses the list */
Chris Allegretta's avatar
Chris Allegretta committed
542

Chris Allegretta's avatar
Chris Allegretta committed
543
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
544

Chris Allegretta's avatar
Chris Allegretta committed
545
546
547
548
549
550
551
552
    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
553

Chris Allegretta's avatar
Chris Allegretta committed
554
	src = src->next;
Chris Allegretta's avatar
Chris Allegretta committed
555
556
    }

Chris Allegretta's avatar
Chris Allegretta committed
557
    prev->next = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
558
559
560
    return head;
}

561
/* Frees a filestruct. */
Chris Allegretta's avatar
Chris Allegretta committed
562
void free_filestruct(filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
563
{
Chris Allegretta's avatar
Chris Allegretta committed
564
565
566
567
    if (src != NULL) {
	while (src->next != NULL) {
	    src = src->next;
	    delete_node(src->prev);
Chris Allegretta's avatar
Chris Allegretta committed
568
#ifdef DEBUG
569
	    fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_node()");
Chris Allegretta's avatar
Chris Allegretta committed
570
#endif
Chris Allegretta's avatar
Chris Allegretta committed
571
572
	}
	delete_node(src);
573
#ifdef DEBUG
574
	fprintf(stderr, "%s: free'd last node.\n", "delete_node()");
575
576
577
578
#endif
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
579
void renumber_all(void)
Chris Allegretta's avatar
Chris Allegretta committed
580
581
{
    filestruct *temp;
582
    int i = 1;
Chris Allegretta's avatar
Chris Allegretta committed
583

Chris Allegretta's avatar
Chris Allegretta committed
584
585
    assert(fileage == NULL || fileage != fileage->next);
    for (temp = fileage; temp != NULL; temp = temp->next)
Chris Allegretta's avatar
Chris Allegretta committed
586
587
588
	temp->lineno = i++;
}

Chris Allegretta's avatar
Chris Allegretta committed
589
void renumber(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
590
{
Chris Allegretta's avatar
Chris Allegretta committed
591
    if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
Chris Allegretta's avatar
Chris Allegretta committed
592
	renumber_all();
Chris Allegretta's avatar
Chris Allegretta committed
593
594
    else {
	int lineno = fileptr->prev->lineno;
595

Chris Allegretta's avatar
Chris Allegretta committed
596
597
598
599
	assert(fileptr != fileptr->next);
	for (; fileptr != NULL; fileptr = fileptr->next)
	    fileptr->lineno = ++lineno;
    }
Chris Allegretta's avatar
Chris Allegretta committed
600
601
}

602
/* Print one usage string to the screen, removes lots of duplicate 
Chris Allegretta's avatar
Chris Allegretta committed
603
604
 * strings to translate and takes out the parts that shouldn't be 
 * translatable (the flag names). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
605
void print1opt(const char *shortflag, const char *longflag,
606
		const char *desc)
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
{
    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

    printf("%s\n", desc);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
623
void usage(void)
Chris Allegretta's avatar
Chris Allegretta committed
624
625
{
#ifdef HAVE_GETOPT_LONG
Chris Allegretta's avatar
Chris Allegretta committed
626
627
    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
628
#else
Chris Allegretta's avatar
Chris Allegretta committed
629
630
    printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
    printf(_("Option\t\tMeaning\n"));
631
632
#endif /* HAVE_GETOPT_LONG */

633
    print1opt("-h, -?", "--help", _("Show this message"));
Chris Allegretta's avatar
Chris Allegretta committed
634
    print1opt(_("+LINE"), "", _("Start at line number LINE"));
635
#ifndef NANO_SMALL
636
    print1opt("-B", "--backup", _("Backup existing files on save"));
637
    print1opt("-D", "--dos", _("Write file in DOS format"));
638
    print1opt("-E", "--backupdir=[dir]", _("Directory for writing backup files"));
639
#endif
640
#ifdef ENABLE_MULTIBUFFER
641
    print1opt("-F", "--multibuffer", _("Enable multiple file buffers"));
Chris Allegretta's avatar
Chris Allegretta committed
642
643
#endif
#ifdef ENABLE_NANORC
644
#ifndef NANO_SMALL
645
    print1opt("-H", "--historylog", _("Log & read search/replace string history"));
646
#endif
Chris Allegretta's avatar
Chris Allegretta committed
647
    print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files"));
Chris Allegretta's avatar
Chris Allegretta committed
648
649
#endif
#ifndef NANO_SMALL
650
651
    print1opt("-M", "--mac", _("Write file in Mac format"));
    print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format"));
652
653
#endif
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
654
    print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \""));
655
#endif
656
657
658
#ifdef HAVE_REGEX_H
    print1opt("-R", "--regexp", _("Do regular expression searches"));
#endif
659
#ifndef NANO_SMALL
660
    print1opt("-S", "--smooth", _("Smooth scrolling"));
661
#endif
662
    print1opt(_("-T [#cols]"), _("--tabsize=[#cols]"), _("Set width of a tab in cols to #cols"));
663
    print1opt("-V", "--version", _("Print version information and exit"));
664
665
666
#ifdef ENABLE_COLOR
    print1opt(_("-Y [str]"), _("--syntax [str]"), _("Syntax definition to use"));
#endif
667
    print1opt(_("-Z"), _("--restricted"), _("Restricted mode"));
668
    print1opt("-c", "--const", _("Constantly show cursor position"));
669
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
670
    print1opt("-d", "--rebinddelete", _("Fix Backspace/Delete confusion problem"));
671
672
    print1opt("-i", "--autoindent", _("Automatically indent new lines"));
    print1opt("-k", "--cut", _("Let ^K cut from cursor to end of line"));
673
#endif
674
    print1opt("-l", "--nofollow", _("Don't follow symbolic links, overwrite"));
675
#ifndef DISABLE_MOUSE
676
    print1opt("-m", "--mouse", _("Enable mouse"));
Chris Allegretta's avatar
Chris Allegretta committed
677
#endif
678
#ifndef DISABLE_OPERATINGDIR
679
    print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), _("Set operating directory"));
Chris Allegretta's avatar
Chris Allegretta committed
680
#endif
Jordi Mallach's avatar
Jordi Mallach committed
681
    print1opt("-p", "--preserve", _("Preserve XON (^Q) and XOFF (^S) keys"));
682
#ifndef DISABLE_WRAPJUSTIFY
683
    print1opt(_("-r [#cols]"), _("--fill=[#cols]"), _("Set fill cols to (wrap lines at) #cols"));
684
#endif
685
#ifndef DISABLE_SPELLER
686
    print1opt(_("-s [prog]"), _("--speller=[prog]"), _("Enable alternate speller"));
687
#endif
688
689
    print1opt("-t", "--tempfile", _("Auto save on exit, don't prompt"));
    print1opt("-v", "--view", _("View (read only) mode"));
690
#ifndef DISABLE_WRAPPING
691
    print1opt("-w", "--nowrap", _("Don't wrap long lines"));
Chris Allegretta's avatar
Chris Allegretta committed
692
#endif
693
694
    print1opt("-x", "--nohelp", _("Don't show help window"));
    print1opt("-z", "--suspend", _("Enable suspend"));
Chris Allegretta's avatar
Chris Allegretta committed
695
696
697

    /* this is a special case */
    printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
698

Chris Allegretta's avatar
Chris Allegretta committed
699
700
701
    exit(0);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
702
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
703
{
Chris Allegretta's avatar
Chris Allegretta committed
704
    printf(_(" GNU nano version %s (compiled %s, %s)\n"),
Chris Allegretta's avatar
Chris Allegretta committed
705
	   VERSION, __TIME__, __DATE__);
706
    printf(_
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
707
	   (" Email: nano@nano-editor.org	Web: http://www.nano-editor.org/"));
708
    printf(_("\n Compiled options:"));
709

710
711
712
#ifndef ENABLE_NLS
    printf(" --disable-nls");
#endif
713
714
715
#ifdef DEBUG
    printf(" --enable-debug");
#endif
716
717
#ifdef NANO_EXTRA
    printf(" --enable-extra");
718
#endif
719
720
721
#ifdef NANO_SMALL
    printf(" --enable-tiny");
#else
722
#ifdef DISABLE_BROWSER
723
    printf(" --disable-browser");
724
#endif
725
726
#ifdef DISABLE_HELP
    printf(" --disable-help");
727
728
#endif
#ifdef DISABLE_JUSTIFY
729
    printf(" --disable-justify");
730
#endif
731
#ifdef DISABLE_MOUSE
732
    printf(" --disable-mouse");
733
#endif
734
735
736
#ifdef DISABLE_OPERATINGDIR
    printf(" --disable-operatingdir");
#endif
737
738
739
740
741
742
743
744
745
746
#ifdef DISABLE_SPELLER
    printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
    printf(" --disable-tabcomp");
#endif
#endif /* NANO_SMALL */
#ifdef DISABLE_WRAPPING
    printf(" --disable-wrapping");
#endif
747
748
749
#ifdef DISABLE_ROOTWRAP
    printf(" --disable-wrapping-as-root");
#endif
750
751
752
753
754
755
756
757
758
#ifdef ENABLE_COLOR
    printf(" --enable-color");
#endif
#ifdef ENABLE_MULTIBUFFER
    printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
    printf(" --enable-nanorc");
#endif
759
760
761
762
#ifdef USE_SLANG
    printf(" --with-slang");
#endif
    printf("\n");
Chris Allegretta's avatar
Chris Allegretta committed
763
764
765
766
}

int no_help(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
767
    return ISSET(NO_HELP) ? 2 : 0;
Chris Allegretta's avatar
Chris Allegretta committed
768
769
}

770
int nano_disabled_msg(void)
771
{
Chris Allegretta's avatar
Chris Allegretta committed
772
    statusbar(_("Sorry, support for this function has been disabled"));
773
    return 1;
774
775
}

Chris Allegretta's avatar
Chris Allegretta committed
776
#ifndef NANO_SMALL
777
static int pid;		/* This is the PID of the newly forked process
778
779
780
			 * below.  It must be global since the signal
			 * handler needs it. */
RETSIGTYPE cancel_fork(int signal)
Chris Allegretta's avatar
Chris Allegretta committed
781
{
782
783
    if (kill(pid, SIGKILL) == -1)
	nperror("kill");
Chris Allegretta's avatar
Chris Allegretta committed
784
785
}

786
int open_pipe(const char *command)
Chris Allegretta's avatar
Chris Allegretta committed
787
{
788
789
790
791
792
    int fd[2];
    FILE *f;
    struct sigaction oldaction, newaction;
			/* original and temporary handlers for SIGINT */
    int cancel_sigs = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
793
794
    /* cancel_sigs == 1 means that sigaction() failed without changing
     * the signal handlers.  cancel_sigs == 2 means the signal handler
795
     * was changed, but the tcsetattr() didn't succeed.
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
796
     *
797
798
     * I 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
799

800
    /* Make our pipes. */
801

802
803
804
    if (pipe(fd) == -1) {
	statusbar(_("Could not pipe"));
	return 1;
805
    }
806

807
    /* Fork a child. */
808

809
810
811
812
813
814
    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. */
      
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
815
	execl("/bin/sh", "sh", "-c", command, 0);
816
817
	exit(0);
    }
818

819
820
821
822
823
824
825
826
    /* Else continue as parent. */

    close(fd[1]);

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
836
    if (sigaction(SIGINT, NULL, &newaction) == -1) {
837
838
	cancel_sigs = 1;
	nperror("sigaction");
Chris Allegretta's avatar
Chris Allegretta committed
839
    } else {
840
	newaction.sa_handler = cancel_fork;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
841
	if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
842
843
844
	    cancel_sigs = 1;
	    nperror("sigaction");
	}
Chris Allegretta's avatar
Chris Allegretta committed
845
    }
846
847
    /* Note that now oldaction is the previous SIGINT signal handler,
     * to be restored later. */
848

849
    f = fdopen(fd[0], "rb");
850
    if (f == NULL)
851
852
853
854
855
856
857
858
859
860
861
      nperror("fdopen");
    
    read_file(f, "stdin", 0);
    /* if multibuffer mode is on, we could be here in view mode; if so,
       don't set the modification flag */
    if (!ISSET(VIEW_MODE))
	set_modified();

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
862
    if (cancel_sigs != 1 && sigaction(SIGINT, &oldaction, NULL) == -1)
863
864
	nperror("sigaction");

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

869
870
    return 0;
}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
871
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
872

873
#ifndef DISABLE_MOUSE
874
void do_mouse(void)
Chris Allegretta's avatar
Chris Allegretta committed
875
{
876
    int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
877

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
878
    if (get_mouseinput(&mouse_x, &mouse_y, 1) == 0) {
Chris Allegretta's avatar
Chris Allegretta committed
879

880
881
882
883
884
885
886
887
	/* Click in the edit window to move the cursor, but only when
	   we're not in a subfunction. */
	if (wenclose(edit, mouse_y, mouse_x) && currshortcut == main_list) {
	    int sameline;
		/* Did they click on the line with the cursor?  If they
		   clicked on the cursor, we set the mark. */
	    size_t xcur;
		/* The character they clicked on. */
888

889
890
891
	    /* Subtract out size of topwin.  Perhaps we need a constant
	       somewhere? */
	    mouse_y -= 2;
892

893
	    sameline = (mouse_y == current_y);
894

895
896
897
898
899
	    /* Move to where the click occurred. */
	    for (; current_y < mouse_y && current->next != NULL; current_y++)
		current = current->next;
	    for (; current_y > mouse_y && current->prev != NULL; current_y--)
		current = current->prev;
900

901
902
	    xcur = actual_x(current->data, get_page_start(xplustabs()) +
		mouse_x);
903

904
905
906
907
908
909
910
911
912
	    /* Selecting where the cursor is toggles the mark.  As does
	       selecting beyond the line length with the cursor at the
	       end of the line. */
	    if (sameline && xcur == current_x) {
		if (ISSET(VIEW_MODE)) {
		    print_view_warning();
		    return;
		}
		do_mark();
913
	    }
Chris Allegretta's avatar
Chris Allegretta committed
914

915
916
917
918
	    current_x = xcur;
	    placewewant = xplustabs();
	    edit_refresh();
	}
Chris Allegretta's avatar
Chris Allegretta committed
919
    }
920
921
    /* FIXME: If we clicked on a location in the statusbar, the cursor
       should move to the location we clicked on. */
922
}
Chris Allegretta's avatar
Chris Allegretta committed
923
#endif
924

925
/* The user typed a character; add it to the edit buffer. */
926
927
928
929
void do_char(char ch)
{
    size_t current_len = strlen(current->data);
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
930
    int refresh = FALSE;
931
932
933
	/* Do we have to run edit_refresh(), or can we get away with
	 * update_line()? */
#endif
934

935
936
937
938
939
940
941
942
943
944
945
    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! */
946
    if (filebot == current)
947
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
948

949
    /* More dangerousness fun =) */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
950
    current->data = charealloc(current->data, current_len + 2);
951
    assert(current_x <= current_len);
952
953
    charmove(&current->data[current_x + 1], &current->data[current_x],
	current_len - current_x + 1);
954
955
956
    current->data[current_x] = ch;
    totsize++;
    set_modified();
957

Chris Allegretta's avatar
Chris Allegretta committed
958
#ifndef NANO_SMALL
959
    /* Note that current_x has not yet been incremented. */
960
961
    if (current == mark_beginbuf && current_x < mark_beginx)
	mark_beginx++;
Chris Allegretta's avatar
Chris Allegretta committed
962
#endif
963

964
    do_right();
965

966
967
968
969
#ifndef DISABLE_WRAPPING
    if (!ISSET(NO_WRAP) && ch != '\t')
	refresh = do_wrap(current);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
970

971
#ifdef ENABLE_COLOR
972
    if (ISSET(COLOR_SYNTAX))
973
	refresh = TRUE;
974
#endif
Chris Allegretta's avatar
Chris Allegretta committed
975

976
977
978
979
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
    if (refresh)
	edit_refresh();
#endif
Chris Allegretta's avatar
Chris Allegretta committed
980
981
}

982
983
int do_verbatim_input(void)
{
984
    int *verbatim_kbinput;	/* Used to hold verbatim input. */
985
986
    size_t verbatim_len;	/* Length of verbatim input. */
    size_t i;
987
988

    statusbar(_("Verbatim input"));
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
989
    verbatim_kbinput = get_verbatim_kbinput(edit, &verbatim_len, 1);
990
991
992
993
994
995

    /* 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);
    for (i = 0; i < verbatim_len; i++)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
996
	do_char((char)verbatim_kbinput[i]);
997
998
999
1000
1001
1002
1003
    UNSET(DISABLE_CURPOS);

    free(verbatim_kbinput);

    return 1;
}

Chris Allegretta's avatar
Chris Allegretta committed
1004
1005
int do_backspace(void)
{
1006
    if (current != fileage || current_x > 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1007
	do_left();
1008
	do_delete();
Chris Allegretta's avatar
Chris Allegretta committed
1009
1010
1011
1012
1013
1014
    }
    return 1;
}

int do_delete(void)
{
1015
1016
    assert(current != NULL && current->data != NULL && current_x <=
	strlen(current->data));
Chris Allegretta's avatar
Chris Allegretta committed
1017

1018
    placewewant = xplustabs();
1019

1020
1021
    if (current->data[current_x] != '\0') {
	size_t linelen = strlen(current->data + current_x);
1022

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

1025
	/* Let's get dangerous. */
1026
	charmove(&current->data[current_x], &current->data[current_x + 1],
1027
		linelen);
Chris Allegretta's avatar
Chris Allegretta committed
1028

1029
1030
1031
1032
	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
1033
#endif
1034
1035
    } else if (current != filebot && (current->next != filebot ||
	current->data[0] == '\0')) {
1036
	/* We can delete the line before filebot only if it is blank: it
1037
1038
	 * becomes the new magic line then. */
	filestruct *foo = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
1039

1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
	assert(current_x == strlen(current->data));
	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
1050
	if (filebot == foo)
Chris Allegretta's avatar
Chris Allegretta committed
1051
1052
1053
1054
1055
1056
	    filebot = current;

	unlink_node(foo);
	delete_node(foo);
	renumber(current);
	totlines--;
1057
	wrap_reset();
Chris Allegretta's avatar
Chris Allegretta committed
1058
1059
1060
1061
1062
    } else
	return 0;

    totsize--;
    set_modified();
1063
    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
1064
1065
1066
    return 1;
}

1067
int do_tab(void)
Chris Allegretta's avatar
Chris Allegretta committed
1068
{
1069
1070
    do_char('\t');
    return 1;
Chris Allegretta's avatar
Chris Allegretta committed
1071
1072
}

1073
1074
/* Someone hits return *gasp!* */
int do_enter(void)
Chris Allegretta's avatar
Chris Allegretta committed
1075
{
1076
1077
    filestruct *newnode;
    char *tmp;
Chris Allegretta's avatar
Chris Allegretta committed
1078

1079
1080
1081
    newnode = make_new_node(current);
    assert(current != NULL && current->data != NULL);
    tmp = &current->data[current_x];
1082

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1083
#ifndef NANO_SMALL
1084
1085
1086
1087
    /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
    if (ISSET(AUTOINDENT)) {
	int extra = 0;
	const char *spc = current->data;
1088

1089
	while (isblank(*spc)) {
1090
1091
	    extra++;
	    spc++;
1092
	}
1093
1094
1095
1096
1097
1098
1099
1100
	/* If current_x < extra, then we are breaking the line in the
	 * indentation.  Autoindenting should add only current_x
	 * characters of indentation. */
	if (current_x < extra)
	    extra = current_x;
	else
	    current_x = extra;
	totsize += extra;
Chris Allegretta's avatar
Chris Allegretta committed
1101

1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
	newnode->data = charalloc(strlen(tmp) + extra + 1);
	strncpy(newnode->data, current->data, extra);
	strcpy(&newnode->data[extra], tmp);
    } else 
#endif
    {
	current_x = 0;
	newnode->data = charalloc(strlen(tmp) + 1);
	strcpy(newnode->data, tmp);
    }
    *tmp = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1113

1114
    if (current->next == NULL)
1115
1116
	filebot = newnode;
    splice_node(current, newnode, current->next);
1117

1118
1119
1120
1121
    totsize++;
    renumber(current);
    current = newnode;
    align(&current->data);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1122

1123
1124
1125
1126
1127
1128
1129
    /* The logic here is as follows:
     *    -> If we are at the bottom of the buffer, we want to recenter
     *       (read: rebuild) the screen and forcibly move the cursor.
     *    -> otherwise, we want simply to redraw the screen and update
     *       where we think the cursor is.
     */
    if (current_y == editwinrows - 1) {
1130
1131
1132
1133
1134
1135
#ifndef NANO_SMALL
	if (ISSET(SMOOTHSCROLL))
	    edit_update(current, NONE);
	else
#endif
	    edit_update(current, CENTER);
1136
1137
1138
1139
1140
1141
	reset_cursor();
    } else {
	current_y++;
	edit_refresh();
	update_cursor();
    }
Chris Allegretta's avatar
Chris Allegretta committed
1142

1143
1144
    totlines++;
    set_modified();
Chris Allegretta's avatar
Chris Allegretta committed
1145

1146
1147
    placewewant = xplustabs();
    return 1;
Chris Allegretta's avatar
Chris Allegretta committed
1148
1149
}

1150
1151
#ifndef NANO_SMALL
int do_next_word(void)
Chris Allegretta's avatar
Chris Allegretta committed
1152
{
1153
    assert(current != NULL && current->data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1154

1155
1156
1157
1158
    /* Skip letters in this word first. */
    while (current->data[current_x] != '\0' &&
	    isalnum((int)current->data[current_x]))
	current_x++;
Chris Allegretta's avatar
Chris Allegretta committed
1159

1160
1161
1162
1163
    for (; current != NULL; current = current->next) {
	while (current->data[current_x] != '\0' &&
		!isalnum((int)current->data[current_x]))
	    current_x++;
Chris Allegretta's avatar
Chris Allegretta committed
1164

1165
1166
	if (current->data[current_x] != '\0')
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1167

1168
1169
1170
1171
	current_x = 0;
    }
    if (current == NULL)
	current = filebot;
Chris Allegretta's avatar
Chris Allegretta committed
1172

1173
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1174

1175
1176
1177
1178
    /* Refresh the screen.  If current has run off the bottom, this
     * call puts it at the center line. */
    edit_refresh();

1179
1180
    return 0;
}
Chris Allegretta's avatar
Chris Allegretta committed
1181

1182
1183
1184
/* The same thing for backwards. */
int do_prev_word(void)
{
1185
    assert(current != NULL && current->data != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1186

1187
1188
1189
    /* Skip letters in this word first. */
    while (current_x >= 0 && isalnum((int)current->data[current_x]))
	current_x--;
Chris Allegretta's avatar
Chris Allegretta committed
1190

1191
1192
1193
    for (; current != NULL; current = current->prev) {
	while (current_x >= 0 && !isalnum((int)current->data[current_x]))
	    current_x--;
Chris Allegretta's avatar
Chris Allegretta committed
1194

1195
1196
	if (current_x >= 0)
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1197

1198
1199
	if (current->prev != NULL)
	    current_x = strlen(current->prev->data);
Chris Allegretta's avatar
Chris Allegretta committed
1200
1201
    }

1202
1203
1204
1205
1206
1207
    if (current != NULL) {
	while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
	    current_x--;
    } else {
	current = fileage;
	current_x = 0;
1208
    }
Chris Allegretta's avatar
Chris Allegretta committed
1209

1210
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1211

1212
1213
1214
1215
    /* Refresh the screen.  If current has run off the top, this call
     * puts it at the center line. */
    edit_refresh();

1216
1217
    return 0;
}
Chris Allegretta's avatar
Chris Allegretta committed
1218

1219
1220
int do_mark(void)
{
1221
1222
    TOGGLE(MARK_ISSET);
    if (ISSET(MARK_ISSET)) {
1223
1224
1225
1226
1227
1228
1229
1230
1231
	statusbar(_("Mark Set"));
	mark_beginbuf = current;
	mark_beginx = current_x;
    } else {
	statusbar(_("Mark UNset"));
	edit_refresh();
    }
    return 1;
}
1232
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
1233

1234
#ifndef DISABLE_WRAPPING
1235
1236
void wrap_reset(void)
{
1237
    same_line_wrap = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1238
}
1239
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1240

1241
1242
1243
1244
1245
#ifndef DISABLE_WRAPPING
/* We wrap the given line.  Precondition: we assume the cursor has been 
 * moved forward since the last typed character.  Return value:
 * whether we wrapped. */
int do_wrap(filestruct *inptr)
Chris Allegretta's avatar
Chris Allegretta committed
1246
{
1247
1248
1249
1250
    size_t len = strlen(inptr->data);	/* length of the line we wrap */
    int i = 0;			/* generic loop variable */
    int wrap_loc = -1;		/* index of inptr->data where we wrap */
    int word_back = -1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1251
#ifndef NANO_SMALL
1252
1253
1254
    const char *indentation = NULL;
	/* indentation to prepend to the new line */
    int indent_len = 0;		/* strlen(indentation) */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1255
#endif
1256
1257
1258
1259
1260
1261
1262
1263
    const char *after_break;	/* text after the wrap point */
    int after_break_len;	/* strlen(after_break) */
    int wrapping = 0;		/* do we prepend to the next line? */
    const char *wrap_line = NULL;
	/* the next line, minus indentation */
    int wrap_line_len = 0;	/* strlen(wrap_line) */
    char *newline = NULL;	/* the line we create */
    int new_line_len = 0;	/* eventual length of newline */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1264

1265
1266
/* 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
1267

1268
1269
/* Step 1, finding where to wrap.  We are going to add a new-line
 * after a white-space character.  In this step, we set wrap_loc as the
1270
1271
1272
1273
1274
1275
 * 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.
 *
1276
1277
 * A "legal wrap point" is a white-space character that is not followed by
 * white-space.
1278
1279
1280
1281
1282
1283
1284
1285
1286
 *
 * 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!
 *
 * Note that the code below could be optimised, by not calling strnlenpt()
 * so often. */
1287

1288
1289
1290
1291
1292
#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
1293
    for (; i < len; i++, wrap_line++) {
1294
	/* record where the last word ended */
1295
	if (!isblank(*wrap_line))
1296
1297
1298
1299
1300
1301
	    word_back = i;
	/* if we have found a "legal wrap point" and the current word
	 * extends too far, then we stop */
	if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
	    break;
	/* we record the latest "legal wrap point" */
1302
	if (word_back != i && !isblank(wrap_line[1]))
1303
	    wrap_loc = i;
1304
    }
1305
1306
    if (wrap_loc < 0 || i == len)
	return 0;
Chris Allegretta's avatar
Chris Allegretta committed
1307

1308
1309
1310
/* 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
1311

1312
1313
1314
1315
    /* 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
1316

1317
1318
1319
    /* 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
1320

1321
1322
1323
    /* 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. */
1324
    if (same_line_wrap && inptr->next) {
1325
1326
	wrap_line = inptr->next->data;
	wrap_line_len = strlen(wrap_line);
Chris Allegretta's avatar
Chris Allegretta committed
1327

1328
1329
1330
1331
1332
1333
	/* +1 for the space between after_break and wrap_line */
	if ((new_line_len + 1 + wrap_line_len) <= fill) {
	    wrapping = 1;
	    new_line_len += (1 + wrap_line_len);
	}
    }
1334

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1335
#ifndef NANO_SMALL
1336
    if (ISSET(AUTOINDENT)) {
1337
1338
	/* Indentation comes from the next line if wrapping, else from
	 * this line. */
1339
1340
1341
	indentation = (wrapping ? wrap_line : inptr->data);
	indent_len = indent_length(indentation);
	if (wrapping)
1342
1343
	    /* The wrap_line text should not duplicate indentation.
	     * Note in this case we need not increase new_line_len. */
1344
1345
1346
	    wrap_line += indent_len;
	else
	    new_line_len += indent_len;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1347
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1348
1349
#endif

1350
1351
1352
    /* Now we allocate the new line and copy into it. */
    newline = charalloc(new_line_len + 1);  /* +1 for \0 */
    *newline = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1353

1354
1355
1356
1357
#ifndef NANO_SMALL
    if (ISSET(AUTOINDENT)) {
	strncpy(newline, indentation, indent_len);
	newline[indent_len] = '\0';
1358
    }
1359
#endif
1360
    strcat(newline, after_break);
1361
    /* We end the old line after wrap_loc.  Note this does not eat the
1362
     * space. */
1363
1364
    null_at(&inptr->data, wrap_loc + 1);
    totsize++;
1365
    if (wrapping) {
1366
	/* In this case, totsize increases by 1 since we add a space
1367
	 * between after_break and wrap_line.  If the line already ends
1368
1369
	 * in a tab or a space, we don't add a space and decrement
	 * totsize to account for that. */
1370
	if (!isblank(newline[strlen(newline) - 1]))
1371
1372
1373
	    strcat(newline, " ");
	else
	    totsize--;
1374
1375
1376
1377
1378
	strcat(newline, wrap_line);
	free(inptr->next->data);
	inptr->next->data = newline;
    } else {
	filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
1379

1380
	/* In this case, the file size changes by +1 for the new line, and
1381
	 * +indent_len for the new indentation. */
1382
1383
1384
1385
1386
1387
1388
1389
#ifndef NANO_SMALL
	totsize += indent_len;
#endif
	totlines++;
	temp->data = newline;
	temp->prev = inptr;
	temp->next = inptr->next;
	temp->prev->next = temp;
1390
1391
1392
	/* If temp->next is NULL, then temp is the last line of the
	 * file, so we must set filebot. */
	if (temp->next != NULL)
1393
1394
1395
1396
	    temp->next->prev = temp;
	else
	    filebot = temp;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1397

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

1401
    /* later wraps of this line will be prepended to the next line. */
1402
    same_line_wrap = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1403

1404
1405
1406
1407
    /* 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
1408

1409
1410
1411
1412
1413
1414
    /* 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 +
1415
#endif
1416
1417
1418
1419
		wrap_loc + 1;
	wrap_reset();
	placewewant = xplustabs();
    }
Chris Allegretta's avatar
Chris Allegretta committed
1420

1421
#ifndef NANO_SMALL
1422
1423
1424
1425
1426
1427
1428
1429
1430
    /* 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 must move it
     * 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 */
1431

1432
1433
1434
1435
    /* Place the cursor. */
    reset_cursor();

    return 1;
1436
}
1437
#endif /* !DISABLE_WRAPPING */
1438

1439
#ifndef DISABLE_SPELLER
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1440
/* A word is misspelled in the file.  Let the user replace it.  We
1441
 * return zero if the user cancels. */
1442
int do_int_spell_fix(const char *word)
1443
{
1444
1445
    char *save_search;
    char *save_replace;
1446
    filestruct *current_save = current;
1447
    size_t current_x_save = current_x;
1448
1449
1450
1451
1452
    filestruct *edittop_save = edittop;
	/* Save where we are. */
    int i = 0;
	/* The return value. */
    int reverse_search_set = ISSET(REVERSE_SEARCH);
1453
#ifndef NANO_SMALL
1454
1455
    int case_sens_set = ISSET(CASE_SENSITIVE);
    int mark_set = ISSET(MARK_ISSET);
1456

1457
    SET(CASE_SENSITIVE);
1458
    /* Make sure the marking highlight is off during spell-check. */
1459
1460
    UNSET(MARK_ISSET);
#endif
1461
    /* Make sure spell-check goes forward only. */
1462
    UNSET(REVERSE_SEARCH);
1463

1464
    /* Save the current search/replace strings. */
1465
1466
1467
    search_init_globals();
    save_search = last_search;
    save_replace = last_replace;
1468

1469
    /* Set search/replace strings to misspelled word. */
1470
1471
    last_search = mallocstrcpy(NULL, word);
    last_replace = mallocstrcpy(NULL, word);
1472

1473
    /* Start from the top of the file. */
1474
    current = fileage;
1475
    current_x = -1;
1476

1477
    search_last_line = FALSE;
1478

1479
1480
    /* Find the first whole-word occurrence of word. */
    while (findnextstr(TRUE, TRUE, fileage, 0, word, FALSE) != 0)
1481
1482
	if (is_whole_word(current_x, current->data, word)) {
	    edit_refresh();
1483

1484
	    do_replace_highlight(TRUE, word);
1485

1486
	    /* Allow the replace word to be corrected. */
1487
	    i = statusq(FALSE, spell_list, word,
Chris Allegretta's avatar
Chris Allegretta committed
1488
#ifndef NANO_SMALL
1489
			NULL,
Chris Allegretta's avatar
Chris Allegretta committed
1490
#endif
1491
			 _("Edit a replacement"));
1492

1493
	    do_replace_highlight(FALSE, word);
1494

1495
1496
1497
	    if (i != -1 && strcmp(word, answer)) {
		search_last_line = FALSE;
		current_x--;
1498
		do_replace_loop(word, current_save, &current_x_save, TRUE);
1499
	    }
1500
1501

	    break;
1502
	}
Chris Allegretta's avatar
Chris Allegretta committed
1503

1504
1505
1506
1507
1508
    /* 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
1509

1510
    /* Restore where we were. */
1511
1512
1513
    current = current_save;
    current_x = current_x_save;
    edittop = edittop_save;
Chris Allegretta's avatar
Chris Allegretta committed
1514

1515
    /* Restore search/replace direction. */
1516
1517
    if (reverse_search_set)
	SET(REVERSE_SEARCH);
1518

1519
#ifndef NANO_SMALL
1520
1521
1522
    if (!case_sens_set)
	UNSET(CASE_SENSITIVE);

1523
    /* Restore marking highlight. */
1524
1525
    if (mark_set)
	SET(MARK_ISSET);
1526
1527
#endif

1528
    return i != -1;
Chris Allegretta's avatar
Chris Allegretta committed
1529
1530
}

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

1541
    /* Create all three pipes up front. */
1542
1543
    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
1544

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

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

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

1552
	close(spell_fd[0]);
1553

1554
	/* Replace the standard input with the temp file. */
1555
1556
1557
1558
1559
1560
	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;

1561
	close(tempfile_fd);
1562

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

1567
	close(spell_fd[1]);
1568

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

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

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

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

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

1587
1588
	close(spell_fd[0]);

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

	close(sort_fd[1]);

1595
1596
1597
	/* 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. */
1598
1599
	execlp("sort", "sort", "-f", NULL);

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

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

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

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

1615
1616
	close(sort_fd[0]);

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

	close(uniq_fd[1]);

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

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

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

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

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

1645
    /* Read in the returned spelling errors. */
1646
1647
1648
    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
1649

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

1656
    }
Chris Allegretta's avatar
Chris Allegretta committed
1657

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

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

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

1666
	if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
1667
	    *read_buff_ptr = (char)NULL;
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
	    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
1678

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

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

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

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

1695
1696
1697
1698
1699
1700
1701
1702
    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;
1703

1704
  close_pipes_and_exit:
1705

1706
    /* Don't leak any handles. */
1707
1708
1709
1710
1711
1712
1713
1714
    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
1715
1716
}

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

    if (mark_set) {
	mbb_lineno_cur = mark_beginbuf->lineno;
	UNSET(MARK_ISSET);
    }
1739
#endif
1740

1741
    endwin();
1742

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

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

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

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

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

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

1773
1774
1775
1776
1777
1778
1779
1780
1781
    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;
    }
1782
1783
1784

    refresh();
#ifndef NANO_SMALL
1785
1786
1787
1788
1789
1790
1791
1792
1793
    if (!mark_set) {
	/* Only reload the temp file if it isn't a marked selection. */
#endif
	free_filestruct(fileage);
	global_init(1);
	open_file(tempfile_name, 0, 1);
#ifndef NANO_SMALL
    }

1794
1795
1796
    if (mark_set) {
	do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
	mark_beginbuf = current;
1797
	/* In case the line got shorter, assign mark_beginx. */
1798
1799
1800
1801
	mark_beginx = current_x;
	SET(MARK_ISSET);
    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1802

1803
1804
    /* Go back to the old position, mark the file as modified, and make
     * sure that the titlebar is refreshed. */
1805
1806
1807
1808
    do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
    set_modified();
    clearok(topwin, FALSE);
    titlebar(NULL);
1809

1810
    return NULL;
1811
}
1812

1813
1814
int do_spell(void)
{
1815
    int i;
1816
    char *temp, *spell_msg;
1817

1818
1819
1820
1821
1822
    if ((temp = safe_tempnam(0, "nano.")) == NULL) {
	statusbar(_("Could not create a temporary filename: %s"),
		  strerror(errno));
	return 0;
    }
1823

1824
1825
1826
1827
1828
1829
1830
1831
#ifndef NANO_SMALL
    if (ISSET(MARK_ISSET))
	i = write_marked(temp, 1, 0, 0);
    else
#endif
	i = write_file(temp, 1, 0, 0);

    if (i == -1) {
1832
1833
1834
	statusbar(_("Spell checking failed: unable to write temp file!"));
	free(temp);
	return 0;
1835
1836
    }

1837
#ifdef ENABLE_MULTIBUFFER
1838
1839
    /* Update the current open_files entry before spell-checking, in
     * case any problems occur. */
1840
    add_open_file(1);
Chris Allegretta's avatar
Chris Allegretta committed
1841
#endif
1842

1843
    if (alt_speller != NULL)
1844
	spell_msg = do_alt_speller(temp);
1845
    else
1846
	spell_msg = do_int_speller(temp);
1847
    unlink(temp);
1848
    free(temp);
1849

1850
    if (spell_msg != NULL) {
1851
1852
1853
	statusbar(_("Spell checking failed: %s"), spell_msg);
	return 0;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1854

1855
1856
    statusbar(_("Finished checking spelling"));
    return 1;
1857
}
1858
#endif /* !DISABLE_SPELLER */
1859

1860
#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
Chris Allegretta's avatar
Chris Allegretta committed
1861
1862
/* The "indentation" of a line is the white-space between the quote part
 * and the non-white-space of the line. */
1863
1864
size_t indent_length(const char *line)
{
Chris Allegretta's avatar
Chris Allegretta committed
1865
    size_t len = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1866

Chris Allegretta's avatar
Chris Allegretta committed
1867
    assert(line != NULL);
1868
    while (isblank(*line)) {
Chris Allegretta's avatar
Chris Allegretta committed
1869
1870
	line++;
	len++;
Chris Allegretta's avatar
Chris Allegretta committed
1871
    }
Chris Allegretta's avatar
Chris Allegretta committed
1872
    return len;
Chris Allegretta's avatar
Chris Allegretta committed
1873
}
1874
#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
Chris Allegretta's avatar
Chris Allegretta committed
1875

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

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

    back = line->data + skip;
1900
    for (front = back; ; front++) {
1901
	int remove_space = FALSE;
1902
1903
	    /* Do we want to remove this space? */

1904
	if (*front == '\t')
Chris Allegretta's avatar
Chris Allegretta committed
1905
	    *front = ' ';
1906

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

1912
	    remove_space = TRUE;
1913
	    for (bob = back - 2; bob >= line->data + skip; bob--) {
1914
		if (strchr(punct, *bob) != NULL) {
1915
		    remove_space = FALSE;
1916
1917
1918
1919
1920
1921
1922
1923
		    break;
		}
		if (strchr(brackets, *bob) == NULL)
		    break;
	    }
	}

	if (remove_space) {
Chris Allegretta's avatar
Chris Allegretta committed
1924
	    /* Now *front is a space we want to remove.  We do that by
1925
	     * simply failing to assign it to *back. */
Chris Allegretta's avatar
Chris Allegretta committed
1926
1927
1928
1929
#ifndef NANO_SMALL
	    if (mark_beginbuf == line && back - line->data < mark_beginx)
		mark_beginx--;
#endif
1930
1931
	    if (*front == '\0')
		*(back - 1) = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1932
1933
1934
1935
	} else {
	    *back = *front;
	    back++;
	}
1936
1937
	if (*front == '\0')
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1938
1939
    }

1940
    back--;
1941
    assert(*back == '\0' && *front == '\0');
Chris Allegretta's avatar
Chris Allegretta committed
1942

Chris Allegretta's avatar
Chris Allegretta committed
1943
1944
    /* Now back is the new end of line->data. */
    if (back != front) {
1945
	totsize -= front - back;
Chris Allegretta's avatar
Chris Allegretta committed
1946
1947
1948
1949
1950
	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
1951
    }
Chris Allegretta's avatar
Chris Allegretta committed
1952
}
Chris Allegretta's avatar
Chris Allegretta committed
1953

Chris Allegretta's avatar
Chris Allegretta committed
1954
1955
1956
1957
1958
1959
1960
/* 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. */
#ifdef HAVE_REGEX_H
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1961
1962
size_t quote_length(const char *line, const regex_t *qreg)
{
Chris Allegretta's avatar
Chris Allegretta committed
1963
1964
    regmatch_t matches;
    int rc = regexec(qreg, line, 1, &matches, 0);
Chris Allegretta's avatar
Chris Allegretta committed
1965

Chris Allegretta's avatar
Chris Allegretta committed
1966
1967
1968
1969
1970
    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1)
	return 0;
    /* matches.rm_so should be 0, since the quote string should start with
     * the caret ^. */
    return matches.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
1971
}
Chris Allegretta's avatar
Chris Allegretta committed
1972
#else	/* !HAVE_REGEX_H */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1973
1974
size_t quote_length(const char *line)
{
Chris Allegretta's avatar
Chris Allegretta committed
1975
1976
    size_t qdepth = 0;
    size_t qlen = strlen(quotestr);
1977
1978

    /* Compute quote depth level */
Chris Allegretta's avatar
Chris Allegretta committed
1979
1980
1981
1982
1983
    while (!strcmp(line + qdepth, quotestr))
	qdepth += qlen;
    return qdepth;
}
#endif	/* !HAVE_REGEX_H */
Chris Allegretta's avatar
Chris Allegretta committed
1984

Chris Allegretta's avatar
Chris Allegretta committed
1985
1986
1987
/* 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. */
1988
1989
int quotes_match(const char *a_line, size_t a_quote, IFREG(const char
	*b_line, const regex_t *qreg))
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1990
{
Chris Allegretta's avatar
Chris Allegretta committed
1991
1992
    /* Here is the assumption about a_quote: */
    assert(a_quote == quote_length(IFREG(a_line, qreg)));
Chris Allegretta's avatar
Chris Allegretta committed
1993
    return a_quote == quote_length(IFREG(b_line, qreg)) &&
Chris Allegretta's avatar
Chris Allegretta committed
1994
1995
	!strncmp(a_line, b_line, a_quote);
}
1996

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

Chris Allegretta's avatar
Chris Allegretta committed
2005
2006
    return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent);
}
Chris Allegretta's avatar
Chris Allegretta committed
2007

Chris Allegretta's avatar
Chris Allegretta committed
2008
/* Put the next par_len lines, starting with first_line, in the cut
2009
2010
2011
 * 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. */
2012
2013
filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
	quote_len)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2014
{
2015
2016
2017
    /* 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. */
2018
    filestruct *alice = first_line;
Chris Allegretta's avatar
Chris Allegretta committed
2019
2020

    set_modified();
2021
    cutbuffer = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2022
    for (; par_len > 0; par_len--) {
2023
	filestruct *bob = copy_node(alice);
Chris Allegretta's avatar
Chris Allegretta committed
2024

2025
	if (alice == first_line)
Chris Allegretta's avatar
Chris Allegretta committed
2026
	    first_line = bob;
2027
	if (alice == current)
Chris Allegretta's avatar
Chris Allegretta committed
2028
	    current = bob;
2029
	if (alice == edittop)
Chris Allegretta's avatar
Chris Allegretta committed
2030
2031
	    edittop = bob;
#ifndef NANO_SMALL
2032
	if (alice == mark_beginbuf)
Chris Allegretta's avatar
Chris Allegretta committed
2033
2034
	    mark_beginbuf = bob;
#endif
2035
	justify_format(bob, quote_len + indent_length(bob->data + quote_len));
Chris Allegretta's avatar
Chris Allegretta committed
2036

2037
	assert(alice != NULL && bob != NULL);
2038
	add_to_cutbuffer(alice, FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2039
	splice_node(bob->prev, bob, bob->next);
2040
	alice = bob->next;
Chris Allegretta's avatar
Chris Allegretta committed
2041
2042
2043
2044
    }
    return first_line;
}

Chris Allegretta's avatar
Chris Allegretta committed
2045
2046
2047
/* Is it possible to break line at or before goal? */
int breakable(const char *line, int goal)
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2048
    for (; *line != '\0' && goal >= 0; line++) {
2049
	if (isblank(*line))
Chris Allegretta's avatar
Chris Allegretta committed
2050
2051
2052
2053
2054
2055
2056
	    return TRUE;

	if (is_cntrl_char(*line) != 0)
	    goal -= 2;
	else
	    goal -= 1;
    }
Chris Allegretta's avatar
Chris Allegretta committed
2057
2058
2059
    /* If goal is not negative, the whole line (one word) was short
     * enough. */
    return goal >= 0;
Chris Allegretta's avatar
Chris Allegretta committed
2060
2061
}

Chris Allegretta's avatar
Chris Allegretta committed
2062
2063
2064
2065
2066
/* We are trying to break a chunk off line.  We find the last space such
 * that the display length to there is at most goal + 1.  If there is
 * no such space, and force is not 0, 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. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2067
2068
int break_line(const char *line, int goal, int force)
{
Chris Allegretta's avatar
Chris Allegretta committed
2069
2070
2071
2072
2073
2074
    /* Note that we use int instead of size_t, since goal is at most COLS,
     * the screen width, which will always be reasonably small. */
    int space_loc = -1;
	/* Current tentative return value.  Index of the last space we
	 * found with short enough display width.  */
    int cur_loc = 0;
2075
	/* Current index in line. */
Chris Allegretta's avatar
Chris Allegretta committed
2076
2077

    assert(line != NULL);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2078
    for (; *line != '\0' && goal >= 0; line++, cur_loc++) {
Chris Allegretta's avatar
Chris Allegretta committed
2079
2080
2081
2082
	if (*line == ' ')
	    space_loc = cur_loc;
	assert(*line != '\t');

Chris Allegretta's avatar
Chris Allegretta committed
2083
	if (is_cntrl_char(*line))
Chris Allegretta's avatar
Chris Allegretta committed
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
	    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
2094
	    for (; *line != '\0'; line++, cur_loc++)
2095
		if (*line == ' ' && *(line + 1) != ' ' && *(line + 1) != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
2096
2097
2098
2099
		    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
2100
     * of justify_format(), there can be only two adjacent. */
Chris Allegretta's avatar
Chris Allegretta committed
2101
2102
2103
2104
2105
2106
    if (*(line - cur_loc + space_loc + 1) == ' ' ||
	*(line - cur_loc + space_loc + 1) == '\0')
	space_loc++;
    return space_loc;
}

2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
/* Search a paragraph.  If search_type is 0, search for the beginning of
 * the current paragraph or, if we're at the end of it, the beginning of
 * the next paragraph.  If search_type is 1, search for the beginning of
 * the current paragraph or, if we're already there, the beginning of
 * the previous paragraph.  If search_type is 2, search for the end of
 * the current paragraph or, if we're already there, the end of the next
 * paragraph.  Afterwards, save the quote length, paragraph length, and
 * indentation length in *quote, *par, and *indent if they aren't NULL,
 * and refresh the screen if do_refresh is nonzero.  Return 0 if we
 * found a paragraph, or 1 if there was an error or we didn't find a
2117
2118
 * paragraph.
 *
2119
 * To explain the searching algorithm, I first need to define some
Chris Allegretta's avatar
Chris Allegretta committed
2120
2121
2122
2123
2124
2125
2126
2127
 * phrases about paragraphs and quotation:
 *   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.
 *
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2128
2129
 *   A line is "the beginning of a paragraph" if it is part of a
 *   paragraph and
Chris Allegretta's avatar
Chris Allegretta committed
2130
2131
2132
2133
 *	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
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2134
2135
 *	4) the indentation of this line is not an initial substring of
 *	   the indentation of the previous line, or
Chris Allegretta's avatar
Chris Allegretta committed
2136
2137
2138
 *	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
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2139
2140
2141
 *   indented line is expected to start a paragraph, like in books.
 *   Thus, nano can justify an indented paragraph only if AUTOINDENT is
 *   turned on.
Chris Allegretta's avatar
Chris Allegretta committed
2142
2143
 *
 *   A contiguous set of lines is a "paragraph" if each line is part of
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2144
 *   a paragraph and only the first line is the beginning of a
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2145
 *   paragraph. */
2146
2147
2148
int do_para_search(int search_type, size_t *quote, size_t *par, size_t
	*indent, int do_refresh)
{
Chris Allegretta's avatar
Chris Allegretta committed
2149
    size_t quote_len;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2150
	/* Length of the initial quotation of the paragraph we
2151
	 * search. */
Chris Allegretta's avatar
Chris Allegretta committed
2152
2153
2154
    size_t par_len;
	/* Number of lines in that paragraph. */

2155
2156
    /* We save this global variable to see if we're where we started
     * when searching for the beginning of the paragraph. */
Chris Allegretta's avatar
Chris Allegretta committed
2157
    filestruct *current_save = current;
2158

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2159
2160
    size_t indent_len;	/* Generic indentation length. */
    filestruct *line;	/* Generic line of text. */
Robert Siemborski's avatar
Robert Siemborski committed
2161

2162
2163
2164
    static int do_restart = 1;
    	/* Whether we're restarting when searching for the beginning
    	 * line of the paragraph. */
2165

Chris Allegretta's avatar
Chris Allegretta committed
2166
#ifdef HAVE_REGEX_H
2167
2168
    regex_t qreg;	/* qreg is the compiled quotation regexp.  We no
			 * longer care about quotestr. */
Chris Allegretta's avatar
Chris Allegretta committed
2169
    int rc = regcomp(&qreg, quotestr, REG_EXTENDED);
Chris Allegretta's avatar
Chris Allegretta committed
2170

2171
    if (rc != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
2172
2173
	size_t size = regerror(rc, &qreg, NULL, 0);
	char *strerror = charalloc(size);
Chris Allegretta's avatar
Chris Allegretta committed
2174

Chris Allegretta's avatar
Chris Allegretta committed
2175
2176
2177
	regerror(rc, &qreg, strerror, size);
	statusbar(_("Bad quote string %s: %s"), quotestr, strerror);
	free(strerror);
2178
	return 1;
Chris Allegretta's avatar
Chris Allegretta committed
2179
    }
Chris Allegretta's avatar
Chris Allegretta committed
2180
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2181

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

2185
2186
    current_x = 0;

2187
  restart_para_search:
2188
2189
2190
2191
    /* 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.  Otherwise, we move to the first line that is in a
     * paragraph. */
Chris Allegretta's avatar
Chris Allegretta committed
2192
2193
    quote_len = quote_length(IFREG(current->data, &qreg));
    indent_len = indent_length(current->data + quote_len);
Robert Siemborski's avatar
Robert Siemborski committed
2194

Chris Allegretta's avatar
Chris Allegretta committed
2195
2196
    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
2197
2198
	 * the first line of this paragraph.  First we check items 1)
	 * and 3) above. */
2199
	while (current->prev != NULL && quotes_match(current->data,
Chris Allegretta's avatar
Chris Allegretta committed
2200
			quote_len, IFREG(current->prev->data, &qreg))) {
2201
	    size_t temp_id_len =
Chris Allegretta's avatar
Chris Allegretta committed
2202
			indent_length(current->prev->data + quote_len);
2203
		/* The indentation length of the previous line. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2204

2205
	    /* Is this line the beginning of a paragraph, according to
2206
	     * items 2), 5), or 4) above?  If so, stop. */
2207
	    if (current->prev->data[quote_len + temp_id_len] == '\0' ||
2208
		(quote_len == 0 && indent_len > 0
2209
#ifndef NANO_SMALL
2210
		&& !ISSET(AUTOINDENT)
2211
#endif
2212
2213
		) || !indents_match(current->prev->data + quote_len,
		temp_id_len, current->data + quote_len, indent_len))
2214
2215
2216
2217
		break;
	    indent_len = temp_id_len;
	    current = current->prev;
	    current_y--;
Chris Allegretta's avatar
Chris Allegretta committed
2218
	}
2219
    } else if (search_type == 1) {
2220
2221
2222
2223
2224
2225
	/* This line is not part of a paragraph.  Move up until we get
	 * to a non "blank" line, and then move down once. */
	do {
	    /* There is no previous paragraph, so nothing to move to. */
	    if (current->prev == NULL) {
		placewewant = 0;
2226
2227
		if (do_refresh)
		    edit_refresh();
2228
2229
2230
2231
2232
#ifdef HAVE_REGEX_H
		if (!do_restart)
		    regfree(&qreg);
#endif
		return 1;
2233
2234
2235
2236
2237
2238
2239
	    }
	    current = current->prev;
	    current_y--;
	    quote_len = quote_length(IFREG(current->data, &qreg));
	    indent_len = indent_length(current->data + quote_len);
	} while (current->data[quote_len + indent_len] == '\0');
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
2240
    } else {
Chris Allegretta's avatar
Chris Allegretta committed
2241
2242
	/* 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
2243
	do {
2244
	    /* There is no next paragraph, so nothing to move to. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2245
2246
	    if (current->next == NULL) {
		placewewant = 0;
2247
2248
		if (do_refresh)
		    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
2249
2250
2251
#ifdef HAVE_REGEX_H
		regfree(&qreg);
#endif
2252
		return 1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2253
	    }
Chris Allegretta's avatar
Chris Allegretta committed
2254
	    current = current->next;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2255
	    current_y++;
Chris Allegretta's avatar
Chris Allegretta committed
2256
2257
2258
2259
	    quote_len = quote_length(IFREG(current->data, &qreg));
	    indent_len = indent_length(current->data + quote_len);
	} while (current->data[quote_len + indent_len] == '\0');
    }
2260

2261
2262
    /* 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
2263

2264
2265
    /* Next step, compute par_len, the number of lines in this
     * paragraph. */
Chris Allegretta's avatar
Chris Allegretta committed
2266
2267
2268
2269
    line = current;
    par_len = 1;
    indent_len = indent_length(line->data + quote_len);

2270
    while (line->next != NULL && quotes_match(current->data, quote_len,
2271
	IFREG(line->next->data, &qreg))) {
Chris Allegretta's avatar
Chris Allegretta committed
2272
2273
2274
	size_t temp_id_len = indent_length(line->next->data + quote_len);

	if (!indents_match(line->data + quote_len, indent_len,
2275
		line->next->data + quote_len, temp_id_len) ||
Chris Allegretta's avatar
Chris Allegretta committed
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
		line->next->data[quote_len + temp_id_len] == '\0' ||
		(quote_len == 0 && temp_id_len > 0
#ifndef NANO_SMALL
			&& !ISSET(AUTOINDENT)
#endif
		))
	    break;
	indent_len = temp_id_len;
	line = line->next;
	par_len++;
    }
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301

    if (search_type == 1) {
	/* We're on the same line we started on.  Move up until we get
	 * to a non-"blank" line, restart the search from there until we
	 * find a line that's part of a paragraph, and search once more
	 * so that we end up at the beginning of that paragraph. */
	if (current != fileage && current == current_save && do_restart) {
	    while (current->prev != NULL) {
		size_t i, j = 0;
		current = current->prev;
		current_y--;
		/* Skip over lines consisting only of spacing
		 * characters, as searching for the end of the paragraph
		 * does. */
		for (i = 0; current->data[i] != '\0'; i++) {
2302
		    if (isblank(current->data[i]))
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
			j++;
		    else {
			i = 0;
			j = 1;
			break;
		    }
		}
		if (i != j && strlen(current->data) >=
			(quote_len + indent_len) &&
			current->data[quote_len + indent_len] != '\0') {
		    do_restart = 0;
		    break;
		}
	    }
	    goto restart_para_search;
	} else
	    do_restart = 1;
    }

Chris Allegretta's avatar
Chris Allegretta committed
2322
#ifdef HAVE_REGEX_H
2323
2324
2325
2326
    /* We no longer need to check quotation. */
    regfree(&qreg);
#endif

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

    /* If we're searching for the end of the paragraph, move down the
     * number of lines in the paragraph. */
    if (search_type == 2) {
	for (; par_len > 0; current_y++, par_len--)
2334
	    current = current->next;
2335
2336
2337
    }

    /* Refresh the screen if needed. */
2338
2339
    if (do_refresh)
	edit_refresh();
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362

    /* Save the values of quote_len, par_len, and indent_len if
     * needed. */
    if (quote != NULL)
	*quote = quote_len;
    if (par != NULL)
	*par = par_len;
    if (indent != NULL)
	*indent = indent_len;

    return 0;
}

int do_para_begin(void)
{
    return do_para_search(1, NULL, NULL, NULL, TRUE);
}

int do_para_end(void)
{
    return do_para_search(2, NULL, NULL, NULL, TRUE);
}

2363
2364
2365
/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
 * the current paragraph. */
int do_justify(int full_justify)
2366
2367
2368
2369
2370
2371
{
    size_t quote_len;
	/* Length of the initial quotation of the paragraph we
	 * justify. */
    size_t par_len;
	/* Number of lines in that paragraph. */
2372
2373
2374
    filestruct *first_par_line = NULL;
	/* Will be the first line of the resulting justified paragraph.
	 * For restoring after uncut. */
2375
    filestruct *last_par_line;
2376
2377
2378
	/* 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
2379
	 * one down are stored in the cutbuffer.  We back up the
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
	 * original to restore it later. */

    /* 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;
    filestruct *current_save = current;
    int flags_save = flags;
    long totsize_save = totsize;
    filestruct *edittop_save = edittop;
#ifndef NANO_SMALL
    filestruct *mark_beginbuf_save = mark_beginbuf;
    int mark_beginx_save = mark_beginx;
#endif

    size_t indent_len;	/* Generic indentation length. */
    size_t i;		/* Generic loop variable. */

2398
2399
    /* If we're justifying the entire file, start at the beginning. */
    if (full_justify)
2400
	current = fileage;
2401
2402

    last_par_line = current;
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427

    while (TRUE) {

	/* First, search for the beginning of the current paragraph or,
	 * if we're at the end of it, the beginning of the next
	 * paragraph.  Save the quote length, paragraph length, and
	 * indentation length and 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(0, &quote_len, &par_len, &indent_len, FALSE) != 0) {
	    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();
		return 0;
	    }
	}
2428

2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
	/* Next step, we loop through the lines of this paragraph,
	 * justifying each one individually. */
	for (; par_len > 0; current_y++, par_len--) {
	    size_t line_len;
	    size_t display_len;
		/* The width of current in screen columns. */
	    int break_pos;
		/* Where we will break the line. */

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

	    /* justify_format() removes excess spaces from the line, and
2442
2443
2444
2445
2446
2447
2448
	     * changes tabs to spaces.  After calling it, we call
	     * backup_lines(), which copies the original paragraph to
	     * the cutbuffer for unjustification and then calls
	     * justify_format() on the remaining lines. */
	    justify_format(current, indent_len);
	    if (first_par_line == NULL)
		first_par_line = backup_lines(current, full_justify ?
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
			filebot->lineno - current->lineno : par_len, quote_len);

	    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. */
2471
		    if (quote_len == 0
Chris Allegretta's avatar
Chris Allegretta committed
2472
#ifndef NANO_SMALL
2473
			&& !ISSET(AUTOINDENT)
2474
#endif
2475
			)
2476
2477
2478
2479
2480
			    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
2481
			current->data + break_pos + 1);
2482
		    assert(strlen(current->next->data) ==
Chris Allegretta's avatar
Chris Allegretta committed
2483
			indent_len + line_len - break_pos - 1);
2484
2485
2486
2487
2488
		    totlines++;
		    totsize += indent_len;
		    par_len++;
		} else {
		    size_t next_line_len = strlen(current->next->data);
Chris Allegretta's avatar
Chris Allegretta committed
2489

2490
		    indent_len = quote_len +
Chris Allegretta's avatar
Chris Allegretta committed
2491
			indent_length(current->next->data + quote_len);
2492
		    current->next->data = charealloc(current->next->data,
Chris Allegretta's avatar
Chris Allegretta committed
2493
			next_line_len + line_len - break_pos + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2494

2495
2496
		    charmove(current->next->data + indent_len + line_len -
			break_pos, current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2497
			next_line_len - indent_len + 1);
2498
		    strcpy(current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2499
			current->data + break_pos + 1);
2500
		    current->next->data[indent_len + line_len - break_pos - 1]
Chris Allegretta's avatar
Chris Allegretta committed
2501
2502
			= ' ';
#ifndef NANO_SMALL
2503
2504
2505
2506
2507
		    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
2508
#endif
2509
		}
Chris Allegretta's avatar
Chris Allegretta committed
2510
#ifndef NANO_SMALL
2511
2512
2513
2514
		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
2515
#endif
2516
2517
2518
2519
		null_at(&current->data, break_pos);
		current = current->next;
	    } else if (display_len < fill && par_len > 1) {
		size_t next_line_len;
Chris Allegretta's avatar
Chris Allegretta committed
2520

2521
		indent_len = quote_len +
Chris Allegretta's avatar
Chris Allegretta committed
2522
			indent_length(current->next->data + quote_len);
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
		/* 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
2537
			current->next->data + indent_len, break_pos);
2538
		current->data[line_len + break_pos + 1] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
2539
#ifndef NANO_SMALL
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
		if (mark_beginbuf == current->next) {
		    if (mark_beginx < indent_len + break_pos) {
 			mark_beginbuf = current;
			if (mark_beginx <= indent_len)
			    mark_beginx = line_len + 1;
			else
			    mark_beginx = line_len + 1 + mark_beginx - indent_len;
		    } else
			mark_beginx -= break_pos + 1;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2550
#endif
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
		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--;
		} else {
		    charmove(current->next->data + indent_len,
Chris Allegretta's avatar
Chris Allegretta committed
2566
2567
			current->next->data + indent_len + break_pos + 1,
			next_line_len - break_pos - indent_len);
2568
2569
2570
2571
		    null_at(&current->next->data, next_line_len - break_pos);
		    current = current->next;
		}
	    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2572
  continue_loc:
2573
		/* Go to the next line. */
2574
		current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
2575

2576
2577
	    /* If the line we were on before still exists, and it was
	     * not the last line of the paragraph, add a space to the
2578
	     * end of it to replace the one removed or left out by
2579
2580
2581
2582
2583
2584
2585
2586
2587
	     * justify_format(). */
	    if (current->prev != NULL && par_len > 1) {
		size_t prev_line_len = strlen(current->prev->data);
		current->prev->data = charealloc(current->prev->data,
			prev_line_len + 2);
		current->prev->data[prev_line_len] = ' ';
		current->prev->data[prev_line_len + 1] = '\0';
		totsize++;
	    }
2588
2589
	}

2590
2591
2592
	/* 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. */
2593
2594
2595
2596
	if (!full_justify)
	    break;

    } /* while (TRUE) */
2597

2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
    /* 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
     * fileage, and set current_x.  Also, edit_refresh() needs the line
     * numbers to be right, so renumber(). */
    last_par_line = current->prev;
    if (first_par_line->prev == NULL)
	fileage = first_par_line;
    renumber(first_par_line);

2608
    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
2609

2610
    statusbar(_("Can now UnJustify!"));
2611
    /* Display the shortcut list with UnJustify. */
2612
2613
    shortcut_init(1);
    display_main_list();
2614
2615
    reset_cursor();

Chris Allegretta's avatar
Chris Allegretta committed
2616
    /* Now get a keystroke and see if it's unjustify; if not, unget the
2617
     * keystroke and return. */
2618

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2619
    {
2620
2621
	int meta_key;
	i = get_kbinput(edit, &meta_key);
2622
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2623
2624
2625
2626
2627
2628
2629
	/* If it was a mouse click, parse it with do_mouse() and it
	 * might become the unjustify key.  Else give it back to the
	 * input stream. */
	if (i == KEY_MOUSE)
	    do_mouse();
	else
	    ungetch(i);
2630
	i = get_kbinput(edit, &meta_key);
2631
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2632
    }
2633

2634
    if (i != NANO_UNJUSTIFY_KEY && i != NANO_UNJUSTIFY_FKEY) {
Chris Allegretta's avatar
Chris Allegretta committed
2635
2636
	ungetch(i);
	placewewant = 0;
2637
    } else {
2638
2639
2640
	/* Else restore the justify we just did (ungrateful user!). */
	filestruct *cutbottom = get_cutbottom();

Chris Allegretta's avatar
Chris Allegretta committed
2641
2642
2643
2644
2645
	current = current_save;
	current_x = current_x_save;
	current_y = current_y_save;
	edittop = edittop_save;

2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
	/* Splice the cutbuffer back into the file. */
	cutbottom->next = last_par_line->next;
	cutbottom->next->prev = cutbottom;
	    /* The line numbers after the end of the paragraph have been
	     * changed, so we change them back. */
	renumber(cutbottom->next);
	if (first_par_line->prev != NULL) {
	    cutbuffer->prev = first_par_line->prev;
	    cutbuffer->prev->next = cutbuffer;
	} else
	    fileage = cutbuffer;
	cutbuffer = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
2658

2659
2660
2661
2662
2663
2664
	last_par_line->next = NULL;
	free_filestruct(first_par_line);

	/* Restore global variables from before the justify. */
	totsize = totsize_save;
	totlines = filebot->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2665
#ifndef NANO_SMALL
2666
2667
	mark_beginbuf = mark_beginbuf_save;
	mark_beginx = mark_beginx_save;
Chris Allegretta's avatar
Chris Allegretta committed
2668
#endif
2669
2670
2671
2672
	flags = flags_save;
	if (!ISSET(MODIFIED)) {
	    titlebar(NULL);
	    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2673
2674
	}
	edit_refresh();
2675
    }
Chris Allegretta's avatar
Chris Allegretta committed
2676
2677
    cutbuffer = cutbuffer_save;
    blank_statusbar_refresh();
2678
    /* Display the shortcut list with UnCut. */
2679
2680
    shortcut_init(0);
    display_main_list();
2681

Chris Allegretta's avatar
Chris Allegretta committed
2682
    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
2683
}
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693

int do_justify_void(void)
{
    return do_justify(FALSE);
}

int do_full_justify(void)
{
    return do_justify(TRUE);
}
2694
#endif /* !DISABLE_JUSTIFY */
Chris Allegretta's avatar
Chris Allegretta committed
2695

2696
int do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
2697
{
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
    int i;

    if (!ISSET(MODIFIED)) {

#ifdef ENABLE_MULTIBUFFER
	if (!close_open_file()) {
	    display_main_list();
	    return 1;
	}
	else
2708
#endif
2709
2710
	    finish(0);
    }
2711

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2712
    if (ISSET(TEMP_OPT))
2713
	i = 1;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2714
    else
2715
	i = do_yesno(FALSE, _("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2716
    
2717
2718
#ifdef DEBUG
    dump_buffer(fileage);
2719
#endif
2720
2721

    if (i == 1) {
2722
	if (do_writeout(TRUE) > 0) {
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751

#ifdef ENABLE_MULTIBUFFER
	    if (!close_open_file()) {
		display_main_list();
		return 1;
	    }
	    else
#endif
		finish(0);
	}
    } else if (i == 0) {

#ifdef ENABLE_MULTIBUFFER
	if (!close_open_file()) {
	    display_main_list();
	    return 1;
	}
	else
#endif
	    finish(0);
    } else
	statusbar(_("Cancelled"));

    display_main_list();
    return 1;
}

void signal_init(void)
{
2752
2753
    /* Trap SIGINT and SIGQUIT because we want them to do useful
     * things. */
2754
2755
2756
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
2757
    sigaction(SIGQUIT, &act, NULL);
2758

2759
    /* Trap SIGHUP and SIGTERM because we want to write the file out. */
2760
    act.sa_handler = handle_hupterm;
2761
    sigaction(SIGHUP, &act, NULL);
2762
    sigaction(SIGTERM, &act, NULL);
2763

2764
#ifndef NANO_SMALL
2765
    /* Trap SIGWINCH because we want to handle window resizes. */
2766
2767
    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);
2768
    allow_pending_sigwinch(FALSE);
2769
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2770

2771
    /* Trap normal suspend (^Z) so we can handle it ourselves. */
2772
2773
2774
2775
    if (!ISSET(SUSPEND)) {
	act.sa_handler = SIG_IGN;
	sigaction(SIGTSTP, &act, NULL);
    } else {
2776
2777
	/* Block all other signals in the suspend and continue handlers.
	 * If we don't do this, other stuff interrupts them! */
2778
	sigfillset(&act.sa_mask);
Chris Allegretta's avatar
Chris Allegretta committed
2779

2780
2781
	act.sa_handler = do_suspend;
	sigaction(SIGTSTP, &act, NULL);
2782

2783
2784
2785
2786
	act.sa_handler = do_cont;
	sigaction(SIGCONT, &act, NULL);
    }
}
2787

2788
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
2789
RETSIGTYPE handle_hupterm(int signal)
2790
{
2791
    die(_("Received SIGHUP or SIGTERM\n"));
2792
}
2793

2794
/* Handler for SIGTSTP (suspend). */
2795
2796
2797
RETSIGTYPE do_suspend(int signal)
{
    endwin();
2798
    printf("\n\n\n\n\n%s\n", _("Use \"fg\" to return to nano"));
2799
    fflush(stdout);
2800

2801
    /* Restore the old terminal settings. */
2802
    tcsetattr(0, TCSANOW, &oldterm);
2803

2804
    /* Trap SIGHUP and SIGTERM so we can properly deal with them while
2805
     * suspended. */
2806
2807
2808
2809
    act.sa_handler = handle_hupterm;
    sigaction(SIGHUP, &act, NULL);
    sigaction(SIGTERM, &act, NULL);

2810
    /* Do what mutt does: send ourselves a SIGSTOP. */
2811
2812
    kill(0, SIGSTOP);
}
2813

2814
/* Handler for SIGCONT (continue after suspend). */
2815
2816
RETSIGTYPE do_cont(int signal)
{
2817
#ifndef NANO_SMALL
2818
2819
    /* Perhaps the user resized the window while we slept.  Handle it
     * and update the screen in the process. */
2820
    handle_sigwinch(0);
2821
#else
2822
2823
    /* Just update the screen. */
    doupdate();
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
#endif
}

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

2835
    if (tty == NULL)
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
	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;
2851
2852
    editwinrows = LINES - 5 + no_help();
    if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
2853
2854
2855
2856
2857
2858
	die_too_small();

#ifndef DISABLE_WRAPJUSTIFY
    fill = wrap_at;
    if (fill <= 0)
	fill += COLS;
2859
2860
    if (fill < 0)
	fill = 0;
2861
2862
#endif

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2863
    hblank = charealloc(hblank, COLS + 1);
2864
2865
2866
    memset(hblank, ' ', COLS);
    hblank[COLS] = '\0';

2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
#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
2880

2881
2882
2883
2884
2885
2886
    /* 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. */
2887
    blank_statusbar();
2888
    display_main_list();
2889
2890
    total_refresh();

2891
    /* Turn cursor back on for sure. */
2892
2893
    curs_set(1);

2894
2895
    /* Restore the terminal to its previously saved state. */
    resetty();
2896
2897

    /* Jump back to the main loop. */
2898
2899
    siglongjmp(jmpbuf, 1);
}
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910

void allow_pending_sigwinch(int allow)
{
    sigset_t winch;
    sigemptyset(&winch);
    sigaddset(&winch, SIGWINCH);
    if (allow)
	sigprocmask(SIG_UNBLOCK, &winch, NULL);
    else
	sigprocmask(SIG_BLOCK, &winch, NULL);
}
2911
#endif /* !NANO_SMALL */
2912

2913
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2914
void do_toggle(const toggle *which)
2915
{
Chris Allegretta's avatar
Chris Allegretta committed
2916
    int enabled;
2917

2918
    /* Even easier! */
2919
    TOGGLE(which->flag);
Chris Allegretta's avatar
Chris Allegretta committed
2920

2921
    switch (which->val) {
2922
2923
2924
    case TOGGLE_SUSPEND_KEY:
	signal_init();
	break;
2925
#ifndef DISABLE_MOUSE
2926
2927
2928
    case TOGGLE_MOUSE_KEY:
	mouse_init();
	break;
2929
#endif
2930
    case TOGGLE_NOHELP_KEY:
2931
2932
	blank_statusbar();
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
2933
2934
2935
2936
	wrefresh(bottomwin);
	window_init();
	edit_refresh();
	display_main_list();
2937
	break;
2938
2939
2940
2941
2942
2943
    case TOGGLE_DOS_KEY:
	UNSET(MAC_FILE);
	break;
    case TOGGLE_MAC_KEY:
	UNSET(DOS_FILE);
	break;
2944
#ifdef ENABLE_COLOR
2945
2946
    case TOGGLE_SYNTAX_KEY:
	edit_refresh();
2947
2948
	break;
#endif
2949
    }
Chris Allegretta's avatar
Chris Allegretta committed
2950

Chris Allegretta's avatar
Chris Allegretta committed
2951
2952
2953
2954
2955
2956
2957
    /* 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
2958
}
2959
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2960

2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
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);
}

Chris Allegretta's avatar
Chris Allegretta committed
2999
3000
3001
int main(int argc, char *argv[])
{
    int optchr;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3002
    int startline = 0;		/* Line to try and start at */
3003
    int modify_control_seq = 0;
Chris Allegretta's avatar
Chris Allegretta committed
3004
    int fill_flag_used = 0;	/* Was the fill option used? */
Chris Allegretta's avatar
Chris Allegretta committed
3005
    const shortcut *s;
3006
    int keyhandled = 0;	/* Have we handled the keystroke yet? */
3007
    int kbinput;		/* Input from keyboard */
3008
    int meta_key;
3009

Chris Allegretta's avatar
Chris Allegretta committed
3010
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
3011
    const toggle *t;
Chris Allegretta's avatar
Chris Allegretta committed
3012
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3013
#ifdef HAVE_GETOPT_LONG
3014
    const struct option long_options[] = {
3015
3016
3017
	{"help", 0, 0, 'h'},
#ifdef ENABLE_MULTIBUFFER
	{"multibuffer", 0, 0, 'F'},
Chris Allegretta's avatar
Chris Allegretta committed
3018
3019
#endif
#ifdef ENABLE_NANORC
3020
#ifndef NANO_SMALL
3021
	{"historylog", 0, 0, 'H'},
3022
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3023
	{"ignorercfiles", 0, 0, 'I'},
3024
3025
3026
3027
#endif
#ifndef DISABLE_JUSTIFY
	{"quotestr", 1, 0, 'Q'},
#endif
3028
#ifdef HAVE_REGEX_H
3029
	{"regexp", 0, 0, 'R'},
3030
#endif
3031
	{"tabsize", 1, 0, 'T'},
Chris Allegretta's avatar
Chris Allegretta committed
3032
	{"version", 0, 0, 'V'},
3033
3034
#ifdef ENABLE_COLOR
	{"syntax", 1, 0, 'Y'},
3035
#endif
3036
	{"const", 0, 0, 'c'},
3037
	{"rebinddelete", 0, 0, 'd'},
3038
	{"nofollow", 0, 0, 'l'},
3039
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
3040
	{"mouse", 0, 0, 'm'},
3041
#endif
3042
3043
3044
#ifndef DISABLE_OPERATINGDIR
	{"operatingdir", 1, 0, 'o'},
#endif
3045
	{"preserve", 0, 0, 'p'},
3046
3047
3048
3049
3050
#ifndef DISABLE_WRAPJUSTIFY
	{"fill", 1, 0, 'r'},
#endif
#ifndef DISABLE_SPELLER
	{"speller", 1, 0, 's'},
3051
#endif
3052
3053
	{"tempfile", 0, 0, 't'},
	{"view", 0, 0, 'v'},
3054
#ifndef DISABLE_WRAPPING
3055
	{"nowrap", 0, 0, 'w'},
3056
#endif
3057
3058
	{"nohelp", 0, 0, 'x'},
	{"suspend", 0, 0, 'z'},
3059
#ifndef NANO_SMALL
3060
3061
	{"backup", 0, 0, 'B'},
	{"dos", 0, 0, 'D'},
3062
	{"backupdir", 1, 0, 'E'},
3063
3064
	{"mac", 0, 0, 'M'},
	{"noconvert", 0, 0, 'N'},
3065
	{"smooth", 0, 0, 'S'},
3066
	{"restricted", 0, 0, 'Z'},
3067
3068
	{"autoindent", 0, 0, 'i'},
	{"cut", 0, 0, 'k'},
3069
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3070
3071
3072
3073
	{0, 0, 0, 0}
    };
#endif

3074
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
3075
3076
3077
3078
3079
    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3080
#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
3081
3082
    /* if we don't have rcfile support, we're root, and
       --disable-wrapping-as-root is used, turn wrapping off */
3083
    if (geteuid() == NANO_ROOT_UID)
3084
3085
	SET(NO_WRAP);
#endif
3086

Chris Allegretta's avatar
Chris Allegretta committed
3087
#ifdef HAVE_GETOPT_LONG
3088
    while ((optchr = getopt_long(argc, argv, "h?BDE:FHIMNQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz",
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3089
				 long_options, NULL)) != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
3090
#else
3091
    while ((optchr =
3092
	    getopt(argc, argv, "h?BDE:FHIMNQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz")) != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
3093
3094
3095
#endif

	switch (optchr) {
3096

3097
3098
3099
3100
3101
3102
3103
3104
	case 'a':
	case 'b':
	case 'e':
	case 'f':
	case 'g':
	case 'j':
	    /* Pico compatibility flags */
	    break;
3105
#ifndef NANO_SMALL
3106
3107
3108
	case 'B':
	    SET(BACKUP_FILE);
	    break;
3109
3110
3111
	case 'D':
	    SET(DOS_FILE);
	    break;
3112
3113
3114
	case 'E':
	    backup_dir = mallocstrcpy(backup_dir, optarg);
	    break;
3115
#endif
3116
#ifdef ENABLE_MULTIBUFFER
3117
	case 'F':
3118
	    SET(MULTIBUFFER);
3119
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
3120
3121
#endif
#ifdef ENABLE_NANORC
3122
#ifndef NANO_SMALL
3123
3124
3125
	case 'H':
	    SET(HISTORYLOG);
	    break;
3126
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3127
	case 'I':
Chris Allegretta's avatar
Chris Allegretta committed
3128
3129
	    SET(NO_RCFILE);
	    break;
3130
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3131
3132
3133
3134
#ifndef NANO_SMALL
	case 'M':
	    SET(MAC_FILE);
	    break;
3135
3136
	case 'N':
	    SET(NO_CONVERT);
Chris Allegretta's avatar
Chris Allegretta committed
3137
	    break;
3138
3139
#endif
#ifndef DISABLE_JUSTIFY
3140
	case 'Q':
Chris Allegretta's avatar
Chris Allegretta committed
3141
	    quotestr = mallocstrcpy(quotestr, optarg);
3142
	    break;
3143
#endif
3144
#ifdef HAVE_REGEX_H
3145
3146
3147
	case 'R':
	    SET(USE_REGEXP);
	    break;
3148
3149
3150
3151
3152
#endif
#ifndef NANO_SMALL
	case 'S':
	    SET(SMOOTHSCROLL);
	    break;
3153
#endif
3154
	case 'T':
Chris Allegretta's avatar
Chris Allegretta committed
3155
3156
3157
3158
	    {
		int i;
		char *first_error;

Chris Allegretta's avatar
Chris Allegretta committed
3159
3160
		/* Using strtol() instead of atoi() lets us accept 0
		 * while checking other errors. */
Chris Allegretta's avatar
Chris Allegretta committed
3161
		i = (int)strtol(optarg, &first_error, 10);
Chris Allegretta's avatar
Chris Allegretta committed
3162
		if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
3163
		    usage();
Chris Allegretta's avatar
Chris Allegretta committed
3164
		else
Chris Allegretta's avatar
Chris Allegretta committed
3165
3166
3167
3168
3169
		    tabsize = i;
		if (tabsize <= 0) {
		    fprintf(stderr, _("Tab size is too small for nano...\n"));
		    exit(1);
		}
3170
3171
	    }
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
3172
3173
3174
	case 'V':
	    version();
	    exit(0);
3175
3176
3177
3178
3179
#ifdef ENABLE_COLOR
	case 'Y':
	    syntaxstr = mallocstrcpy(syntaxstr, optarg);
	    break;
#endif
3180
3181
3182
	case 'Z':
	    SET(RESTRICTED);
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
3183
3184
3185
	case 'c':
	    SET(CONSTUPDATE);
	    break;
3186
3187
3188
	case 'd':
	    SET(REBIND_DELETE);
	    break;
3189
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
3190
3191
3192
	case 'i':
	    SET(AUTOINDENT);
	    break;
3193
3194
3195
	case 'k':
	    SET(CUT_TO_END);
	    break;
3196
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3197
	case 'l':
3198
	    SET(NOFOLLOW_SYMLINKS);
Chris Allegretta's avatar
Chris Allegretta committed
3199
	    break;
3200
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
3201
3202
3203
	case 'm':
	    SET(USE_MOUSE);
	    break;
3204
#endif
3205
3206
#ifndef DISABLE_OPERATINGDIR
	case 'o':
3207
	    operating_dir = mallocstrcpy(operating_dir, optarg);
3208
3209
	    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3210
	case 'p':
3211
	    SET(PRESERVE);
Chris Allegretta's avatar
Chris Allegretta committed
3212
	    break;
3213
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
3214
3215
3216
3217
3218
	case 'r':
	    {
		int i;
		char *first_error;

Chris Allegretta's avatar
Chris Allegretta committed
3219
3220
		/* Using strtol() instead of atoi() lets us accept 0
		 * while checking other errors. */
Chris Allegretta's avatar
Chris Allegretta committed
3221
		i = (int)strtol(optarg, &first_error, 10);
Chris Allegretta's avatar
Chris Allegretta committed
3222
		if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
Chris Allegretta's avatar
Chris Allegretta committed
3223
		    usage();
Chris Allegretta's avatar
Chris Allegretta committed
3224
		else
Chris Allegretta's avatar
Chris Allegretta committed
3225
		    wrap_at = i;
Chris Allegretta's avatar
Chris Allegretta committed
3226
	    }
Chris Allegretta's avatar
Chris Allegretta committed
3227
	    fill_flag_used = 1;
Chris Allegretta's avatar
Chris Allegretta committed
3228
	    break;
3229
#endif
3230
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
3231
	case 's':
3232
	    alt_speller = mallocstrcpy(alt_speller, optarg);
Chris Allegretta's avatar
Chris Allegretta committed
3233
	    break;
3234
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3235
	case 't':
3236
	    SET(TEMP_OPT);
Chris Allegretta's avatar
Chris Allegretta committed
3237
3238
3239
3240
	    break;
	case 'v':
	    SET(VIEW_MODE);
	    break;
3241
#ifndef DISABLE_WRAPPING
3242
	case 'w':
Chris Allegretta's avatar
Chris Allegretta committed
3243
3244
	    SET(NO_WRAP);
	    break;
3245
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
	case 'x':
	    SET(NO_HELP);
	    break;
	case 'z':
	    SET(SUSPEND);
	    break;
	default:
	    usage();
	}
    }

3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
    /* If filename starts with 'r', we use restricted mode. */
    if (*(tail(argv[0])) == 'r')
	SET(RESTRICTED);

    /* If we're using restricted mode, disable suspending, backup files,
     * and reading rcfiles. */
    if (ISSET(RESTRICTED)) {
	UNSET(SUSPEND);
	UNSET(BACKUP_FILE);
	SET(NO_RCFILE);
    }

Chris Allegretta's avatar
Chris Allegretta committed
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
/* We've read through the command line options.  Now back up the flags
   and values that are set, and read the rcfile(s).  If the values
   haven't changed afterward, restore the backed-up values. */
#ifdef ENABLE_NANORC
    if (!ISSET(NO_RCFILE)) {
#ifndef DISABLE_OPERATINGDIR
	char *operating_dir_cpy = operating_dir;
#endif
#ifndef DISABLE_WRAPPING
	int wrap_at_cpy = wrap_at;
#endif
3280
3281
3282
#ifndef NANO_SMALL
	char *backup_dir_cpy = backup_dir;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3283
3284
3285
3286
3287
3288
3289
3290
3291
#ifndef DISABLE_JUSTIFY
	char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
	char *alt_speller_cpy = alt_speller;
#endif
	int tabsize_cpy = tabsize;
	long flags_cpy = flags;

3292
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
3293
	operating_dir = NULL;
3294
#endif
3295
3296
3297
#ifndef NANO_SMALL
	backup_dir = NULL;
#endif
3298
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
3299
	quotestr = NULL;
3300
3301
#endif
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
3302
	alt_speller = NULL;
3303
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316

	do_rcfile();

#ifndef DISABLE_OPERATINGDIR
	if (operating_dir_cpy != NULL) {
	    free(operating_dir);
	    operating_dir = operating_dir_cpy;
	}
#endif
#ifndef DISABLE_WRAPPING
	if (fill_flag_used)
	    wrap_at = wrap_at_cpy;
#endif
3317
3318
3319
3320
3321
3322
#ifndef NANO_SMALL
	if (backup_dir_cpy != NULL) {
	    free(backup_dir);
	    backup_dir = backup_dir_cpy;
	}
#endif	
Chris Allegretta's avatar
Chris Allegretta committed
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
#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
	if (tabsize_cpy > 0)
	    tabsize = tabsize_cpy;
	flags |= flags_cpy;
    }
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
3340
    else if (geteuid() == NANO_ROOT_UID)
Chris Allegretta's avatar
Chris Allegretta committed
3341
3342
3343
3344
	SET(NO_WRAP);
#endif
#endif /* ENABLE_NANORC */

3345
3346
3347
3348
3349
3350
3351
3352
#ifndef NANO_SMALL
    history_init();
#ifdef ENABLE_NANORC
    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
	load_history();
#endif
#endif

3353
3354
3355
3356
3357
3358
#ifndef NANO_SMALL
    /* Set up the backup directory.  This entails making sure it exists
     * and is a directory, so that backup files will be saved there. */
    init_backup_dir();
#endif

3359
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
3360
3361
    /* Set up the operating directory.  This entails chdir()ing there,
       so that file reads and writes will be based there. */
3362
3363
3364
    init_operating_dir();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3365
3366
3367
3368
3369
3370
3371
3372
#ifndef DISABLE_JUSTIFY
    if (quotestr == NULL)
#ifdef HAVE_REGEX_H
	quotestr = mallocstrcpy(NULL, "^([ \t]*[|>:}#])+");
#else
	quotestr = mallocstrcpy(NULL, "> ");
#endif
#endif /* !DISABLE_JUSTIFY */
3373

Chris Allegretta's avatar
Chris Allegretta committed
3374
3375
3376
    if (tabsize == -1)
	tabsize = 8;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3377
3378
3379
3380
    /* Clear the filename we'll be using */
    filename = charalloc(1);
    filename[0] = '\0';

Chris Allegretta's avatar
Chris Allegretta committed
3381
3382
3383
3384
3385
3386
3387
    /* If there's a +LINE flag, it is the first non-option argument. */
    if (0 < optind && optind < argc && argv[optind][0] == '+') {
	startline = atoi(&argv[optind][1]);
	optind++;
    }
    if (0 < optind && optind < argc)
	filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta's avatar
Chris Allegretta committed
3388
3389
3390

    /* See if there's a non-option in argv (first non-option is the
       filename, if +LINE is not given) */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3391
    if (argc > 1 && argc > optind) {
Chris Allegretta's avatar
Chris Allegretta committed
3392
3393
3394
3395
	/* Look for the +line flag... */
	if (argv[optind][0] == '+') {
	    startline = atoi(&argv[optind][1]);
	    optind++;
Chris Allegretta's avatar
Chris Allegretta committed
3396
	    if (argc > optind)
Chris Allegretta's avatar
Chris Allegretta committed
3397
3398
3399
		filename = mallocstrcpy(filename, argv[optind]);
	} else
	    filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta's avatar
Chris Allegretta committed
3400
3401
    }

3402
    /* Back up the old terminal settings so that they can be restored. */
3403
    tcgetattr(0, &oldterm);
3404

3405
   /* Curses initialization stuff: Start curses, save the state of the
3406
3407
    * terminal mode, put the terminal in cbreak mode (read one character
    * at a time and interpret the special control keys), disable
3408
    * translation of carriage return (^M) into newline (^J) so that we
3409
    * can tell the difference between the Enter key and Ctrl-J, and
3410
3411
3412
3413
    * disable echoing of characters as they're typed.  Finally, disable
    * interpretation of the special control keys, and if we're not in
    * preserve mode, disable interpretation of the flow control
    * characters too. */
Chris Allegretta's avatar
Chris Allegretta committed
3414
    initscr();
3415
    cbreak();
3416
    nonl();
3417
    noecho();
3418
3419
3420
    disable_signals();
    if (!ISSET(PRESERVE))
	disable_flow_control();
3421
3422
3423
3424
3425
3426

#ifndef NANO_SMALL
    /* Save the terminal's current state, so that we can restore it
     * after a resize. */
    savetty();
#endif
3427

3428
    /* Set up the global variables and the shortcuts. */
3429
    global_init(0);
3430
    shortcut_init(0);
3431
3432

    /* Set up the signal handlers. */
3433
    signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
3434
3435

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

3439
    window_init();
3440
#ifndef DISABLE_MOUSE
3441
    mouse_init();
3442
#endif
3443

Chris Allegretta's avatar
Chris Allegretta committed
3444
#ifdef DEBUG
3445
    fprintf(stderr, "Main: bottom win\n");
Chris Allegretta's avatar
Chris Allegretta committed
3446
#endif
3447
    /* Set up bottom of window */
Chris Allegretta's avatar
Chris Allegretta committed
3448
3449
3450
    display_main_list();

#ifdef DEBUG
3451
    fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
3452
3453
#endif

3454
    open_file(filename, 0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
3455
3456
3457
3458
#ifdef ENABLE_MULTIBUFFER
    /* If we're using multibuffers and more than one file is specified
       on the command line, load them all and switch to the first one
       afterward */
3459
3460
3461
    if (optind + 1 < argc) {
	int old_multibuffer = ISSET(MULTIBUFFER);
	SET(MULTIBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
3462
3463
3464
3465
3466
3467
3468
3469
	for (optind++; optind < argc; optind++) {
	    add_open_file(1);
	    new_file();
	    filename = mallocstrcpy(filename, argv[optind]);
	    open_file(filename, 0, 0);
	    load_file(0);
	}
	open_nextfile_void();
3470
3471
	if (!old_multibuffer)
	    UNSET(MULTIBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
3472
3473
    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3474

Chris Allegretta's avatar
Chris Allegretta committed
3475
3476
    titlebar(NULL);

Chris Allegretta's avatar
Chris Allegretta committed
3477
    if (startline > 0)
3478
	do_gotoline(startline, 0);
Chris Allegretta's avatar
Chris Allegretta committed
3479

3480
3481
#ifndef NANO_SMALL
    /* Return here after a SIGWINCH. */
3482
    sigsetjmp(jmpbuf, 1);
3483
#endif
3484

3485
3486
3487
3488
3489
    /* SHUT UP GCC! */
    startline = 0;
    fill_flag_used = 0;
    keyhandled = 0;

Chris Allegretta's avatar
Chris Allegretta committed
3490
3491
    /* This variable should be initialized after the sigsetjmp(), so we
       can't do Esc-Esc then quickly resize and muck things up. */
3492
3493
    modify_control_seq = 0;

Robert Siemborski's avatar
Robert Siemborski committed
3494
3495
3496
    edit_refresh();
    reset_cursor();

3497
    while (TRUE) {
3498
	keyhandled = 0;
3499

3500
3501
3502
	if (ISSET(CONSTUPDATE))
	    do_cursorpos(1);

3503
#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
3504
	currshortcut = main_list;
3505
#endif
3506

3507
	kbinput = get_kbinput(edit, &meta_key);
3508
#ifdef DEBUG
3509
	fprintf(stderr, "AHA!  %c (%d)\n", kbinput, kbinput);
3510
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3511
	if (meta_key == TRUE) {
3512
	    /* Check for the metaval and miscval defs... */
3513
	    for (s = main_list; s != NULL; s = s->next)
3514
		if ((s->metaval != NANO_NO_KEY && kbinput == s->metaval) ||
3515
		    (s->miscval != NANO_NO_KEY && kbinput == s->miscval)) {
3516
3517
3518
3519
3520
3521
		    if (ISSET(VIEW_MODE) && !s->viewok)
			print_view_warning();
		    else {
			if (s->func != do_cut_text)
			    UNSET(KEEP_CUTBUFFER);
			s->func();
Chris Allegretta's avatar
Chris Allegretta committed
3522
		    }
3523
3524
		    keyhandled = 1;
		}
3525
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
3526
3527
3528
		if (!keyhandled)
		    /* And for toggle switches */
		    for (t = toggles; t != NULL; t = t->next)
3529
			if (kbinput == t->val) {
Chris Allegretta's avatar
Chris Allegretta committed
3530
3531
3532
3533
			    UNSET(KEEP_CUTBUFFER);
			    do_toggle(t);
			    keyhandled = 1;
		        }
3534
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3535
#ifdef DEBUG
3536
3537
	    fprintf(stderr, "I got Alt-%c! (%d)\n", kbinput,
		kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
3538
3539
#endif
	}
3540

Chris Allegretta's avatar
Chris Allegretta committed
3541
	/* Look through the main shortcut list to see if we've hit a
3542
	   shortcut key or function key */
3543

Chris Allegretta's avatar
Chris Allegretta committed
3544
	if (!keyhandled)
3545
#if !defined(DISABLE_BROWSER) || !defined (DISABLE_HELP) || !defined(DISABLE_MOUSE)
Chris Allegretta's avatar
Chris Allegretta committed
3546
	    for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
3547
#else
Chris Allegretta's avatar
Chris Allegretta committed
3548
	    for (s = main_list; s != NULL && !keyhandled; s = s->next) {
3549
#endif
3550
3551
		if ((s->ctrlval != NANO_NO_KEY && kbinput == s->ctrlval) ||
		    (s->funcval != NANO_NO_KEY && kbinput == s->funcval)) {
Chris Allegretta's avatar
Chris Allegretta committed
3552
3553
3554
3555
3556
3557
3558
3559
		    if (ISSET(VIEW_MODE) && !s->viewok)
			print_view_warning();
		    else {
			if (s->func != do_cut_text)
			    UNSET(KEEP_CUTBUFFER);
			s->func();
		    }
		    keyhandled = 1;
3560
3561
		    /* Break out explicitly once we successfully handle
		       a shortcut */
Chris Allegretta's avatar
Chris Allegretta committed
3562
3563
		    break;
		}
Chris Allegretta's avatar
Chris Allegretta committed
3564
	    }
Chris Allegretta's avatar
Chris Allegretta committed
3565
3566
3567

	if (!keyhandled)
	    UNSET(KEEP_CUTBUFFER);
3568
3569

	/* Don't even think about changing this string */
3570
	if (kbinput == NANO_CONTROL_Q)
3571
	    statusbar(_("XON ignored, mumble mumble."));
3572
3573
	if (kbinput == NANO_CONTROL_S)
	    statusbar(_("XOFF ignored, mumble mumble."));
3574

3575
3576
	/* If we're in raw mode or using Alt-Alt-x, we have to catch
	   Control-S and Control-Q */
3577
	if (kbinput == NANO_CONTROL_Q || kbinput == NANO_CONTROL_S)
3578
3579
	    keyhandled = 1;

3580
3581
	/* Catch ^Z by hand when triggered also */
	if (kbinput == NANO_SUSPEND_KEY) {
3582
3583
3584
3585
3586
	    if (ISSET(SUSPEND))
		do_suspend(0);
	    keyhandled = 1;
	}

Chris Allegretta's avatar
Chris Allegretta committed
3587
3588
3589
	/* Last gasp, stuff that's not in the main lists */
	if (!keyhandled)
	    switch (kbinput) {
3590
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
3591
3592
3593
3594
	    case KEY_MOUSE:
		do_mouse();
		break;
#endif
3595

3596
3597
3598
3599
	    case NANO_CONTROL_3:	/* Ctrl-[ (Esc), which should
	    				 * have been handled before we
	    				 * got here */
	    case NANO_CONTROL_5:	/* Ctrl-] */
Chris Allegretta's avatar
Chris Allegretta committed
3600
3601
3602
		break;
	    default:
#ifdef DEBUG
3603
		fprintf(stderr, "I got %c (%d)!\n", kbinput, kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
3604
3605
3606
3607
#endif
		/* We no longer stop unhandled sequences so that people with
		   odd character sets can type... */

Chris Allegretta's avatar
Chris Allegretta committed
3608
		if (ISSET(VIEW_MODE))
Chris Allegretta's avatar
Chris Allegretta committed
3609
		    print_view_warning();
Chris Allegretta's avatar
Chris Allegretta committed
3610
3611
		else
		    do_char(kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
3612
	    }
Chris Allegretta's avatar
Chris Allegretta committed
3613

Chris Allegretta's avatar
Chris Allegretta committed
3614
3615
3616
	reset_cursor();
	wrefresh(edit);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3617
    assert(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
3618
}