nano.c 71.3 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
2
3
/**************************************************************************
 *   nano.c                                                               *
 *                                                                        *
4
 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,  *
5
 *   2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.    *
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 3, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
 *   any later version.                                                   *
 *                                                                        *
11
12
13
14
 *   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.                             *
Chris Allegretta's avatar
Chris Allegretta committed
15
16
17
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
18
19
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
20
21
22
 *                                                                        *
 **************************************************************************/

23
#include "proto.h"
24

Chris Allegretta's avatar
Chris Allegretta committed
25
26
27
28
29
30
31
32
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <locale.h>
33
#ifdef ENABLE_UTF8
34
#include <langinfo.h>
35
#endif
36
#include <termios.h>
Chris Allegretta's avatar
Chris Allegretta committed
37
38
39
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
40
41
42
43
#ifndef NANO_TINY
#include <sys/ioctl.h>
#endif

44
45
46
47
#ifndef DISABLE_MOUSE
static int oldinterval = -1;
	/* Used to store the user's original mouse click interval. */
#endif
48
#ifndef DISABLE_NANORC
49
50
51
static bool no_rcfiles = FALSE;
	/* Should we ignore all rcfiles? */
#endif
52
53
54
static struct termios oldterm;
	/* The user's original terminal settings. */
static struct sigaction act;
55
	/* Used to set up all our fun signal handlers. */
Chris Allegretta's avatar
Chris Allegretta committed
56

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
57
58
/* Create a new filestruct node.  Note that we do not set prevnode->next
 * to the new line. */
59
filestruct *make_new_node(filestruct *prevnode)
60
{
61
62
63
64
65
66
67
    filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));

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

68
#ifndef DISABLE_COLOR
69
    newnode->multidata = NULL;
70
71
#endif

72
    return newnode;
73
74
}

75
76
/* Make a copy of a filestruct node. */
filestruct *copy_node(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
77
{
78
    filestruct *dst;
Chris Allegretta's avatar
Chris Allegretta committed
79

80
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
81

82
    dst = (filestruct *)nmalloc(sizeof(filestruct));
83

84
85
86
87
    dst->data = mallocstrcpy(NULL, src->data);
    dst->next = src->next;
    dst->prev = src->prev;
    dst->lineno = src->lineno;
88
#ifndef DISABLE_COLOR
89
    dst->multidata = NULL;
90
#endif
91

92
    return dst;
Chris Allegretta's avatar
Chris Allegretta committed
93
94
}

95
96
/* Splice a new node into an existing linked list of filestructs. */
void splice_node(filestruct *afterthis, filestruct *newnode)
Chris Allegretta's avatar
Chris Allegretta committed
97
{
98
    assert(afterthis != NULL && newnode != NULL);
99

100
101
102
103
104
    newnode->next = afterthis->next;
    newnode->prev = afterthis;
    if (afterthis->next != NULL)
	afterthis->next->prev = newnode;
    afterthis->next = newnode;
105
106
107
108

    /* Update filebot when inserting a node at the end of file. */
    if (openfile && openfile->filebot == afterthis)
	openfile->filebot = newnode;
109
}
110

111
/* Disconnect a node from a linked list of filestructs and delete it. */
112
void unlink_node(filestruct *fileptr)
113
114
{
    assert(fileptr != NULL);
115

116
117
118
119
    if (fileptr->prev != NULL)
	fileptr->prev->next = fileptr->next;
    if (fileptr->next != NULL)
	fileptr->next->prev = fileptr->prev;
120

121
122
123
124
    /* Update filebot when removing a node at the end of file. */
    if (openfile && openfile->filebot == fileptr)
	openfile->filebot = fileptr->prev;

125
    delete_node(fileptr);
126
}
127

128
/* Free the data structures in the given node. */
129
130
131
void delete_node(filestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->data != NULL);
132

133
    free(fileptr->data);
134
#ifndef DISABLE_COLOR
135
    free(fileptr->multidata);
136
#endif
137
    free(fileptr);
138
139
}

140
141
/* Duplicate a whole filestruct. */
filestruct *copy_filestruct(const filestruct *src)
142
{
143
    filestruct *head, *copy;
144

145
    assert(src != NULL);
146

147
148
149
150
    copy = copy_node(src);
    copy->prev = NULL;
    head = copy;
    src = src->next;
151

152
153
154
155
    while (src != NULL) {
	copy->next = copy_node(src);
	copy->next->prev = copy;
	copy = copy->next;
Chris Allegretta's avatar
Chris Allegretta committed
156

157
158
	src = src->next;
    }
159

160
    copy->next = NULL;
161

162
    return head;
Chris Allegretta's avatar
Chris Allegretta committed
163
164
}

165
/* Free a whole linked list of filestructs. */
166
void free_filestruct(filestruct *src)
167
{
168
169
    if (src == NULL)
	return;
170
171
172
173
174

    while (src->next != NULL) {
	src = src->next;
	delete_node(src->prev);
    }
175

176
    delete_node(src);
177
178
}

179
/* Renumber all entries in a filestruct, starting with fileptr. */
180
void renumber(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
181
{
182
    ssize_t line;
183

184
185
    if (fileptr == NULL)
	return;
Chris Allegretta's avatar
Chris Allegretta committed
186

187
    line = (fileptr->prev == NULL) ? 0 : fileptr->prev->lineno;
188

189
    assert(fileptr != fileptr->next);
190

191
    for (; fileptr != NULL; fileptr = fileptr->next)
192
	fileptr->lineno = ++line;
Chris Allegretta's avatar
Chris Allegretta committed
193
194
}

195
196
/* Partition a filestruct so that it begins at (top, top_x) and ends at
 * (bot, bot_x). */
197
198
partition *partition_filestruct(filestruct *top, size_t top_x,
	filestruct *bot, size_t bot_x)
199
{
200
    partition *p;
201

202
    assert(top != NULL && bot != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
203

204
205
    /* Initialize the partition. */
    p = (partition *)nmalloc(sizeof(partition));
206

207
208
209
    /* If the top and bottom of the partition are different from the top
     * and bottom of the filestruct, save the latter and then set them
     * to top and bot. */
210
211
212
    if (top != openfile->fileage) {
	p->fileage = openfile->fileage;
	openfile->fileage = top;
213
    } else
214
	p->fileage = NULL;
215
216
217
    if (bot != openfile->filebot) {
	p->filebot = openfile->filebot;
	openfile->filebot = bot;
218
219
    } else
	p->filebot = NULL;
220

221
222
223
224
225
226
227
    /* Save the line above the top of the partition, detach the top of
     * the partition from it, and save the text before top_x in
     * top_data. */
    p->top_prev = top->prev;
    top->prev = NULL;
    p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
    p->top_data[top_x] = '\0';
228

229
230
231
232
233
234
    /* Save the line below the bottom of the partition, detach the
     * bottom of the partition from it, and save the text after bot_x in
     * bot_data. */
    p->bot_next = bot->next;
    bot->next = NULL;
    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
235

236
237
    /* Remove all text after bot_x at the bottom of the partition. */
    null_at(&bot->data, bot_x);
238

239
    /* Remove all text before top_x at the top of the partition. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
240
241
    charmove(top->data, top->data + top_x, strlen(top->data) -
	top_x + 1);
242
    align(&top->data);
243

244
245
246
    /* Return the partition. */
    return p;
}
247

248
249
/* Unpartition a filestruct so that it begins at (fileage, 0) and ends
 * at (filebot, strlen(filebot->data)) again. */
250
251
252
void unpartition_filestruct(partition **p)
{
    char *tmp;
253

254
    assert(p != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
255

256
257
258
    /* Reattach the line above the top of the partition, and restore the
     * text before top_x from top_data.  Free top_data when we're done
     * with it. */
259
260
261
262
263
264
265
    tmp = mallocstrcpy(NULL, openfile->fileage->data);
    openfile->fileage->prev = (*p)->top_prev;
    if (openfile->fileage->prev != NULL)
	openfile->fileage->prev->next = openfile->fileage;
    openfile->fileage->data = charealloc(openfile->fileage->data,
	strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
    strcpy(openfile->fileage->data, (*p)->top_data);
266
    free((*p)->top_data);
267
    strcat(openfile->fileage->data, tmp);
268
    free(tmp);
269

270
271
272
    /* Reattach the line below the bottom of the partition, and restore
     * the text after bot_x from bot_data.  Free bot_data when we're
     * done with it. */
273
274
275
276
277
278
    openfile->filebot->next = (*p)->bot_next;
    if (openfile->filebot->next != NULL)
	openfile->filebot->next->prev = openfile->filebot;
    openfile->filebot->data = charealloc(openfile->filebot->data,
	strlen(openfile->filebot->data) + strlen((*p)->bot_data) + 1);
    strcat(openfile->filebot->data, (*p)->bot_data);
279
    free((*p)->bot_data);
280

281
282
283
    /* Restore the top and bottom of the filestruct, if they were
     * different from the top and bottom of the partition. */
    if ((*p)->fileage != NULL)
284
	openfile->fileage = (*p)->fileage;
285
    if ((*p)->filebot != NULL)
286
	openfile->filebot = (*p)->filebot;
287

288
289
290
291
    /* Uninitialize the partition. */
    free(*p);
    *p = NULL;
}
292

293
294
295
296
297
298
299
300
/* Move all the text between (top, top_x) and (bot, bot_x) in the
 * current filestruct to a filestruct beginning with file_top and ending
 * with file_bot.  If no text is between (top, top_x) and (bot, bot_x),
 * don't do anything. */
void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
	filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
{
    filestruct *top_save;
301
    bool edittop_inside;
302
#ifndef NANO_TINY
303
    bool mark_inside = FALSE;
304
    bool same_line = FALSE;
305
#endif
306

307
    assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
308

309
310
311
    /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
    if (top == bot && top_x == bot_x)
	return;
312

313
314
    /* Partition the filestruct so that it contains only the text from
     * (top, top_x) to (bot, bot_x), keep track of whether the top of
315
     * the edit window is inside the partition, and keep track of
316
317
     * whether the mark begins inside the partition. */
    filepart = partition_filestruct(top, top_x, bot, bot_x);
318
319
320
    edittop_inside = (openfile->edittop->lineno >=
	openfile->fileage->lineno && openfile->edittop->lineno <=
	openfile->filebot->lineno);
321
#ifndef NANO_TINY
322
    if (openfile->mark_set) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
323
	mark_inside = (openfile->mark_begin->lineno >=
324
		openfile->fileage->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
325
		openfile->mark_begin->lineno <=
326
		openfile->filebot->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
327
328
329
330
		(openfile->mark_begin != openfile->fileage ||
		openfile->mark_begin_x >= top_x) &&
		(openfile->mark_begin != openfile->filebot ||
		openfile->mark_begin_x <= bot_x));
331
332
	same_line = (openfile->mark_begin == openfile->fileage);
    }
333
#endif
334

335
336
    /* Get the number of characters in the text, and subtract it from
     * totsize. */
337
    openfile->totsize -= get_totsize(top, bot);
338

339
340
341
342
    if (*file_top == NULL) {
	/* If file_top is empty, just move all the text directly into
	 * it.  This is equivalent to tacking the text in top onto the
	 * (lack of) text at the end of file_top. */
343
344
	*file_top = openfile->fileage;
	*file_bot = openfile->filebot;
345
346
347

	/* Renumber starting with file_top. */
	renumber(*file_top);
348
    } else {
349
350
	filestruct *file_bot_save = *file_bot;

351
352
353
	/* Otherwise, tack the text in top onto the text at the end of
	 * file_bot. */
	(*file_bot)->data = charealloc((*file_bot)->data,
354
355
356
		strlen((*file_bot)->data) +
		strlen(openfile->fileage->data) + 1);
	strcat((*file_bot)->data, openfile->fileage->data);
357

358
359
360
	/* Attach the line after top to the line after file_bot.  Then,
	 * if there's more than one line after top, move file_bot down
	 * to bot. */
361
	(*file_bot)->next = openfile->fileage->next;
362
363
	if ((*file_bot)->next != NULL) {
	    (*file_bot)->next->prev = *file_bot;
364
	    *file_bot = openfile->filebot;
365
	}
366

367
	delete_node(openfile->fileage);
368

369
370
	/* Renumber starting with the line after the original
	 * file_bot. */
371
	renumber(file_bot_save->next);
372
    }
373

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
374
375
    /* Since the text has now been saved, remove it from the
     * filestruct. */
376
377
378
    openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct));
    openfile->fileage->data = mallocstrcpy(NULL, "");
    openfile->filebot = openfile->fileage;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
379

380
#ifndef DISABLE_COLOR
381
    openfile->fileage->multidata = NULL;
382
383
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
384
385
386
387
388
    /* Restore the current line and cursor position.  If the mark begins
     * inside the partition, set the beginning of the mark to where the
     * saved text used to start. */
    openfile->current = openfile->fileage;
    openfile->current_x = top_x;
389
#ifndef NANO_TINY
390
    if (mark_inside) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
391
392
	openfile->mark_begin = openfile->current;
	openfile->mark_begin_x = openfile->current_x;
393
    } else if (same_line)
394
	/* Update the pointer to this partially cut line. */
395
	openfile->mark_begin = openfile->current;
396
#endif
397

398
    top_save = openfile->fileage;
399

400
401
402
    /* Unpartition the filestruct so that it contains all the text
     * again, minus the saved text. */
    unpartition_filestruct(&filepart);
403

404
405
    /* If the top of the edit window was inside the old partition, put
     * it in range of current. */
406
    if (edittop_inside) {
407
	edit_update(STATIONARY);
408
	refresh_needed = TRUE;
409
    }
410

411
412
413
    /* Renumber starting with the beginning line of the old
     * partition. */
    renumber(top_save);
414

415
416
417
    /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
     * a magicline, add a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
418
419
	new_magicline();
}
420

421
422
423
/* Copy all text from the given filestruct to the current filestruct
 * at the current cursor position. */
void copy_from_filestruct(filestruct *somebuffer)
424
425
{
    filestruct *top_save;
426
    size_t current_x_save = openfile->current_x;
427
    bool edittop_inside;
428
#ifndef NANO_TINY
429
    bool right_side_up = FALSE, single_line = FALSE;
430
#endif
431

432
    assert(somebuffer != NULL);
433

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
#ifndef NANO_TINY
    /* Keep track of whether the mark begins inside the partition and
     * will need adjustment. */
    if (openfile->mark_set) {
	filestruct *top, *bot;
	size_t top_x, bot_x;

	mark_order((const filestruct **)&top, &top_x,
		(const filestruct **)&bot, &bot_x, &right_side_up);

	single_line = (top == bot);
    }
#endif

    /* Partition the filestruct so that it contains no text, and keep
     * track of whether the top of the edit window is inside the
     * partition. */
451
452
453
    filepart = partition_filestruct(openfile->current,
	openfile->current_x, openfile->current, openfile->current_x);
    edittop_inside = (openfile->edittop == openfile->fileage);
454

455
456
    /* Put the top and bottom of the current filestruct at the top and
     * bottom of a copy of the passed buffer. */
457
    free_filestruct(openfile->fileage);
458
    openfile->fileage = copy_filestruct(somebuffer);
459
460
461
    openfile->filebot = openfile->fileage;
    while (openfile->filebot->next != NULL)
	openfile->filebot = openfile->filebot->next;
462

463
    /* Put the cursor at the end of the pasted text. */
464
465
    openfile->current = openfile->filebot;
    openfile->current_x = strlen(openfile->filebot->data);
466
467
468

    /* Refresh the mark's pointer, and compensate the mark's
     * x coordinate for the change in the current line. */
469
470
    if (openfile->fileage == openfile->filebot) {
#ifndef NANO_TINY
471
	if (openfile->mark_set && single_line) {
472
	    openfile->mark_begin = openfile->current;
473
	    if (!right_side_up)
474
475
476
		openfile->mark_begin_x += openfile->current_x;
	}
#endif
477
478
	/* When the pasted stuff contains no newline, adjust the cursor's
	 * x coordinate for the text that is before the pasted stuff. */
479
	openfile->current_x += current_x_save;
480
    }
481
#ifndef NANO_TINY
482
483
484
485
486
487
    else if (openfile->mark_set && single_line) {
	if (right_side_up)
	    openfile->mark_begin = openfile->fileage;
	else {
	    openfile->mark_begin = openfile->current;
	    openfile->mark_begin_x += openfile->current_x - current_x_save;
488
489
	}
    }
490
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
491

492
    /* Get the number of characters in the copied text, and add it to
493
494
495
     * totsize. */
    openfile->totsize += get_totsize(openfile->fileage,
	openfile->filebot);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
496

497
498
499
    /* Update the current y-coordinate to account for the number of
     * lines the copied text has, less one since the first line will be
     * tacked onto the current line. */
500
    openfile->current_y += openfile->filebot->lineno - 1;
501

502
    top_save = openfile->fileage;
503

504
505
506
507
508
    /* If the top of the edit window is inside the partition, set it to
     * where the copied text now starts. */
    if (edittop_inside)
	openfile->edittop = openfile->fileage;

509
    /* Unpartition the filestruct so that it contains all the text
510
     * again, plus the copied text. */
511
    unpartition_filestruct(&filepart);
512

513
514
515
    /* Renumber starting with the beginning line of the old
     * partition. */
    renumber(top_save);
516

517
518
519
    /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
     * a magicline, add a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
520
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
521
522
}

523
524
525
/* Create a new openfilestruct node. */
openfilestruct *make_new_opennode(void)
{
526
    return (openfilestruct *)nmalloc(sizeof(openfilestruct));
527
528
529
530
531
}

/* Unlink a node from the rest of the openfilestruct, and delete it. */
void unlink_opennode(openfilestruct *fileptr)
{
532
533
    assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL &&
		 fileptr != fileptr->prev && fileptr != fileptr->next);
534
535
536
537
538
539
540

    fileptr->prev->next = fileptr->next;
    fileptr->next->prev = fileptr->prev;

    delete_opennode(fileptr);
}

541
/* Free all the memory in the given open-file node. */
542
543
544
545
546
547
void delete_opennode(openfilestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);

    free(fileptr->filename);
    free_filestruct(fileptr->fileage);
548
#ifndef NANO_TINY
549
    free(fileptr->current_stat);
550
    free(fileptr->lock_filename);
551
552
    /* Free the undo stack. */
    discard_until(NULL, fileptr);
553
554
555
556
#endif
    free(fileptr);
}

557
/* Display a warning about a key disabled in view mode. */
558
void print_view_warning(void)
559
{
560
    statusbar(_("Key is invalid in view mode"));
561
562
}

563
564
565
566
567
568
569
570
571
572
573
/* Indicate that something is disabled in restricted mode. */
void show_restricted_warning(void)
{
    statusbar(_("This function is disabled in restricted mode"));
    beep();
}

#ifdef DISABLE_HELP
/* Indicate that help texts are unavailable. */
void say_there_is_no_help(void)
{
574
    statusbar(_("Help is not available"));
575
576
577
}
#endif

578
/* Make nano exit gracefully. */
579
void finish(void)
Chris Allegretta's avatar
Chris Allegretta committed
580
{
581
582
583
584
    /* Blank the statusbar and (if applicable) the shortcut list,
     * and move the cursor to the last line of the screen. */
    blank_statusbar();
    blank_bottombars();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
585
    wrefresh(bottomwin);
586
    endwin();
587

588
589
    /* Restore the old terminal settings. */
    tcsetattr(0, TCSANOW, &oldterm);
590

591
#ifndef DISABLE_HISTORIES
592
    if (ISSET(HISTORYLOG))
593
	save_history();
594
    if (ISSET(POS_HISTORY)) {
595
	update_poshistory(openfile->filename, openfile->current->lineno, xplustabs() + 1);
596
	save_poshistory();
597
    }
598
599
600
601
602
603
#endif

#ifdef DEBUG
    thanks_for_all_the_fish();
#endif

604
    /* Get out. */
605
    exit(0);
606
607
}

608
/* Make nano die gracefully. */
609
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
610
{
611
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
612

613
    endwin();
Chris Allegretta's avatar
Chris Allegretta committed
614

615
616
    /* Restore the old terminal settings. */
    tcsetattr(0, TCSANOW, &oldterm);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
617

618
619
620
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
621

622
    /* Save the current file buffer if it's been modified. */
623
    if (openfile && openfile->modified) {
624
625
626
627
	/* If we've partitioned the filestruct, unpartition it now. */
	if (filepart != NULL)
	    unpartition_filestruct(&filepart);

628
629
630
631
632
	die_save_file(openfile->filename
#ifndef NANO_TINY
		, openfile->current_stat
#endif
		);
Chris Allegretta's avatar
Chris Allegretta committed
633
634
    }

635
#ifndef DISABLE_MULTIBUFFER
636
    /* Save all of the other modified file buffers, if any. */
637
638
    if (openfile != NULL) {
	openfilestruct *tmp = openfile;
Chris Allegretta's avatar
Chris Allegretta committed
639

640
641
	while (tmp != openfile->next) {
	    openfile = openfile->next;
642

643
	    /* Save the current file buffer if it's been modified. */
644
	    if (openfile->modified)
645
646
647
648
649
		die_save_file(openfile->filename
#ifndef NANO_TINY
			, openfile->current_stat
#endif
			);
650
	}
651
    }
652
653
654
655
#endif

    /* Get out. */
    exit(1);
656
657
}

658
659
/* Save the current file under the name spacified in die_filename, which
 * is modified to be unique if necessary. */
660
661
662
663
664
void die_save_file(const char *die_filename
#ifndef NANO_TINY
	, struct stat *die_stat
#endif
	)
665
{
666
667
    char *retval;
    bool failed = TRUE;
668

669
670
671
672
673
    /* If we're using restricted mode, don't write any emergency backup
     * files, since that would allow reading from or writing to files
     * not specified on the command line. */
    if (ISSET(RESTRICTED))
	return;
674

675
676
    /* If we can't save, we have really bad problems, but we might as
     * well try. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
677
    if (*die_filename == '\0')
678
	die_filename = "nano";
679

680
681
    retval = get_next_filename(die_filename, ".save");
    if (retval[0] != '\0')
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
682
	failed = !write_file(retval, NULL, TRUE, OVERWRITE, TRUE);
683

684
685
686
687
688
689
690
691
    if (!failed)
	fprintf(stderr, _("\nBuffer written to %s\n"), retval);
    else if (retval[0] != '\0')
	fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
		strerror(errno));
    else
	fprintf(stderr, _("\nBuffer not written: %s\n"),
		_("Too many backup files?"));
692

693
#ifndef NANO_TINY
694
695
696
    /* Try and chmod/chown the save file to the values of the original file,
     * but don't worry if it fails because we're supposed to be bailing as
     * fast as possible. */
697
698
699
700
    if (die_stat) {
	int shush;
	shush = chmod(retval, die_stat->st_mode);
	shush = chown(retval, die_stat->st_uid, die_stat->st_gid);
701
702
	if (shush)
	    ;
703
    }
704
#endif
705

706
707
    free(retval);
}
708

709
/* Initialize the three window portions nano uses. */
710
void window_init(void)
711
{
712
    /* If the screen height is too small, get out. */
713
    editwinrows = LINES - 5 + more_space() + no_help();
714
    if (COLS < MIN_EDITOR_COLS || editwinrows < MIN_EDITOR_ROWS)
715
	die(_("Window size is too small for nano...\n"));
716

717
#ifndef DISABLE_WRAPJUSTIFY
718
    /* Set up fill, based on the screen width. */
719
720
721
722
723
724
    fill = wrap_at;
    if (fill <= 0)
	fill += COLS;
    if (fill < 0)
	fill = 0;
#endif
725

726
727
728
729
730
731
    if (topwin != NULL)
	delwin(topwin);
    if (edit != NULL)
	delwin(edit);
    if (bottomwin != NULL)
	delwin(bottomwin);
732

733
    /* Set up the windows. */
734
735
    topwin = newwin(2 - more_space(), COLS, 0, 0);
    edit = newwin(editwinrows, COLS, 2 - more_space(), 0);
736
    bottomwin = newwin(3 - no_help(), COLS, editwinrows + (2 -
737
	more_space()), 0);
738

739
    /* Turn the keypad on for the windows, if necessary. */
740
    if (!ISSET(REBIND_KEYPAD)) {
741
	keypad(topwin, TRUE);
742
743
744
	keypad(edit, TRUE);
	keypad(bottomwin, TRUE);
    }
745
746
}

747
#ifndef DISABLE_MOUSE
748
749
750
751
/* Disable mouse support. */
void disable_mouse_support(void)
{
    mousemask(0, NULL);
752
    mouseinterval(oldinterval);
753
754
755
756
757
}

/* Enable mouse support. */
void enable_mouse_support(void)
{
758
    mousemask(ALL_MOUSE_EVENTS, NULL);
759
    oldinterval = mouseinterval(50);
760
761
762
763
}

/* Initialize mouse support.  Enable it if the USE_MOUSE flag is set,
 * and disable it otherwise. */
764
void mouse_init(void)
765
{
766
767
768
769
    if (ISSET(USE_MOUSE))
	enable_mouse_support();
    else
	disable_mouse_support();
770
}
771
#endif /* !DISABLE_MOUSE */
772

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
773
#ifdef HAVE_GETOPT_LONG
774
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, longflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
775
#else
776
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
777
778
#endif

779
/* Print one usage string to the screen.  This cuts down on duplicate
780
 * strings to translate, and leaves out the parts that shouldn't be
781
 * translatable (i.e. the flag names). */
782
void print_opt_full(const char *shortflag
783
784
785
786
#ifdef HAVE_GETOPT_LONG
	, const char *longflag
#endif
	, const char *desc)
787
788
{
    printf(" %s\t", shortflag);
789
    if (strlenpt(shortflag) < 8)
790
791
792
793
	printf("\t");

#ifdef HAVE_GETOPT_LONG
    printf("%s\t", longflag);
794
    if (strlenpt(longflag) < 8)
795
	printf("\t\t");
796
    else if (strlenpt(longflag) < 16)
797
798
799
	printf("\t");
#endif

800
801
802
    if (desc != NULL)
	printf("%s", _(desc));
    printf("\n");
803
804
}

805
/* Explain how to properly use nano and its command-line options. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
806
void usage(void)
Chris Allegretta's avatar
Chris Allegretta committed
807
{
808
    printf(_("Usage: nano [OPTIONS] [[+LINE,COLUMN] FILE]...\n\n"));
809
    printf(
810
811
#ifdef HAVE_GETOPT_LONG
	_("Option\t\tGNU long option\t\tMeaning\n")
Chris Allegretta's avatar
Chris Allegretta committed
812
#else
813
	_("Option\t\tMeaning\n")
814
#endif
815
	);
816
    print_opt(_("+LINE,COLUMN"), "",
817
818
	/* TRANSLATORS: The next forty or so strings are option descriptions
	 * for the --help output.  Try to keep them at most 40 characters. */
819
	N_("Start at line LINE, column COLUMN"));
820
#ifndef NANO_TINY
821
    print_opt("-A", "--smarthome", N_("Enable smart home key"));
822
    if (!ISSET(RESTRICTED)) {
823
824
825
	print_opt("-B", "--backup", N_("Save backups of existing files"));
	print_opt(_("-C <dir>"), _("--backupdir=<dir>"),
		N_("Directory for saving unique backup files"));
826
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
827
#endif
828
    print_opt("-D", "--boldtext", N_("Use bold instead of reverse video text"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
829
#ifndef NANO_TINY
830
    print_opt("-E", "--tabstospaces", N_("Convert typed tabs to spaces"));
831
#endif
832
#ifndef DISABLE_MULTIBUFFER
833
    if (!ISSET(RESTRICTED))
834
835
	print_opt("-F", "--multibuffer",
		N_("Read a file into a new buffer by default"));
Chris Allegretta's avatar
Chris Allegretta committed
836
#endif
837
#ifndef NANO_TINY
838
    print_opt("-G", "--locking", N_("Use (vim-style) lock files"));
839
840
#endif
#ifndef DISABLE_HISTORIES
841
    if (!ISSET(RESTRICTED))
842
843
	print_opt("-H", "--historylog",
		N_("Log & read search/replace string history"));
844
845
#endif
#ifndef DISABLE_NANORC
846
    if (!ISSET(RESTRICTED))
847
	print_opt("-I", "--ignorercfiles", N_("Don't look at nanorc files"));
Chris Allegretta's avatar
Chris Allegretta committed
848
#endif
849
    print_opt("-K", "--rebindkeypad",
850
	N_("Fix numeric keypad key confusion problem"));
851
    print_opt("-L", "--nonewlines",
852
	N_("Don't add newlines to the ends of files"));
853
#ifndef NANO_TINY
854
    print_opt("-N", "--noconvert",
855
	N_("Don't convert files from DOS/Mac format"));
856
#endif
857
    print_opt("-O", "--morespace", N_("Use one more line for editing"));
858
#ifndef DISABLE_HISTORIES
859
    if (!ISSET(RESTRICTED))
860
861
	print_opt("-P", "--positionlog",
		N_("Log & read location of cursor position"));
862
#endif
863
#ifndef DISABLE_JUSTIFY
864
    print_opt(_("-Q <str>"), _("--quotestr=<str>"), N_("Quoting string"));
865
#endif
866
    if (!ISSET(RESTRICTED))
867
	print_opt("-R", "--restricted", N_("Restricted mode"));
868
#ifndef NANO_TINY
869
    print_opt("-S", "--smooth", N_("Scroll by line instead of half-screen"));
870
#endif
871
    print_opt(_("-T <#cols>"), _("--tabsize=<#cols>"),
872
	N_("Set width of a tab to #cols columns"));
873
#ifndef NANO_TINY
874
    print_opt("-U", "--quickblank", N_("Do quick statusbar blanking"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
875
#endif
876
    print_opt("-V", "--version", N_("Print version information and exit"));
877
#ifndef NANO_TINY
878
    print_opt("-W", "--wordbounds",
879
880
	N_("Detect word boundaries more accurately"));
#endif
881
#ifndef DISABLE_COLOR
882
    if (!ISSET(RESTRICTED))
883
884
	print_opt(_("-Y <str>"), _("--syntax=<str>"),
		N_("Syntax definition to use for coloring"));
885
#endif
886
    print_opt("-c", "--constantshow", N_("Constantly show cursor position"));
887
    print_opt("-d", "--rebinddelete",
888
	N_("Fix Backspace/Delete confusion problem"));
889
    print_opt("-h", "--help", N_("Show this help text and exit"));
890
#ifndef NANO_TINY
891
    print_opt("-i", "--autoindent", N_("Automatically indent new lines"));
892
    print_opt("-k", "--cut", N_("Cut from cursor to end of line"));
893
#endif
894
#ifndef DISABLE_MOUSE
895
    print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
Chris Allegretta's avatar
Chris Allegretta committed
896
#endif
897
    print_opt("-n", "--noread", N_("Do not read the file (only write it)"));
898
#ifndef DISABLE_OPERATINGDIR
899
    print_opt(_("-o <dir>"), _("--operatingdir=<dir>"),
900
	N_("Set operating directory"));
Chris Allegretta's avatar
Chris Allegretta committed
901
#endif
902
    print_opt("-p", "--preserve", N_("Preserve XON (^Q) and XOFF (^S) keys"));
903
#ifndef DISABLE_NANORC
904
    if (!ISSET(RESTRICTED))
905
906
	print_opt("-q", "--quiet",
		N_("Silently ignore startup issues like rc file errors"));
907
#endif
908
#ifndef DISABLE_WRAPJUSTIFY
909
    print_opt(_("-r <#cols>"), _("--fill=<#cols>"),
910
	N_("Set hard-wrapping point at column #cols"));
911
#endif
912
#ifndef DISABLE_SPELLER
913
    if (!ISSET(RESTRICTED))
914
915
	print_opt(_("-s <prog>"), _("--speller=<prog>"),
		N_("Enable alternate speller"));
916
#endif
917
    print_opt("-t", "--tempfile", N_("Auto save on exit, don't prompt"));
918
919
920
#ifndef NANO_TINY
    print_opt("-u", "--unix", N_("Save a file by default in Unix format"));
#endif
921
    print_opt("-v", "--view", N_("View mode (read-only)"));
922
#ifndef DISABLE_WRAPPING
923
    print_opt("-w", "--nowrap", N_("Don't hard-wrap long lines"));
Chris Allegretta's avatar
Chris Allegretta committed
924
#endif
925
    print_opt("-x", "--nohelp", N_("Don't show the two help lines"));
926
    if (!ISSET(RESTRICTED))
927
	print_opt("-z", "--suspend", N_("Enable suspension"));
928
#ifndef NANO_TINY
929
    print_opt("-$", "--softwrap", N_("Enable soft line wrapping"));
930
#endif
Chris Allegretta's avatar
Chris Allegretta committed
931
932
}

933
934
935
/* Display the current version of nano, the date and time it was
 * compiled, contact information for it, and the configuration options
 * it was compiled with. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
936
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
937
{
938
939
940
#ifdef REVISION
    printf(" GNU nano from git, commit %s (after %s)\n", REVISION, VERSION);
#else
941
    printf(_(" GNU nano, version %s\n"), VERSION);
942
#endif
943
    printf(" (C) 1999..2016 Free Software Foundation, Inc.\n");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
944
945
    printf(
	_(" Email: nano@nano-editor.org	Web: http://www.nano-editor.org/"));
946
    printf(_("\n Compiled options:"));
947

948
949
950
951
952
953
954
955
956
957
958
959
960
961
#ifdef NANO_TINY
    printf(" --enable-tiny");
#ifndef DISABLE_BROWSER
    printf(" --enable-browser");
#endif
#ifndef DISABLE_COLOR
    printf(" --enable-color");
#endif
#ifndef DISABLE_EXTRA
    printf(" --enable-extra");
#endif
#ifndef DISABLE_HELP
    printf(" --enable-help");
#endif
962
963
964
#ifndef DISABLE_HISTORIES
    printf(" --enable-histories");
#endif
965
966
967
#ifndef DISABLE_JUSTIFY
    printf(" --enable-justify");
#endif
968
969
970
#ifdef HAVE_LIBMAGIC
    printf(" --enable-libmagic");
#endif
971
972
973
#ifndef DISABLE_MOUSE
    printf(" --enable-mouse");
#endif
974
975
976
#ifndef DISABLE_NANORC
    printf(" --enable-nanorc");
#endif
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
#ifndef DISABLE_MULTIBUFFER
    printf(" --enable-multibuffer");
#endif
#ifndef DISABLE_OPERATINGDIR
    printf(" --enable-operatingdir");
#endif
#ifndef DISABLE_SPELLER
    printf(" --enable-speller");
#endif
#ifndef DISABLE_TABCOMP
    printf(" --enable-tabcomp");
#endif
#ifndef DISABLE_WRAPPING
    printf(" --enable-wrapping");
#endif
#else /* !NANO_TINY */
993
#ifdef DISABLE_BROWSER
994
    printf(" --disable-browser");
995
#endif
996
997
998
#ifdef DISABLE_COLOR
    printf(" --disable-color");
#endif
999
1000
1001
#ifdef DISABLE_EXTRA
    printf(" --disable-extra");
#endif
1002
1003
#ifdef DISABLE_HELP
    printf(" --disable-help");
1004
#endif
1005
1006
1007
#ifdef DISABLE_HISTORIES
    printf(" --disable-histories");
#endif
1008
#ifdef DISABLE_JUSTIFY
1009
    printf(" --disable-justify");
1010
#endif
1011
1012
1013
#ifndef HAVE_LIBMAGIC
    printf(" --disable-libmagic");
#endif
1014
#ifdef DISABLE_MOUSE
1015
    printf(" --disable-mouse");
1016
#endif
1017
1018
1019
#ifdef DISABLE_MULTIBUFFER
    printf(" --disable-multibuffer");
#endif
1020
1021
1022
#ifdef DISABLE_NANORC
    printf(" --disable-nanorc");
#endif
1023
1024
1025
#ifdef DISABLE_OPERATINGDIR
    printf(" --disable-operatingdir");
#endif
1026
1027
1028
1029
1030
1031
1032
1033
1034
#ifdef DISABLE_SPELLER
    printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
    printf(" --disable-tabcomp");
#endif
#ifdef DISABLE_WRAPPING
    printf(" --disable-wrapping");
#endif
1035
1036
#endif /* !NANO_TINY */

1037
#ifdef DISABLE_ROOTWRAPPING
1038
1039
    printf(" --disable-wrapping-as-root");
#endif
1040
1041
1042
#ifdef DEBUG
    printf(" --enable-debug");
#endif
1043
1044
#ifndef ENABLE_NLS
    printf(" --disable-nls");
1045
#endif
1046
#ifdef ENABLE_UTF8
1047
    printf(" --enable-utf8");
1048
1049
#else
    printf(" --disable-utf8");
1050
1051
1052
#endif
#ifdef USE_SLANG
    printf(" --with-slang");
1053
1054
#endif
    printf("\n");
Chris Allegretta's avatar
Chris Allegretta committed
1055
1056
}

1057
/* Return 1 if the MORE_SPACE flag is set, and 0 otherwise.  This is
1058
1059
 * used to calculate the sizes and Y coordinates of the subwindows. */
int more_space(void)
1060
1061
1062
1063
{
    return ISSET(MORE_SPACE) ? 1 : 0;
}

1064
/* Return 2 if the NO_HELP flag is set, and 0 otherwise.  This is used
1065
1066
 * to calculate the sizes and Y coordinates of the subwindows, because
 * having NO_HELP adds two lines to the edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
1067
1068
int no_help(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1069
    return ISSET(NO_HELP) ? 2 : 0;
Chris Allegretta's avatar
Chris Allegretta committed
1070
1071
}

1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
/* Indicate that the current file has no name, in a way that gets the
 * user's attention.  This is used when trying to save a file with no
 * name with the TEMP_FILE flag set, just before the filename prompt. */
void no_current_file_name_warning(void)
{
    /* Warn that the current file has no name. */
    statusbar(_("No file name"));
    beep();

    /* Ensure that we see the warning. */
    napms(1800);

    curs_set(1);
}

1087
1088
/* If the current file buffer has been modified, and the TEMP_FILE flag
 * isn't set, ask whether or not to save the file buffer.  If the
1089
1090
1091
1092
 * TEMP_FILE flag is set and the current file has a name, save it
 * unconditionally.  Then, if more than one file buffer is open, close
 * the current file buffer and switch to the next one.  If only one file
 * buffer is open, exit from nano. */
1093
void do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
1094
{
1095
1096
    int i;

1097
1098
    /* If the file hasn't been modified, pretend the user chose not to
     * save. */
1099
    if (!openfile->modified)
1100
	i = 0;
1101
1102
1103
    /* If the TEMP_FILE flag is set and the current file has a name,
     * pretend the user chose to save. */
    else if (openfile->filename[0] != '\0' && ISSET(TEMP_FILE))
1104
	i = 1;
1105
    /* Otherwise, ask the user whether or not to save. */
1106
1107
    else {
	/* If the TEMP_FILE flag is set, and the current file doesn't
1108
1109
1110
	 * have a name, warn the user before prompting for a name. */
	if (ISSET(TEMP_FILE))
	    no_current_file_name_warning();
1111

1112
	i = do_yesno_prompt(FALSE,
1113
1114
		_("Save modified buffer?  (Answering \"No\" will"
		  " DISCARD changes.) "));
1115
    }
1116

1117
#ifdef DEBUG
1118
    dump_filestruct(openfile->fileage);
1119
#endif
1120

1121
1122
    /* If the user chose not to save, or if the user chose to save and
     * the save succeeded, we're ready to exit. */
1123
1124
1125
1126
    if (i == 0 || (i == 1 && do_writeout(TRUE)))
	close_and_go();
    else if (i != 1)
	statusbar(_("Cancelled"));
1127

1128
1129
1130
1131
1132
1133
    display_main_list();
}

/* Close the current buffer, and terminate nano if it was the last. */
void close_and_go(void)
{
1134
#ifndef NANO_TINY
1135
1136
1137
    /* If there is a lockfile, remove it. */
    if (ISSET(LOCKING) && openfile->lock_filename)
	delete_lockfile(openfile->lock_filename);
1138
#endif
1139
#ifndef DISABLE_MULTIBUFFER
1140
    /* If there are no more open file buffers, jump off a cliff. */
1141
    if (!close_buffer())
1142
#endif
1143
	finish();
1144
1145
}

1146
/* Another placeholder for function mapping. */
1147
1148
1149
1150
void do_cancel(void)
{
    ;
}
1151

1152
1153
1154
1155
1156
1157
static struct sigaction pager_oldaction, pager_newaction;
	/* Original and temporary handlers for SIGINT. */
static bool pager_sig_failed = FALSE;
	/* Did sigaction() fail without changing the signal handlers? */
static bool pager_input_aborted = FALSE;
	/* Did someone invoke the pager and abort it via ^C? */
1158

1159
/* Things which need to be run regardless of whether
1160
 * we finished the stdin pipe correctly or not. */
1161
1162
1163
1164
1165
void finish_stdin_pager(void)
{
    FILE *f;
    int ttystdin;

1166
    /* Read whatever we did get from stdin. */
1167
    f = fopen("/dev/stdin", "rb");
1168
1169
    if (f == NULL)
	nperror("fopen");
1170

1171
    read_file(f, 0, "stdin", TRUE, FALSE);
1172
1173
    openfile->edittop = openfile->fileage;

1174
1175
    ttystdin = open("/dev/tty", O_RDONLY);
    if (!ttystdin)
1176
	die(_("Couldn't reopen stdin from keyboard, sorry\n"));
1177
1178
1179

    dup2(ttystdin,0);
    close(ttystdin);
1180
1181
    if (!pager_input_aborted)
	tcgetattr(0, &oldterm);
1182
    if (!pager_sig_failed && sigaction(SIGINT, &pager_oldaction, NULL) == -1)
1183
	nperror("sigaction");
1184
1185
1186
1187
    terminal_init();
    doupdate();
}

1188
/* Cancel reading from stdin like a pager. */
1189
1190
RETSIGTYPE cancel_stdin_pager(int signal)
{
1191
    pager_input_aborted = TRUE;
1192
1193
}

1194
/* Let nano read stdin for the first file at least. */
1195
1196
1197
void stdin_pager(void)
{
    endwin();
1198
1199
    if (!pager_input_aborted)
	tcsetattr(0, TCSANOW, &oldterm);
1200
1201
    fprintf(stderr, _("Reading from stdin, ^C to abort\n"));

1202
1203
    /* Enable interpretation of the special control keys so that
     * we get SIGINT when Ctrl-C is pressed. */
1204
#ifndef NANO_TINY
1205
    enable_signals();
1206
#endif
1207

1208
    /* Set things up so that SIGINT will cancel the new process. */
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
    if (sigaction(SIGINT, NULL, &pager_newaction) == -1) {
	pager_sig_failed = TRUE;
	nperror("sigaction");
    } else {
	pager_newaction.sa_handler = cancel_stdin_pager;
	if (sigaction(SIGINT, &pager_newaction, &pager_oldaction) == -1) {
	    pager_sig_failed = TRUE;
	    nperror("sigaction");
	}
    }
1219

1220
1221
1222
1223
    open_buffer("", FALSE);
    finish_stdin_pager();
}

1224
/* Initialize the signal handlers. */
1225
1226
void signal_init(void)
{
1227
    /* Trap SIGINT and SIGQUIT because we want them to do useful things. */
1228
1229
1230
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
1231
    sigaction(SIGQUIT, &act, NULL);
1232

1233
    /* Trap SIGHUP and SIGTERM because we want to write the file out. */
1234
    act.sa_handler = handle_hupterm;
1235
    sigaction(SIGHUP, &act, NULL);
1236
    sigaction(SIGTERM, &act, NULL);
1237

1238
#ifndef NANO_TINY
1239
    /* Trap SIGWINCH because we want to handle window resizes. */
1240
1241
    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);
1242
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1243

1244
    /* Trap normal suspend (^Z) so we can handle it ourselves. */
1245
1246
1247
1248
    if (!ISSET(SUSPEND)) {
	act.sa_handler = SIG_IGN;
	sigaction(SIGTSTP, &act, NULL);
    } else {
1249
1250
	/* Block all other signals in the suspend and continue handlers.
	 * If we don't do this, other stuff interrupts them! */
1251
	sigfillset(&act.sa_mask);
Chris Allegretta's avatar
Chris Allegretta committed
1252

1253
1254
	act.sa_handler = do_suspend;
	sigaction(SIGTSTP, &act, NULL);
1255

1256
	act.sa_handler = do_continue;
1257
1258
1259
	sigaction(SIGCONT, &act, NULL);
    }
}
1260

1261
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
1262
RETSIGTYPE handle_hupterm(int signal)
1263
{
1264
    die(_("Received SIGHUP or SIGTERM\n"));
1265
}
1266

1267
/* Handler for SIGTSTP (suspend). */
1268
RETSIGTYPE do_suspend(int signal)
1269
{
1270
1271
1272
1273
1274
#ifndef DISABLE_MOUSE
    /* Turn mouse support off. */
    disable_mouse_support();
#endif

1275
    /* Move the cursor to the last line of the screen. */
1276
    move(LINES - 1, 0);
1277
    endwin();
1278

1279
1280
    /* Display our helpful message. */
    printf(_("Use \"fg\" to return to nano.\n"));
1281
    fflush(stdout);
1282

1283
    /* Restore the old terminal settings. */
1284
    tcsetattr(0, TCSANOW, &oldterm);
1285

1286
    /* Trap SIGHUP and SIGTERM so we can properly deal with them while
1287
     * suspended. */
1288
1289
1290
1291
    act.sa_handler = handle_hupterm;
    sigaction(SIGHUP, &act, NULL);
    sigaction(SIGTERM, &act, NULL);

1292
    /* Do what mutt does: send ourselves a SIGSTOP. */
1293
1294
    kill(0, SIGSTOP);
}
1295

1296
/* The version of above function that is bound to a key. */
1297
void do_suspend_void(void)
1298
1299
1300
{
    if (ISSET(SUSPEND))
	do_suspend(0);
1301
1302
1303
1304
    else {
	statusbar(_("Suspension is not enabled"));
	beep();
    }
1305
1306
}

1307
/* Handler for SIGCONT (continue after suspend). */
1308
RETSIGTYPE do_continue(int signal)
1309
{
1310
1311
1312
1313
1314
1315
#ifndef DISABLE_MOUSE
    /* Turn mouse support back on if it was on before. */
    if (ISSET(USE_MOUSE))
	enable_mouse_support();
#endif

1316
#ifndef NANO_TINY
1317
    /* Perhaps the user resized the window while we slept.  Handle it,
1318
1319
     * and restore the terminal to its previous state and update the
     * screen in the process. */
1320
    handle_sigwinch(0);
1321
#else
1322
1323
1324
    /* Restore the terminal to its previous state. */
    terminal_init();

1325
1326
    /* Redraw the contents of the windows that need it. */
    blank_statusbar();
1327
    wnoutrefresh(bottomwin);
1328
    total_refresh();
1329
1330
1331
#endif
}

1332
#ifndef NANO_TINY
1333
/* Handler for SIGWINCH (window size change). */
1334
RETSIGTYPE handle_sigwinch(int signal)
1335
1336
1337
1338
1339
1340
1341
{
    /* Let the input routine know that a SIGWINCH has occurred. */
    sigwinch_counter++;
}

/* Reinitialize and redraw the screen completely. */
void regenerate_screen(void)
1342
1343
{
    const char *tty = ttyname(0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1344
    int fd, result = 0;
1345
1346
    struct winsize win;

1347
    if (tty == NULL)
1348
1349
1350
1351
1352
1353
1354
1355
1356
	return;
    fd = open(tty, O_RDWR);
    if (fd == -1)
	return;
    result = ioctl(fd, TIOCGWINSZ, &win);
    close(fd);
    if (result == -1)
	return;

1357
1358
1359
1360
    /* We could check whether the COLS or LINES changed, and return
     * otherwise.  However, COLS and LINES are curses global variables,
     * and in some cases curses has already updated them.  But not in
     * all cases.  Argh. */
1361
#ifdef REDEFINING_MACROS_OK
1362
1363
    COLS = win.ws_col;
    LINES = win.ws_row;
1364
#endif
1365

1366
1367
1368
1369
1370
1371
1372
1373
#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
1374
1375
1376
    /* Do the equivalent of what Minimum Profit does: Leave and
     * immediately reenter curses mode. */
    endwin();
1377
    doupdate();
1378
#endif
1379

1380
1381
1382
    /* Restore the terminal to its previous state. */
    terminal_init();

1383
1384
1385
1386
1387
    /* Do the equivalent of what both mutt and Minimum Profit do:
     * Reinitialize all the windows based on the new screen
     * dimensions. */
    window_init();

1388
    /* Redraw the contents of the windows that need it. */
1389
1390
    total_refresh();
}
1391

1392
1393
1394
/* If allow is FALSE, block any SIGWINCH signal.  If allow is TRUE,
 * unblock SIGWINCH so any pending ones can be dealt with. */
void allow_sigwinch(bool allow)
1395
1396
{
    sigset_t winch;
1397

1398
1399
    sigemptyset(&winch);
    sigaddset(&winch, SIGWINCH);
1400
    sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
1401
}
1402
#endif /* !NANO_TINY */
1403

1404
#ifndef NANO_TINY
1405
/* Handle the global toggle specified in flag. */
1406
void do_toggle(int flag)
1407
{
1408
    bool enabled;
1409

1410
1411
    if (ISSET(RESTRICTED) && (flag == SUSPEND || flag == MULTIBUFFER ||
			flag == BACKUP_FILE || flag == NO_COLOR_SYNTAX)) {
1412
	show_restricted_warning();
1413
1414
1415
	return;
    }

1416
    TOGGLE(flag);
Chris Allegretta's avatar
Chris Allegretta committed
1417

1418
    switch (flag) {
1419
#ifndef DISABLE_MOUSE
1420
	case USE_MOUSE:
1421
1422
	    mouse_init();
	    break;
1423
#endif
1424
1425
	case MORE_SPACE:
	case NO_HELP:
1426
	    window_init();
1427
	    total_refresh();
1428
	    break;
1429
	case SUSPEND:
1430
	    signal_init();
1431
	    break;
1432
	case WHITESPACE_DISPLAY:
1433
	    titlebar(NULL);
1434
1435
	    edit_refresh();
	    break;
1436
#ifndef DISABLE_COLOR
1437
	case NO_COLOR_SYNTAX:
1438
#endif
1439
	case SOFTWRAP:
1440
	    edit_refresh();
1441
	    break;
1442
    }
Chris Allegretta's avatar
Chris Allegretta committed
1443

1444
    enabled = ISSET(flag);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1445

1446
    if (flag == NO_HELP
1447
#ifndef DISABLE_WRAPPING
1448
	|| flag == NO_WRAP
1449
#endif
1450
#ifndef DISABLE_COLOR
1451
	|| flag == NO_COLOR_SYNTAX
1452
1453
#endif
	)
Chris Allegretta's avatar
Chris Allegretta committed
1454
	enabled = !enabled;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1455

1456
1457
    statusline(HUSH, "%s %s", _(flagtostr(flag)),
		enabled ? _("enabled") : _("disabled"));
Chris Allegretta's avatar
Chris Allegretta committed
1458
}
1459
#endif /* !NANO_TINY */
1460

1461
/* Bleh. */
1462
1463
void do_toggle_void(void)
{
1464
    ;
1465
}
Chris Allegretta's avatar
Chris Allegretta committed
1466

1467
1468
/* Disable extended input and output processing in our terminal
 * settings. */
1469
void disable_extended_io(void)
1470
1471
1472
1473
1474
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag &= ~IEXTEN;
1475
    term.c_oflag &= ~OPOST;
1476
1477
1478
    tcsetattr(0, TCSANOW, &term);
}

1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
/* Disable interpretation of the special control keys in our terminal
 * settings. */
void disable_signals(void)
{
    struct termios term;

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

1490
#ifndef NANO_TINY
1491
1492
/* Enable interpretation of the special control keys in our terminal
 * settings. */
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
void enable_signals(void)
{
    struct termios term;

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

1503
1504
/* Disable interpretation of the flow control characters in our terminal
 * settings. */
1505
1506
1507
1508
1509
void disable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1510
    term.c_iflag &= ~IXON;
1511
1512
1513
    tcsetattr(0, TCSANOW, &term);
}

1514
1515
/* Enable interpretation of the flow control characters in our terminal
 * settings. */
1516
1517
1518
1519
1520
void enable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1521
    term.c_iflag |= IXON;
1522
1523
1524
    tcsetattr(0, TCSANOW, &term);
}

1525
1526
1527
1528
1529
1530
1531
1532
/* Set up the terminal state.  Put the terminal in raw mode (read one
 * character at a time, disable the special control keys, and disable
 * the flow control characters), disable translation of carriage return
 * (^M) into newline (^J) so that we can tell the difference between the
 * Enter key and Ctrl-J, and disable echoing of characters as they're
 * typed.  Finally, disable extended input and output processing, and,
 * if we're not in preserve mode, reenable interpretation of the flow
 * control characters. */
1533
1534
void terminal_init(void)
{
1535
1536
1537
1538
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 2: Slang doesn't
     * implement raw(), nonl(), or noecho() properly, so there's no way
     * to properly reinitialize the terminal using them.  We have to
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1539
1540
1541
     * disable the special control keys and interpretation of the flow
     * control characters using termios, save the terminal state after
     * the first call, and restore it on subsequent calls. */
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
    static struct termios newterm;
    static bool newterm_set = FALSE;

    if (!newterm_set) {
#endif

	raw();
	nonl();
	noecho();
	disable_extended_io();
	if (ISSET(PRESERVE))
	    enable_flow_control();

	disable_signals();
1556
#ifdef USE_SLANG
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1557
1558
	if (!ISSET(PRESERVE))
	    disable_flow_control();
1559
1560
1561
1562
1563
1564

	tcgetattr(0, &newterm);
	newterm_set = TRUE;
    } else
	tcsetattr(0, TCSANOW, &newterm);
#endif
1565
1566
}

1567
1568
1569
1570
/* Say that an unbound key was struck, and if possible which one. */
void unbound_key(int code)
{
    if (func_key)
1571
	statusline(ALERT, _("Unbound key"));
1572
1573
1574
    else if (meta_key) {
	if (0x60 < code && code < 0x7B)
	    code -= 0x20;
1575
	statusline(ALERT, _("Unbound key: M-%c"), code);
1576
    } else if (code < 0x20)
1577
	statusline(ALERT, _("Unbound key: ^%c"), code + 0x40);
1578
    else
1579
	statusline(ALERT, _("Unbound key: %c"), code);
1580
1581
}

1582
/* Read in a character, interpret it as a shortcut or toggle if
1583
 * necessary, and return it.
1584
 * If allow_funcs is FALSE, don't actually run any functions associated
1585
 * with shortcut keys. */
1586
int do_input(bool allow_funcs)
1587
1588
1589
1590
1591
1592
1593
{
    int input;
	/* The character we read in. */
    static int *kbinput = NULL;
	/* The input buffer. */
    static size_t kbinput_len = 0;
	/* The length of the input buffer. */
1594
1595
    bool preserve = FALSE;
	/* Preserve the contents of the cutbuffer? */
1596
    const sc *s;
1597
1598
1599
    bool have_shortcut;

    /* Read in a character. */
1600
    input = get_kbinput(edit);
1601

1602
1603
1604
1605
1606
#ifndef NANO_TINY
    if (input == KEY_WINCH)
	return KEY_WINCH;
#endif

1607
#ifndef DISABLE_MOUSE
1608
    if (func_key && input == KEY_MOUSE) {
1609
	/* We received a mouse click. */
1610
	if (do_mouse() == 1)
1611
1612
	    /* The click was on a shortcut -- read in the character
	     * that it was converted into. */
1613
	    input = get_kbinput(edit);
1614
1615
	else
	    /* The click was invalid or has been handled -- get out. */
1616
	    return ERR;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1617
    }
1618
1619
1620
#endif

    /* Check for a shortcut in the main list. */
1621
    s = get_shortcut(&input);
1622
1623
1624

    /* If we got a shortcut from the main list, or a "universal"
     * edit window shortcut, set have_shortcut to TRUE. */
1625
    have_shortcut = (s != NULL);
1626

1627
1628
    /* If we got a non-high-bit control key, a meta key sequence, or a
     * function key, and it's not a shortcut or toggle, throw it out. */
1629
    if (!have_shortcut) {
1630
	if (is_ascii_cntrl_char(input) || meta_key || func_key) {
1631
	    unbound_key(input);
1632
1633
	    meta_key = FALSE;
	    func_key = FALSE;
1634
	    input = ERR;
1635
1636
1637
	}
    }

1638
    if (allow_funcs) {
1639
1640
1641
1642
	/* If we got a character, and it isn't a shortcut or toggle,
	 * it's a normal text character.  Display the warning if we're
	 * in view mode, or add the character to the input buffer if
	 * we're not. */
1643
	if (input != ERR && !have_shortcut) {
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
	    if (ISSET(VIEW_MODE))
		print_view_warning();
	    else {
		kbinput_len++;
		kbinput = (int *)nrealloc(kbinput, kbinput_len *
			sizeof(int));
		kbinput[kbinput_len - 1] = input;
	    }
	}

	/* If we got a shortcut or toggle, or if there aren't any other
	 * characters waiting after the one we read in, we need to
1656
	 * output all the characters in the input buffer if it isn't
1657
1658
	 * empty.  Note that it should be empty if we're in view
	 * mode. */
1659
	if (have_shortcut || get_key_buffer_len() == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1660
#ifndef DISABLE_WRAPPING
1661
	    /* If we got a shortcut or toggle, and it's not the shortcut
1662
	     * for verbatim input, turn off prepending of wrapped text. */
1663
	    if (have_shortcut && s->scfunc != do_verbatim_input)
1664
		wrap_reset();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1665
#endif
1666

1667
1668
	    if (kbinput != NULL) {
		/* Display all the characters in the input buffer at
1669
		 * once, filtering out control characters. */
1670
1671
1672
1673
1674
1675
1676
		char *output = charalloc(kbinput_len + 1);
		size_t i;

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

1677
		do_output(output, kbinput_len, FALSE);
1678
1679

		free(output);
1680
1681
1682
1683
1684
1685
1686
1687
1688

		/* Empty the input buffer. */
		kbinput_len = 0;
		free(kbinput);
		kbinput = NULL;
	    }
	}

	if (have_shortcut) {
1689
	    const subnfunc *f = sctofunc(s);
1690
1691
1692
	    /* If the function associated with this shortcut is
	     * cutting or copying text, remember this. */
	    if (s->scfunc == do_cut_text_void
1693
#ifndef NANO_TINY
1694
		|| s->scfunc == do_copy_text || s->scfunc == do_cut_till_eof
1695
#endif
1696
		)
1697
		preserve = TRUE;
1698

1699
1700
1701
1702
	    if (s->scfunc == NULL) {
		statusbar("Internal error: shortcut without function!");
		return ERR;
	    }
1703
1704
1705
	    if (ISSET(VIEW_MODE) && f && !f->viewok)
		print_view_warning();
	    else {
1706
#ifndef NANO_TINY
1707
1708
1709
1710
1711
1712
1713
1714
1715
		if (s->scfunc == do_toggle_void) {
		    do_toggle(s->toggle);
		    if (s->toggle != CUT_TO_END)
			preserve = TRUE;
		} else
#endif
		{
		    /* Execute the function of the shortcut. */
		    s->scfunc();
1716
#ifndef DISABLE_COLOR
1717
1718
		    if (f && !f->viewok)
			reset_multis(openfile->current, FALSE);
1719
#endif
1720
		    if (refresh_needed) {
1721
#ifdef DEBUG
1722
			fprintf(stderr, "running edit_refresh() as refresh_needed is true\n");
1723
#endif
1724
			edit_refresh();
1725
			refresh_needed = FALSE;
1726
1727
		    } else if (s->scfunc == do_delete || s->scfunc == do_backspace)
			update_line(openfile->current, openfile->current_x);
1728
		}
1729
1730
1731
1732
	    }
	}
    }

1733
1734
1735
    /* If we aren't cutting or copying text, and the key wasn't a toggle,
     * blow away the text in the cutbuffer upon the next cutting action. */
    if (!preserve)
1736
1737
	cutbuffer_reset();

1738
1739
1740
    return input;
}

1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
void xon_complaint(void)
{
    statusbar(_("XON ignored, mumble mumble"));
}

void xoff_complaint(void)
{
    statusbar(_("XOFF ignored, mumble mumble"));
}


1752
#ifndef DISABLE_MOUSE
1753
/* Handle a mouse click on the edit window or the shortcut list. */
1754
int do_mouse(void)
1755
1756
{
    int mouse_x, mouse_y;
1757
    int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
1758

1759
1760
1761
1762
    if (retval != 0)
	/* The click is wrong or already handled. */
	return retval;

1763
    /* We can click on the edit window to move the cursor. */
1764
    if (wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) {
1765
1766
1767
	bool sameline;
	    /* Did they click on the line with the cursor?  If they
	     * clicked on the cursor, we set the mark. */
1768
	filestruct *current_save = openfile->current;
1769
#ifndef NANO_TINY
1770
	size_t current_x_save = openfile->current_x;
1771
#endif
1772
1773
1774

	sameline = (mouse_y == openfile->current_y);

1775
#ifdef DEBUG
1776
	fprintf(stderr, "mouse_y = %d, current_y = %ld\n", mouse_y, (long)openfile->current_y);
1777
1778
#endif

1779
#ifndef NANO_TINY
1780
	if (ISSET(SOFTWRAP)) {
1781
	    size_t i = 0;
1782
1783
1784
1785
1786
1787
1788
	    for (openfile->current = openfile->edittop;
		 openfile->current->next && i < mouse_y;
		 openfile->current = openfile->current->next, i++) {
		openfile->current_y = i;
		i += strlenpt(openfile->current->data) / COLS;
	    }
#ifdef DEBUG
1789
1790
	    fprintf(stderr, "do_mouse(): moving to current_y = %ld, index i = %lu\n",
			(long)openfile->current_y, (unsigned long)i);
1791
1792
1793
1794
1795
1796
1797
	    fprintf(stderr, "            openfile->current->data = \"%s\"\n", openfile->current->data);
#endif

	    if (i > mouse_y) {
		openfile->current = openfile->current->prev;
		openfile->current_x = actual_x(openfile->current->data, mouse_x + (mouse_y - openfile->current_y) * COLS);
#ifdef DEBUG
1798
1799
		fprintf(stderr, "do_mouse(): i > mouse_y, mouse_x = %d, current_x to = %lu\n",
			mouse_x, (unsigned long)openfile->current_x);
1800
1801
#endif
	    } else {
1802
		openfile->current_x = actual_x(openfile->current->data, mouse_x);
1803
#ifdef DEBUG
1804
1805
		fprintf(stderr, "do_mouse(): i <= mouse_y, mouse_x = %d, setting current_x to = %lu\n",
			mouse_x, (unsigned long)openfile->current_x);
1806
1807
#endif
	    }
1808
1809
1810
	} else
#endif /* NANO_TINY */
	{
1811
1812
1813
1814
1815
1816
1817
1818
1819
	    /* Move to where the click occurred. */
	    for (; openfile->current_y < mouse_y && openfile->current !=
		   openfile->filebot; openfile->current_y++)
		openfile->current = openfile->current->next;
	    for (; openfile->current_y > mouse_y && openfile->current !=
		   openfile->fileage; openfile->current_y--)
		openfile->current = openfile->current->prev;

	    openfile->current_x = actual_x(openfile->current->data,
1820
		get_page_start(xplustabs()) + mouse_x);
1821
	}
1822

1823
#ifndef NANO_TINY
1824
1825
1826
1827
1828
	/* Clicking where the cursor is toggles the mark, as does
	 * clicking beyond the line length with the cursor at the end of
	 * the line. */
	if (sameline && openfile->current_x == current_x_save)
	    do_mark();
1829
	else
1830
#endif
1831
1832
	    /* The cursor moved; clean the cutbuffer on the next cut. */
	    cutbuffer_reset();
1833

1834
	edit_redraw(current_save);
1835
1836
    }

1837
    /* No more handling is needed. */
1838
    return 2;
1839
1840
1841
}
#endif /* !DISABLE_MOUSE */

1842
/* The user typed output_len multibyte characters.  Add them to the edit
1843
 * buffer, filtering out all ASCII control characters if allow_cntrls is
1844
1845
 * TRUE. */
void do_output(char *output, size_t output_len, bool allow_cntrls)
1846
{
1847
1848
1849
1850
1851
    size_t current_len, i = 0;
#ifndef NANO_TINY
    size_t orig_lenpt = 0;
#endif

1852
1853
    char *char_buf = charalloc(mb_cur_max());
    int char_buf_len;
1854

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

1857
    current_len = strlen(openfile->current->data);
1858
1859

#ifndef NANO_TINY
1860
1861
    if (ISSET(SOFTWRAP))
	orig_lenpt = strlenpt(openfile->current->data);
1862
#endif
1863

1864
    while (i < output_len) {
1865
	/* If allow_cntrls is TRUE, convert nulls and newlines properly. */
1866
1867
1868
	if (allow_cntrls) {
	    /* Null to newline, if needed. */
	    if (output[i] == '\0')
1869
		output[i] = '\n';
1870
1871
	    /* Newline to Enter, if needed. */
	    else if (output[i] == '\n') {
1872
		do_enter();
1873
1874
1875
		i++;
		continue;
	    }
1876
1877
	}

1878
1879
	/* Interpret the next multibyte character. */
	char_buf_len = parse_mbchar(output + i, char_buf, NULL);
1880
1881

	i += char_buf_len;
1882

1883
1884
	/* If allow_cntrls is FALSE, filter out an ASCII control character. */
	if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -	char_buf_len)))
1885
1886
	    continue;

1887
	/* If the NO_NEWLINES flag isn't set, when a character is
1888
	 * added to the magicline, it means we need a new magicline. */
1889
	if (!ISSET(NO_NEWLINES) && openfile->filebot == openfile->current)
1890
1891
1892
	    new_magicline();

	/* More dangerousness fun =) */
1893
	openfile->current->data = charealloc(openfile->current->data,
1894
					current_len + (char_buf_len * 2));
1895

1896
	assert(openfile->current_x <= current_len);
1897

1898
1899
1900
	charmove(openfile->current->data + openfile->current_x + char_buf_len,
			openfile->current->data + openfile->current_x,
			current_len - openfile->current_x + char_buf_len);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1901
	strncpy(openfile->current->data + openfile->current_x, char_buf,
1902
		char_buf_len);
1903
	current_len += char_buf_len;
1904
	openfile->totsize++;
1905
1906
	set_modified();

1907
#ifndef NANO_TINY
1908
	add_undo(ADD);
1909

1910
	/* Note that current_x has not yet been incremented. */
1911
1912
	if (openfile->mark_set && openfile->current == openfile->mark_begin &&
		openfile->current_x < openfile->mark_begin_x)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1913
	    openfile->mark_begin_x += char_buf_len;
1914
1915
#endif

1916
	openfile->current_x += char_buf_len;
1917

1918
1919
1920
1921
#ifndef NANO_TINY
	update_undo(ADD);
#endif

1922
#ifndef DISABLE_WRAPPING
1923
	/* If we're wrapping text, we need to call edit_refresh(). */
1924
	if (!ISSET(NO_WRAP))
1925
	    if (do_wrap(openfile->current))
1926
		refresh_needed = TRUE;
1927
#endif
1928
    }
1929

1930
1931
#ifndef NANO_TINY
    /* Well, we might also need a full refresh if we've changed the
1932
     * line length to be a new multiple of COLS. */
1933
    if (ISSET(SOFTWRAP) && refresh_needed == FALSE)
1934
	if (strlenpt(openfile->current->data) / COLS != orig_lenpt / COLS)
1935
	    refresh_needed = TRUE;
1936
#endif
1937

1938
    free(char_buf);
1939

1940
1941
    openfile->placewewant = xplustabs();

1942
#ifndef DISABLE_COLOR
1943
    reset_multis(openfile->current, FALSE);
1944
#endif
1945

1946
    if (refresh_needed == TRUE) {
1947
	edit_refresh();
1948
	refresh_needed = FALSE;
1949
    } else
1950
	update_line(openfile->current, openfile->current_x);
1951
1952
}

1953
int main(int argc, char **argv)
Chris Allegretta's avatar
Chris Allegretta committed
1954
1955
{
    int optchr;
1956
1957
    ssize_t startline = 0, startcol = 0;
	/* Target line and column when specified on the command line. */
1958
#ifndef DISABLE_WRAPJUSTIFY
1959
    bool fill_used = FALSE;
1960
	/* Was the fill option used on the command line? */
1961
#ifndef DISABLE_WRAPPING
1962
1963
    bool forced_wrapping = FALSE;
	/* Should long lines be automatically hard wrapped? */
1964
#endif
1965
#endif
1966
#ifndef DISABLE_MULTIBUFFER
1967
1968
1969
1970
    bool old_multibuffer;
	/* The old value of the multibuffer option, restored after we
	 * load all files on the command line. */
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1971
#ifdef HAVE_GETOPT_LONG
1972
    const struct option long_options[] = {
1973
	{"boldtext", 0, NULL, 'D'},
1974
#ifndef DISABLE_MULTIBUFFER
1975
	{"multibuffer", 0, NULL, 'F'},
Chris Allegretta's avatar
Chris Allegretta committed
1976
#endif
1977
#ifndef DISABLE_NANORC
1978
	{"ignorercfiles", 0, NULL, 'I'},
1979
#endif
1980
	{"rebindkeypad", 0, NULL, 'K'},
1981
	{"nonewlines", 0, NULL, 'L'},
1982
	{"morespace", 0, NULL, 'O'},
1983
#ifndef DISABLE_JUSTIFY
1984
	{"quotestr", 1, NULL, 'Q'},
1985
#endif
1986
	{"restricted", 0, NULL, 'R'},
1987
1988
	{"tabsize", 1, NULL, 'T'},
	{"version", 0, NULL, 'V'},
1989
#ifndef DISABLE_COLOR
1990
	{"syntax", 1, NULL, 'Y'},
1991
#endif
1992
	{"constantshow", 0, NULL, 'c'},
1993
	{"rebinddelete", 0, NULL, 'd'},
1994
	{"help", 0, NULL, 'h'},
1995
#ifndef DISABLE_MOUSE
1996
	{"mouse", 0, NULL, 'm'},
1997
#endif
1998
	{"noread", 0, NULL, 'n'},
1999
#ifndef DISABLE_OPERATINGDIR
2000
	{"operatingdir", 1, NULL, 'o'},
2001
#endif
2002
	{"preserve", 0, NULL, 'p'},
2003
	{"quiet", 0, NULL, 'q'},
2004
#ifndef DISABLE_WRAPJUSTIFY
2005
	{"fill", 1, NULL, 'r'},
2006
2007
#endif
#ifndef DISABLE_SPELLER
2008
	{"speller", 1, NULL, 's'},
2009
#endif
2010
2011
	{"tempfile", 0, NULL, 't'},
	{"view", 0, NULL, 'v'},
2012
#ifndef DISABLE_WRAPPING
2013
	{"nowrap", 0, NULL, 'w'},
2014
#endif
2015
2016
	{"nohelp", 0, NULL, 'x'},
	{"suspend", 0, NULL, 'z'},
2017
#ifndef NANO_TINY
2018
2019
	{"smarthome", 0, NULL, 'A'},
	{"backup", 0, NULL, 'B'},
2020
2021
	{"backupdir", 1, NULL, 'C'},
	{"tabstospaces", 0, NULL, 'E'},
2022
	{"locking", 0, NULL, 'G'},
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2023
	{"historylog", 0, NULL, 'H'},
2024
	{"noconvert", 0, NULL, 'N'},
2025
2026
	{"poslog", 0, NULL, 'P'},  /* deprecated form, remove in 2018 */
	{"positionlog", 0, NULL, 'P'},
2027
	{"smooth", 0, NULL, 'S'},
2028
	{"quickblank", 0, NULL, 'U'},
2029
	{"wordbounds", 0, NULL, 'W'},
2030
2031
	{"autoindent", 0, NULL, 'i'},
	{"cut", 0, NULL, 'k'},
2032
	{"unix", 0, NULL, 'u'},
2033
	{"softwrap", 0, NULL, '$'},
2034
#endif
2035
	{NULL, 0, NULL, 0}
Chris Allegretta's avatar
Chris Allegretta committed
2036
    };
2037
2038
#endif

2039
#ifdef ENABLE_UTF8
2040
    {
2041
2042
	/* If the locale set exists and uses UTF-8, we should use
	 * UTF-8. */
2043
2044
	char *locale = setlocale(LC_ALL, "");

2045
2046
	if (locale != NULL && (strcmp(nl_langinfo(CODESET),
		"UTF-8") == 0)) {
2047
#ifdef USE_SLANG
2048
	    SLutf8_enable(1);
2049
#endif
2050
	    utf8_init();
2051
	}
2052
2053
    }
#else
Chris Allegretta's avatar
Chris Allegretta committed
2054
    setlocale(LC_ALL, "");
2055
2056
#endif

2057
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
2058
2059
2060
2061
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

2062
#if defined(DISABLE_NANORC) && defined(DISABLE_ROOTWRAPPING)
2063
2064
    /* If we don't have rcfile support, --disable-wrapping-as-root is
     * used, and we're root, turn wrapping off. */
2065
    if (geteuid() == NANO_ROOT_UID)
2066
2067
	SET(NO_WRAP);
#endif
2068

2069
    while ((optchr =
Chris Allegretta's avatar
Chris Allegretta committed
2070
#ifdef HAVE_GETOPT_LONG
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2071
	getopt_long(argc, argv,
2072
		"ABC:DEFGHIKLNOPQ:RST:UVWY:abcdefghijklmno:pqr:s:tuvwxz$",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2073
		long_options, NULL)
Chris Allegretta's avatar
Chris Allegretta committed
2074
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2075
	getopt(argc, argv,
2076
		"ABC:DEFGHIKLNOPQ:RST:UVWY:abcdefghijklmno:pqr:s:tuvwxz$")
Chris Allegretta's avatar
Chris Allegretta committed
2077
#endif
2078
		) != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
2079
	switch (optchr) {
2080
2081
2082
2083
2084
2085
2086
2087
	    case 'a':
	    case 'b':
	    case 'e':
	    case 'f':
	    case 'g':
	    case 'j':
		/* Pico compatibility flags. */
		break;
2088
#ifndef NANO_TINY
2089
2090
2091
2092
2093
2094
	    case 'A':
		SET(SMART_HOME);
		break;
	    case 'B':
		SET(BACKUP_FILE);
		break;
2095
	    case 'C':
2096
2097
		backup_dir = mallocstrcpy(backup_dir, optarg);
		break;
2098
2099
2100
2101
2102
#endif
	    case 'D':
		SET(BOLD_TEXT);
		break;
#ifndef NANO_TINY
2103
2104
2105
	    case 'E':
		SET(TABS_TO_SPACES);
		break;
2106
#endif
2107
#ifndef DISABLE_MULTIBUFFER
2108
2109
2110
	    case 'F':
		SET(MULTIBUFFER);
		break;
Chris Allegretta's avatar
Chris Allegretta committed
2111
#endif
2112
#ifndef NANO_TINY
2113
2114
2115
	    case 'G':
		SET(LOCKING);
		break;
2116
2117
#endif
#ifndef DISABLE_HISTORIES
2118
2119
2120
	    case 'H':
		SET(HISTORYLOG);
		break;
2121
#endif
2122
#ifndef DISABLE_NANORC
2123
	    case 'I':
2124
		no_rcfiles = TRUE;
2125
		break;
2126
#endif
2127
2128
2129
	    case 'K':
		SET(REBIND_KEYPAD);
		break;
2130
2131
2132
	    case 'L':
		SET(NO_NEWLINES);
		break;
2133
#ifndef NANO_TINY
2134
2135
2136
	    case 'N':
		SET(NO_CONVERT);
		break;
2137
#endif
2138
2139
2140
	    case 'O':
		SET(MORE_SPACE);
		break;
2141
#ifndef DISABLE_HISTORIES
2142
2143
2144
2145
	    case 'P':
		SET(POS_HISTORY);
		break;
#endif
2146
#ifndef DISABLE_JUSTIFY
2147
2148
2149
	    case 'Q':
		quotestr = mallocstrcpy(quotestr, optarg);
		break;
2150
#endif
2151
2152
2153
	    case 'R':
		SET(RESTRICTED);
		break;
2154
#ifndef NANO_TINY
2155
	    case 'S':
2156
		SET(SMOOTH_SCROLL);
2157
		break;
2158
#endif
2159
2160
	    case 'T':
		if (!parse_num(optarg, &tabsize) || tabsize <= 0) {
2161
		    fprintf(stderr, _("Requested tab size \"%s\" is invalid"), optarg);
2162
		    fprintf(stderr, "\n");
2163
2164
2165
		    exit(1);
		}
		break;
2166
#ifndef NANO_TINY
2167
2168
2169
2170
	    case 'U':
		SET(QUICK_BLANK);
		break;
#endif
2171
2172
2173
	    case 'V':
		version();
		exit(0);
2174
#ifndef NANO_TINY
2175
2176
2177
2178
	    case 'W':
		SET(WORD_BOUNDS);
		break;
#endif
2179
#ifndef DISABLE_COLOR
2180
2181
2182
	    case 'Y':
		syntaxstr = mallocstrcpy(syntaxstr, optarg);
		break;
2183
#endif
2184
	    case 'c':
2185
		SET(CONST_UPDATE);
2186
2187
2188
2189
		break;
	    case 'd':
		SET(REBIND_DELETE);
		break;
2190
#ifndef NANO_TINY
2191
2192
2193
2194
2195
2196
	    case 'i':
		SET(AUTOINDENT);
		break;
	    case 'k':
		SET(CUT_TO_END);
		break;
2197
#endif
2198
#ifndef DISABLE_MOUSE
2199
2200
2201
	    case 'm':
		SET(USE_MOUSE);
		break;
2202
#endif
2203
2204
2205
	    case 'n':
		SET(NOREAD_MODE);
		break;
2206
#ifndef DISABLE_OPERATINGDIR
2207
2208
2209
	    case 'o':
		operating_dir = mallocstrcpy(operating_dir, optarg);
		break;
2210
#endif
2211
2212
2213
	    case 'p':
		SET(PRESERVE);
		break;
2214
#ifndef DISABLE_NANORC
2215
2216
2217
	    case 'q':
		SET(QUIET);
		break;
2218
#endif
2219
#ifndef DISABLE_WRAPJUSTIFY
2220
2221
	    case 'r':
		if (!parse_num(optarg, &wrap_at)) {
2222
		    fprintf(stderr, _("Requested fill size \"%s\" is invalid"), optarg);
2223
		    fprintf(stderr, "\n");
2224
2225
		    exit(1);
		}
2226
		fill_used = TRUE;
2227
#ifndef DISABLE_WRAPPING
2228
		forced_wrapping = TRUE;
2229
#endif
2230
		break;
2231
#endif
2232
#ifndef DISABLE_SPELLER
2233
2234
2235
	    case 's':
		alt_speller = mallocstrcpy(alt_speller, optarg);
		break;
2236
#endif
2237
2238
2239
	    case 't':
		SET(TEMP_FILE);
		break;
2240
#ifndef NANO_TINY
2241
2242
2243
	    case 'u':
		SET(MAKE_IT_UNIX);
		break;
2244
#endif
2245
2246
2247
	    case 'v':
		SET(VIEW_MODE);
		break;
2248
#ifndef DISABLE_WRAPPING
2249
2250
	    case 'w':
		SET(NO_WRAP);
2251
2252
		/* If both --fill and --nowrap are given on the
		 * command line, the last given option wins. */
2253
		forced_wrapping = FALSE;
2254
		break;
2255
#endif
2256
2257
2258
2259
2260
2261
	    case 'x':
		SET(NO_HELP);
		break;
	    case 'z':
		SET(SUSPEND);
		break;
2262
2263
2264
2265
2266
#ifndef NANO_TINY
	    case '$':
		SET(SOFTWRAP);
		break;
#endif
2267
	    case 'h':
2268
		usage();
2269
2270
2271
2272
		exit(0);
	    default:
		printf(_("Type '%s -h' for a list of available options.\n"), argv[0]);
		exit(1);
Chris Allegretta's avatar
Chris Allegretta committed
2273
2274
2275
	}
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2276
    /* If the executable filename starts with 'r', enable restricted
2277
     * mode. */
2278
2279
2280
    if (*(tail(argv[0])) == 'r')
	SET(RESTRICTED);

2281
2282
2283
    /* If we're using restricted mode, disable suspending, backups,
     * rcfiles, and history files, since they all would allow reading
     * from or writing to files not specified on the command line. */
2284
2285
2286
    if (ISSET(RESTRICTED)) {
	UNSET(SUSPEND);
	UNSET(BACKUP_FILE);
2287
#ifndef DISABLE_NANORC
2288
	no_rcfiles = TRUE;
2289
2290
	UNSET(HISTORYLOG);
	UNSET(POS_HISTORY);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2291
#endif
2292
2293
    }

2294
2295
2296
    /* Set up the function and shortcut lists.  This needs to be done
     * before reading the rcfile, to be able to rebind/unbind keys. */
    shortcut_init();
2297

Chris Allegretta's avatar
Chris Allegretta committed
2298
/* We've read through the command line options.  Now back up the flags
2299
2300
 * and values that are set, and read the rcfile(s).  If the values
 * haven't changed afterward, restore the backed-up values. */
2301
#ifndef DISABLE_NANORC
2302
    if (!no_rcfiles) {
Chris Allegretta's avatar
Chris Allegretta committed
2303
2304
2305
#ifndef DISABLE_OPERATINGDIR
	char *operating_dir_cpy = operating_dir;
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2306
#ifndef DISABLE_WRAPJUSTIFY
2307
	ssize_t wrap_at_cpy = wrap_at;
Chris Allegretta's avatar
Chris Allegretta committed
2308
#endif
2309
#ifndef NANO_TINY
2310
2311
	char *backup_dir_cpy = backup_dir;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2312
2313
2314
2315
2316
2317
#ifndef DISABLE_JUSTIFY
	char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
	char *alt_speller_cpy = alt_speller;
#endif
2318
	ssize_t tabsize_cpy = tabsize;
2319
2320
2321
2322
	unsigned flags_cpy[sizeof(flags) / sizeof(flags[0])];
	size_t i;

	memcpy(flags_cpy, flags, sizeof(flags_cpy));
Chris Allegretta's avatar
Chris Allegretta committed
2323

2324
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
2325
	operating_dir = NULL;
2326
#endif
2327
#ifndef NANO_TINY
2328
2329
	backup_dir = NULL;
#endif
2330
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
2331
	quotestr = NULL;
2332
2333
#endif
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
2334
	alt_speller = NULL;
2335
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2336
2337
2338

	do_rcfile();

2339
#ifdef DEBUG
2340
2341
	fprintf(stderr, "After rebinding keys...\n");
	print_sclist();
2342
2343
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2344
2345
2346
2347
2348
2349
#ifndef DISABLE_OPERATINGDIR
	if (operating_dir_cpy != NULL) {
	    free(operating_dir);
	    operating_dir = operating_dir_cpy;
	}
#endif
2350
#ifndef DISABLE_WRAPJUSTIFY
2351
	if (fill_used)
Chris Allegretta's avatar
Chris Allegretta committed
2352
2353
	    wrap_at = wrap_at_cpy;
#endif
2354
#ifndef NANO_TINY
2355
2356
2357
2358
	if (backup_dir_cpy != NULL) {
	    free(backup_dir);
	    backup_dir = backup_dir_cpy;
	}
2359
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
#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
2372
	if (tabsize_cpy != -1)
Chris Allegretta's avatar
Chris Allegretta committed
2373
	    tabsize = tabsize_cpy;
2374
2375
2376

	for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
	    flags[i] |= flags_cpy[i];
Chris Allegretta's avatar
Chris Allegretta committed
2377
    }
2378
2379
2380
2381
2382
2383
#ifdef DISABLE_ROOTWRAPPING
    /* If we don't have any rcfiles, --disable-wrapping-as-root is used,
     * and we're root, turn wrapping off. */
    else if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
2384
#endif /* !DISABLE_NANORC */
Chris Allegretta's avatar
Chris Allegretta committed
2385

2386
#ifndef DISABLE_WRAPPING
2387
2388
2389
    /* Override a "set nowrap" in an rcfile (or a --disable-wrapping-as-root)
     * if --fill was given on the command line and not undone by --nowrap. */
    if (forced_wrapping)
2390
	UNSET(NO_WRAP);
2391
#endif
2392

2393
2394
2395
    /* If we're using bold text instead of reverse video text, set it up
     * now. */
    if (ISSET(BOLD_TEXT))
2396
	hilite_attribute = A_BOLD;
2397

2398
#ifndef DISABLE_HISTORIES
2399
    /* Set up the search/replace history. */
2400
    history_init();
2401
    /* Verify that the home directory and ~/.nano subdir exist. */
2402
2403
2404
2405
2406
    if (ISSET(HISTORYLOG) || ISSET(POS_HISTORY)) {
	get_homedir();
	if (homedir == NULL || check_dotnano() == 0) {
	    UNSET(HISTORYLOG);
	    UNSET(POS_HISTORY);
2407
2408
	}
    }
2409
2410
2411
2412
    if (ISSET(HISTORYLOG))
	load_history();
    if (ISSET(POS_HISTORY))
	load_poshistory();
2413
#endif /* !DISABLE_HISTORIES */
2414

2415
#ifndef NANO_TINY
2416
    /* Set up the backup directory (unless we're using restricted mode,
2417
2418
2419
2420
     * in which case backups are disabled, since they would allow
     * reading from or writing to files not specified on the command
     * line).  This entails making sure it exists and is a directory, so
     * that backup files will be saved there. */
2421
2422
    if (!ISSET(RESTRICTED))
	init_backup_dir();
2423
#endif
2424

2425
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
2426
    /* Set up the operating directory.  This entails chdir()ing there,
2427
     * so that file reads and writes will be based there. */
2428
2429
2430
    init_operating_dir();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2431
#ifndef DISABLE_JUSTIFY
2432
    /* If punct wasn't specified, set its default value. */
2433
    if (punct == NULL)
2434
	punct = mallocstrcpy(NULL, "!.?");
2435

2436
    /* If brackets wasn't specified, set its default value. */
2437
    if (brackets == NULL)
2438
	brackets = mallocstrcpy(NULL, "\"')>]}");
2439

2440
    /* If quotestr wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2441
    if (quotestr == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2442
	quotestr = mallocstrcpy(NULL,
Chris Allegretta's avatar
Chris Allegretta committed
2443
#ifdef HAVE_REGEX_H
2444
		"^([ \t]*[#:>|}])+"
Chris Allegretta's avatar
Chris Allegretta committed
2445
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2446
		"> "
Chris Allegretta's avatar
Chris Allegretta committed
2447
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2448
		);
2449
#ifdef HAVE_REGEX_H
2450
    quoterc = regcomp(&quotereg, quotestr, NANO_REG_EXTENDED);
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464

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

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

2467
2468
#ifndef DISABLE_SPELLER
    /* If we don't have an alternative spell checker after reading the
2469
     * command line and/or rcfile(s), check $SPELL for one, as Pico
2470
     * does (unless we're using restricted mode, in which case spell
2471
2472
     * checking is disabled, since it would allow reading from or
     * writing to files not specified on the command line). */
2473
    if (!ISSET(RESTRICTED) && alt_speller == NULL) {
2474
2475
2476
2477
2478
2479
	char *spellenv = getenv("SPELL");
	if (spellenv != NULL)
	    alt_speller = mallocstrcpy(NULL, spellenv);
    }
#endif

2480
2481
2482
2483
2484
#ifndef NANO_TINY
    /* If matchbrackets wasn't specified, set its default value. */
    if (matchbrackets == NULL)
	matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");

2485
2486
2487
2488
    /* If whitespace wasn't specified, set its default value.  If we're
     * using UTF-8, it's Unicode 00BB (Right-Pointing Double Angle
     * Quotation Mark) and Unicode 00B7 (Middle Dot).  Otherwise, it's
     * ">" and ".". */
2489
    if (whitespace == NULL) {
2490
#ifdef ENABLE_UTF8
2491
	if (using_utf8()) {
2492
	    whitespace = mallocstrcpy(NULL, "\xC2\xBB\xC2\xB7");
2493
2494
	    whitespace_len[0] = 2;
	    whitespace_len[1] = 2;
2495
2496
2497
	} else
#endif
	{
2498
2499
2500
2501
	    whitespace = mallocstrcpy(NULL, ">.");
	    whitespace_len[0] = 1;
	    whitespace_len[1] = 1;
	}
2502
    }
Benno Schulenberg's avatar
Benno Schulenberg committed
2503
#endif /* !NANO_TINY */
2504

2505
    /* Initialize the search string. */
2506
2507
    last_search = mallocstrcpy(NULL, "");

2508
    /* If tabsize wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2509
    if (tabsize == -1)
2510
	tabsize = WIDTH_OF_TAB;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2511

2512
    /* Back up the old terminal settings so that they can be restored. */
2513
    tcgetattr(0, &oldterm);
2514

2515
2516
2517
    /* Initialize curses mode.  If this fails, get out. */
    if (initscr() == NULL)
	exit(1);
2518
2519

    /* Set up the terminal state. */
2520
    terminal_init();
2521

2522
2523
2524
#ifdef DEBUG
    fprintf(stderr, "Main: set up windows\n");
#endif
2525

2526
2527
2528
    /* Initialize all the windows based on the current screen
     * dimensions. */
    window_init();
2529
2530

    /* Set up the signal handlers. */
2531
    signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
2532

2533
#ifndef DISABLE_MOUSE
2534
    /* Initialize mouse support. */
2535
    mouse_init();
2536
#endif
2537

2538
2539
2540
#ifndef DISABLE_COLOR
    set_colorpairs();
#else
2541
2542
2543
2544
2545
2546
2547
    interface_color_pair[TITLE_BAR].pairnum = hilite_attribute;
    interface_color_pair[STATUS_BAR].pairnum = hilite_attribute;
    interface_color_pair[KEY_COMBO].pairnum = hilite_attribute;
    interface_color_pair[FUNCTION_TAG].pairnum = A_NORMAL;
    interface_color_pair[TITLE_BAR].bright = FALSE;
    interface_color_pair[STATUS_BAR].bright = FALSE;
    interface_color_pair[KEY_COMBO].bright = FALSE;
2548
    interface_color_pair[FUNCTION_TAG].bright = FALSE;
2549
2550
#endif

2551
#if !defined(NANO_TINY) && defined(HAVE_KEY_DEFINED)
2552
    const char *keyvalue;
2553
    /* Ask ncurses for the key codes for Control+Left and Control+Right. */
2554
2555
2556
2557
2558
2559
    keyvalue = tigetstr("kLFT5");
    if (keyvalue != 0 && keyvalue != (char *)-1)
	controlleft = key_defined(keyvalue);
    keyvalue = tigetstr("kRIT5");
    if (keyvalue != 0 && keyvalue != (char *)-1)
	controlright = key_defined(keyvalue);
2560
2561
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2562
#ifdef DEBUG
2563
    fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
2564
#endif
2565

2566
2567
2568
    /* If there's a +LINE or +LINE,COLUMN flag here, it is the first
     * non-option argument, and it is followed by at least one other
     * argument, the filename it applies to. */
2569
    if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
2570
	parse_line_column(&argv[optind][1], &startline, &startcol);
2571
2572
2573
	optind++;
    }

2574
    if (optind < argc && !strcmp(argv[optind], "-")) {
2575
	stdin_pager();
2576
2577
2578
2579
	set_modified();
	optind++;
    }

2580
#ifndef DISABLE_MULTIBUFFER
2581
2582
2583
2584
2585
2586
    old_multibuffer = ISSET(MULTIBUFFER);
    SET(MULTIBUFFER);

    /* Read all the files after the first one on the command line into
     * new buffers. */
    {
2587
	int i = optind + 1;
2588
	ssize_t iline = 0, icol = 0;
2589

2590
	for (; i < argc; i++) {
2591
2592
	    /* If there's a +LINE or +LINE,COLUMN flag here, it is followed
	     * by at least one other argument: the filename it applies to. */
2593
	    if (i < argc - 1 && argv[i][0] == '+')
2594
		parse_line_column(&argv[i][1], &iline, &icol);
2595
	    else {
2596
2597
2598
		/* If opening fails, don't try to position the cursor. */
		if (!open_buffer(argv[i], FALSE))
		    continue;
2599

2600
		/* If a position was given on the command line, go there. */
2601
		if (iline > 0 || icol > 0) {
2602
		    do_gotolinecolumn(iline, icol, FALSE, FALSE);
2603
2604
		    iline = 0;
		    icol = 0;
2605
		}
2606
#ifndef DISABLE_HISTORIES
2607
		else if (ISSET(POS_HISTORY)) {
2608
		    ssize_t savedposline, savedposcol;
2609
		    /* If edited before, restore the last cursor position. */
2610
		    if (check_poshistory(argv[i], &savedposline, &savedposcol))
2611
			do_gotolinecolumn(savedposline, savedposcol,
2612
						FALSE, FALSE);
2613
		}
2614
#endif
2615
2616
	    }
	}
2617
    }
Benno Schulenberg's avatar
Benno Schulenberg committed
2618
#endif /* !DISABLE_MULTIBUFFER */
2619

2620
    /* Now read the first file on the command line into a new buffer. */
2621
    if (optind < argc)
2622
	open_buffer(argv[optind], FALSE);
2623

2624
2625
2626
    /* If all the command-line arguments were invalid files like directories,
     * or if there were no filenames given, we didn't open any file.  In this
     * case, load a blank buffer.  Also, unset view mode to allow editing. */
2627
    if (openfile == NULL) {
2628
	open_buffer("", FALSE);
2629
	UNSET(VIEW_MODE);
Chris Allegretta's avatar
Chris Allegretta committed
2630
    }
2631

2632
#ifndef DISABLE_MULTIBUFFER
2633
2634
    if (!old_multibuffer)
	UNSET(MULTIBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
2635
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2636

2637
    /* If a starting position was given on the command line, go there. */
2638
    if (startline > 0 || startcol > 0)
2639
	do_gotolinecolumn(startline, startcol, FALSE, FALSE);
2640
#ifndef DISABLE_HISTORIES
2641
    else if (ISSET(POS_HISTORY)) {
2642
	ssize_t savedposline, savedposcol;
2643
	/* If the file was edited before, restore the last cursor position. */
2644
	if (check_poshistory(argv[optind], &savedposline, &savedposcol))
2645
	    do_gotolinecolumn(savedposline, savedposcol, FALSE, FALSE);
2646
    }
2647
#endif
2648

2649
2650
2651
2652
#ifdef DEBUG
    fprintf(stderr, "Main: bottom win, top win and edit win\n");
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2653
2654
    display_main_list();

2655
    display_buffer();
Robert Siemborski's avatar
Robert Siemborski committed
2656

2657
    while (TRUE) {
2658
	currmenu = MMAIN;
2659
	focusing = TRUE;
2660
	lastmessage = HUSH;
2661

2662
2663
2664
2665
	/* If constant cursor position display is on, and there are no
	 * keys waiting in the input buffer, display the current cursor
	 * position on the statusbar. */
	if (ISSET(CONST_UPDATE) && get_key_buffer_len() == 0)
2666
	    do_cursorpos(TRUE);
2667

2668
2669
2670
	/* Forget any earlier statusbar x position. */
	reinit_statusbar_x();

2671
2672
	/* Place the cursor in the edit window and make it visible. */
	reset_cursor();
2673
	curs_set(1);
2674
	wnoutrefresh(edit);
2675

2676
	/* Read in and interpret characters. */
2677
	do_input(TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
2678
    }
2679

2680
    /* We should never get here. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2681
    assert(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2682
}