nano.c 73.7 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/**************************************************************************
2
 *   nano.c  --  This file is part of GNU nano.                           *
Chris Allegretta's avatar
Chris Allegretta committed
3
 *                                                                        *
4
 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,  *
5
 *   2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.    *
6
7
 *   Copyright (C) 2014, 2015, 2016 Benno Schulenberg                     *
 *                                                                        *
8
9
10
11
 *   GNU nano is free software: you can redistribute it and/or modify     *
 *   it under the terms of the GNU General Public License as published    *
 *   by the Free Software Foundation, either version 3 of the License,    *
 *   or (at your option) any later version.                               *
Chris Allegretta's avatar
Chris Allegretta committed
12
 *                                                                        *
13
14
15
16
 *   GNU nano 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
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
19
 *   along with this program.  If not, see http://www.gnu.org/licenses/.  *
Chris Allegretta's avatar
Chris Allegretta committed
20
21
22
 *                                                                        *
 **************************************************************************/

23
#include "proto.h"
24
#include "revision.h"
25

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

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

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

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

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

73
    return newnode;
74
75
}

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

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

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

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

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

96
97
/* 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
98
{
99
    assert(afterthis != NULL && newnode != NULL);
100

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

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

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

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

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

126
    delete_node(fileptr);
127
}
128

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

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

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

146
    assert(src != NULL);
147

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

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

158
159
	src = src->next;
    }
160

161
    copy->next = NULL;
162

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

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

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

177
    delete_node(src);
178
179
}

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

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

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

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

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

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

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

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

208
209
210
    /* 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. */
211
212
213
    if (top != openfile->fileage) {
	p->fileage = openfile->fileage;
	openfile->fileage = top;
214
    } else
215
	p->fileage = NULL;
216
217
218
    if (bot != openfile->filebot) {
	p->filebot = openfile->filebot;
	openfile->filebot = bot;
219
220
    } else
	p->filebot = NULL;
221

222
223
    /* Remember which line is above the top of the partition, detach the
     * top of the partition from it, and save the text before top_x. */
224
225
226
227
    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
    /* Remember which line is below the bottom of the partition, detach the
     * bottom of the partition from it, and save the text after bot_x. */
231
232
233
    p->bot_next = bot->next;
    bot->next = NULL;
    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
234

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

238
    /* Remove all text before top_x at the top of the partition. */
239
    charmove(top->data, top->data + top_x, strlen(top->data) - top_x + 1);
240
    align(&top->data);
241

242
243
244
    /* Return the partition. */
    return p;
}
245

246
247
/* Unpartition a filestruct so that it begins at (fileage, 0) and ends
 * at (filebot, strlen(filebot->data)) again. */
248
249
void unpartition_filestruct(partition **p)
{
250
    assert(p != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
251

252
253
254
    /* 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. */
255
256
257
258
    openfile->fileage->prev = (*p)->top_prev;
    if (openfile->fileage->prev != NULL)
	openfile->fileage->prev->next = openfile->fileage;
    openfile->fileage->data = charealloc(openfile->fileage->data,
259
		strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
260
261
262
    charmove(openfile->fileage->data + strlen((*p)->top_data),
		openfile->fileage->data, strlen(openfile->fileage->data) + 1);
    strncpy(openfile->fileage->data, (*p)->top_data, strlen((*p)->top_data));
263
    free((*p)->top_data);
264

265
266
267
    /* 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. */
268
269
270
271
    openfile->filebot->next = (*p)->bot_next;
    if (openfile->filebot->next != NULL)
	openfile->filebot->next->prev = openfile->filebot;
    openfile->filebot->data = charealloc(openfile->filebot->data,
272
		strlen(openfile->filebot->data) + strlen((*p)->bot_data) + 1);
273
    strcat(openfile->filebot->data, (*p)->bot_data);
274
    free((*p)->bot_data);
275

276
277
278
    /* Restore the top and bottom of the filestruct, if they were
     * different from the top and bottom of the partition. */
    if ((*p)->fileage != NULL)
279
	openfile->fileage = (*p)->fileage;
280
    if ((*p)->filebot != NULL)
281
	openfile->filebot = (*p)->filebot;
282

283
284
285
286
    /* Uninitialize the partition. */
    free(*p);
    *p = NULL;
}
287

288
289
290
291
292
293
294
295
/* 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;
296
    bool edittop_inside;
297
#ifndef NANO_TINY
298
    bool mark_inside = FALSE;
299
    bool same_line = FALSE;
300
#endif
301

302
    assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
303

304
305
306
    /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
    if (top == bot && top_x == bot_x)
	return;
307

308
309
    /* 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
310
     * the edit window is inside the partition, and keep track of
311
312
     * whether the mark begins inside the partition. */
    filepart = partition_filestruct(top, top_x, bot, bot_x);
313
314
    edittop_inside = (openfile->edittop->lineno >= openfile->fileage->lineno &&
			openfile->edittop->lineno <= openfile->filebot->lineno);
315
#ifndef NANO_TINY
316
    if (openfile->mark_set) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
317
	mark_inside = (openfile->mark_begin->lineno >=
318
		openfile->fileage->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
319
		openfile->mark_begin->lineno <=
320
		openfile->filebot->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
321
322
323
324
		(openfile->mark_begin != openfile->fileage ||
		openfile->mark_begin_x >= top_x) &&
		(openfile->mark_begin != openfile->filebot ||
		openfile->mark_begin_x <= bot_x));
325
326
	same_line = (openfile->mark_begin == openfile->fileage);
    }
327
#endif
328

329
    /* Subtract the number of characters in the text from the file size. */
330
    openfile->totsize -= get_totsize(top, bot);
331

332
333
334
335
    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. */
336
337
	*file_top = openfile->fileage;
	*file_bot = openfile->filebot;
338

339
	/* Renumber, starting with file_top. */
340
	renumber(*file_top);
341
    } else {
342
343
	filestruct *file_bot_save = *file_bot;

344
345
346
	/* Otherwise, tack the text in top onto the text at the end of
	 * file_bot. */
	(*file_bot)->data = charealloc((*file_bot)->data,
347
348
349
		strlen((*file_bot)->data) +
		strlen(openfile->fileage->data) + 1);
	strcat((*file_bot)->data, openfile->fileage->data);
350

351
352
353
	/* 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. */
354
	(*file_bot)->next = openfile->fileage->next;
355
356
	if ((*file_bot)->next != NULL) {
	    (*file_bot)->next->prev = *file_bot;
357
	    *file_bot = openfile->filebot;
358
	}
359

360
	delete_node(openfile->fileage);
361

362
	/* Renumber, starting with the line after the original file_bot. */
363
	renumber(file_bot_save->next);
364
    }
365

366
    /* Since the text has now been saved, remove it from the filestruct. */
367
368
369
    openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct));
    openfile->fileage->data = mallocstrcpy(NULL, "");
    openfile->filebot = openfile->fileage;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
370

371
#ifndef DISABLE_COLOR
372
    openfile->fileage->multidata = NULL;
373
374
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
375
376
377
378
379
    /* 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;
380
#ifndef NANO_TINY
381
    if (mark_inside) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
382
383
	openfile->mark_begin = openfile->current;
	openfile->mark_begin_x = openfile->current_x;
384
    } else if (same_line)
385
	/* Update the pointer to this partially cut line. */
386
	openfile->mark_begin = openfile->current;
387
#endif
388

389
    top_save = openfile->fileage;
390

391
392
393
    /* Unpartition the filestruct so that it contains all the text
     * again, minus the saved text. */
    unpartition_filestruct(&filepart);
394

395
396
    /* If the top of the edit window was inside the old partition, put
     * it in range of current. */
397
    if (edittop_inside) {
398
	adjust_viewport(STATIONARY);
399
	refresh_needed = TRUE;
400
    }
401

402
    /* Renumber, starting with the beginning line of the old partition. */
403
    renumber(top_save);
404

405
    /* If the text doesn't end with a magicline, and it should, add one. */
406
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
407
408
	new_magicline();
}
409

410
411
412
/* Copy all text from the given filestruct to the current filestruct
 * at the current cursor position. */
void copy_from_filestruct(filestruct *somebuffer)
413
414
{
    filestruct *top_save;
415
    size_t current_x_save = openfile->current_x;
416
    bool edittop_inside;
417
#ifndef NANO_TINY
418
    bool right_side_up = FALSE, single_line = FALSE;
419
#endif
420

421
    assert(somebuffer != NULL);
422

423
424
425
426
427
428
429
430
#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,
431
			(const filestruct **)&bot, &bot_x, &right_side_up);
432
433
434
435
436

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

437
    /* Partition the filestruct so that it contains no text, and remember
438
     * whether the current line is at the top of the edit window. */
439
440
    filepart = partition_filestruct(openfile->current, openfile->current_x,
				openfile->current, openfile->current_x);
441
    edittop_inside = (openfile->edittop == openfile->fileage);
442
    free_filestruct(openfile->fileage);
443

444
445
446
    /* Put the top and bottom of the current filestruct at the top and
     * bottom of a copy of the passed buffer. */
    openfile->fileage = copy_filestruct(somebuffer);
447
448
449
    openfile->filebot = openfile->fileage;
    while (openfile->filebot->next != NULL)
	openfile->filebot = openfile->filebot->next;
450

451
    /* Put the cursor at the end of the pasted text. */
452
453
    openfile->current = openfile->filebot;
    openfile->current_x = strlen(openfile->filebot->data);
454
455
456

    /* Refresh the mark's pointer, and compensate the mark's
     * x coordinate for the change in the current line. */
457
458
    if (openfile->fileage == openfile->filebot) {
#ifndef NANO_TINY
459
	if (openfile->mark_set && single_line) {
460
	    openfile->mark_begin = openfile->current;
461
	    if (!right_side_up)
462
463
464
		openfile->mark_begin_x += openfile->current_x;
	}
#endif
465
466
	/* When the pasted stuff contains no newline, adjust the cursor's
	 * x coordinate for the text that is before the pasted stuff. */
467
	openfile->current_x += current_x_save;
468
    }
469
#ifndef NANO_TINY
470
471
472
473
474
475
    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;
476
477
	}
    }
478
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
479

480
481
    /* Add the number of characters in the copied text to the file size. */
    openfile->totsize += get_totsize(openfile->fileage, openfile->filebot);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
482

483
484
485
    /* 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. */
486
    openfile->current_y += openfile->filebot->lineno - 1;
487

488
489
    /* If we pasted onto the first line of the edit window, the corresponding
     * struct has been freed, so... point at the start of the copied text. */
490
491
492
    if (edittop_inside)
	openfile->edittop = openfile->fileage;

493
494
    top_save = openfile->fileage;

495
    /* Unpartition the filestruct so that it contains all the text
496
     * again, plus the copied text. */
497
    unpartition_filestruct(&filepart);
498

499
    /* Renumber, starting with the beginning line of the old partition. */
500
    renumber(top_save);
501

502
    /* If the text doesn't end with a magicline, and it should, add one. */
503
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
504
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
505
506
}

507
508
509
/* Create a new openfilestruct node. */
openfilestruct *make_new_opennode(void)
{
510
    return (openfilestruct *)nmalloc(sizeof(openfilestruct));
511
512
513
514
515
}

/* Unlink a node from the rest of the openfilestruct, and delete it. */
void unlink_opennode(openfilestruct *fileptr)
{
516
    assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL &&
517
		fileptr != fileptr->prev && fileptr != fileptr->next);
518
519
520
521
522
523
524

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

    delete_opennode(fileptr);
}

525
/* Free all the memory in the given open-file node. */
526
527
528
529
530
531
void delete_opennode(openfilestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);

    free(fileptr->filename);
    free_filestruct(fileptr->fileage);
532
#ifndef NANO_TINY
533
    free(fileptr->current_stat);
534
    free(fileptr->lock_filename);
535
536
    /* Free the undo stack. */
    discard_until(NULL, fileptr);
537
538
539
540
#endif
    free(fileptr);
}

541
/* Display a warning about a key disabled in view mode. */
542
void print_view_warning(void)
543
{
544
    statusbar(_("Key is invalid in view mode"));
545
546
}

547
548
549
550
551
552
553
554
555
556
557
/* 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)
{
558
    statusbar(_("Help is not available"));
559
560
561
}
#endif

562
/* Make nano exit gracefully. */
563
void finish(void)
Chris Allegretta's avatar
Chris Allegretta committed
564
{
565
566
567
568
    /* 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
569
    wrefresh(bottomwin);
570
    endwin();
571

572
573
    /* Restore the old terminal settings. */
    tcsetattr(0, TCSANOW, &oldterm);
574

575
#ifndef DISABLE_HISTORIES
576
    if (ISSET(HISTORYLOG))
577
	save_history();
578
    if (ISSET(POS_HISTORY)) {
579
	update_poshistory(openfile->filename, openfile->current->lineno, xplustabs() + 1);
580
	save_poshistory();
581
    }
582
583
584
585
586
587
#endif

#ifdef DEBUG
    thanks_for_all_the_fish();
#endif

588
    /* Get out. */
589
    exit(0);
590
591
}

592
/* Make nano die gracefully. */
593
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
594
{
595
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
596

597
    endwin();
Chris Allegretta's avatar
Chris Allegretta committed
598

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

602
603
604
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
605

606
607
#ifndef NANO_TINY
    /* If the current buffer has a lockfile, remove it. */
608
    if (openfile && ISSET(LOCKING) && openfile->lock_filename)
609
610
611
	delete_lockfile(openfile->lock_filename);
#endif

612
    /* If the current file buffer was modified, save it. */
613
    if (openfile && openfile->modified) {
614
	/* If the filestruct is partitioned, unpartition it first. */
615
616
617
	if (filepart != NULL)
	    unpartition_filestruct(&filepart);

618
	die_save_file(openfile->filename, openfile->current_stat);
Chris Allegretta's avatar
Chris Allegretta committed
619
620
    }

621
#ifndef DISABLE_MULTIBUFFER
622
    /* Save all of the other modified file buffers, if any. */
623
    if (openfile != NULL) {
624
	openfilestruct *firstone = openfile;
Chris Allegretta's avatar
Chris Allegretta committed
625

626
	while (openfile->next != firstone) {
627
	    openfile = openfile->next;
628

629
630
631
632
#ifndef NANO_TINY
	    if (ISSET(LOCKING) && openfile->lock_filename)
		delete_lockfile(openfile->lock_filename);
#endif
633
	    if (openfile->modified)
634
		die_save_file(openfile->filename, openfile->current_stat);
635
	}
636
    }
637
638
#endif

639
    /* Abandon the building. */
640
    exit(1);
641
642
}

643
/* Save the current file under the name specified in die_filename, which
644
 * is modified to be unique if necessary. */
645
void die_save_file(const char *die_filename, struct stat *die_stat)
646
{
647
    char *targetname;
648
    bool failed = TRUE;
649

650
651
652
653
654
    /* 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;
655

656
657
    /* 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
658
    if (*die_filename == '\0')
659
	die_filename = "nano";
660

661
662
663
664
    targetname = get_next_filename(die_filename, ".save");

    if (*targetname != '\0')
	failed = !write_file(targetname, NULL, TRUE, OVERWRITE, TRUE);
665

666
    if (!failed)
667
668
669
	fprintf(stderr, _("\nBuffer written to %s\n"), targetname);
    else if (*targetname != '\0')
	fprintf(stderr, _("\nBuffer not written to %s: %s\n"), targetname,
670
671
672
673
		strerror(errno));
    else
	fprintf(stderr, _("\nBuffer not written: %s\n"),
		_("Too many backup files?"));
674

675
#ifndef NANO_TINY
676
677
678
    /* 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. */
679
    if (die_stat) {
680
681
682
	IGNORE_CALL_RESULT(chmod(targetname, die_stat->st_mode));
	IGNORE_CALL_RESULT(chown(targetname, die_stat->st_uid,
						die_stat->st_gid));
683
    }
684
#endif
685

686
    free(targetname);
687
}
688

689
/* Initialize the three window portions nano uses. */
690
void window_init(void)
691
{
692
693
    /* First delete existing windows, in case of resizing. */
    delwin(topwin);
694
    topwin = NULL;
695
696
697
    delwin(edit);
    delwin(bottomwin);

698
699
700
701
702
703
704
705
706
707
    /* If the terminal is very flat, don't set up a titlebar. */
    if (LINES < 3) {
	editwinrows = 1;
	/* Set up two subwindows.  If the terminal is just one line,
	 * edit window and statusbar window will cover each other. */
	edit = newwin(1, COLS, 0, 0);
	bottomwin = newwin(1, COLS, LINES - 1, 0);
    } else {
	int toprows = (ISSET(MORE_SPACE) ? 1 : (LINES < 6) ? 1 : 2);
	int bottomrows = (ISSET(NO_HELP) ? 1 : (LINES < 5) ? 1 : 3);
708

709
	editwinrows = LINES - toprows - bottomrows;
710

711
712
713
714
715
716
717
718
719
	/* Set up the normal three subwindows. */
	topwin = newwin(toprows, COLS, 0, 0);
	edit = newwin(editwinrows, COLS, toprows, 0);
	bottomwin = newwin(bottomrows, COLS, toprows + editwinrows, 0);
    }

    /* In case the terminal shrunk, make sure the status line is clear. */
    blank_statusbar();
    wnoutrefresh(bottomwin);
720

721
    /* Turn the keypad on for the windows, if necessary. */
722
    if (!ISSET(REBIND_KEYPAD)) {
723
	keypad(topwin, TRUE);
724
725
726
	keypad(edit, TRUE);
	keypad(bottomwin, TRUE);
    }
727
728
729
730
731
732
733
734
735

#ifndef DISABLE_WRAPJUSTIFY
    /* Set up the wrapping point, accounting for screen width when negative. */
    fill = wrap_at;
    if (fill <= 0)
	fill += COLS;
    if (fill < 0)
	fill = 0;
#endif
736
737
}

738
#ifndef DISABLE_MOUSE
739
740
741
742
/* Disable mouse support. */
void disable_mouse_support(void)
{
    mousemask(0, NULL);
743
    mouseinterval(oldinterval);
744
745
746
747
748
}

/* Enable mouse support. */
void enable_mouse_support(void)
{
749
    mousemask(ALL_MOUSE_EVENTS, NULL);
750
    oldinterval = mouseinterval(50);
751
752
753
754
}

/* Initialize mouse support.  Enable it if the USE_MOUSE flag is set,
 * and disable it otherwise. */
755
void mouse_init(void)
756
{
757
758
759
760
    if (ISSET(USE_MOUSE))
	enable_mouse_support();
    else
	disable_mouse_support();
761
}
762
#endif /* !DISABLE_MOUSE */
763

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
764
#ifdef HAVE_GETOPT_LONG
765
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, longflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
766
#else
767
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
768
769
#endif

770
/* Print one usage string to the screen.  This cuts down on duplicate
771
 * strings to translate, and leaves out the parts that shouldn't be
772
 * translatable (i.e. the flag names). */
773
void print_opt_full(const char *shortflag
774
775
776
777
#ifdef HAVE_GETOPT_LONG
	, const char *longflag
#endif
	, const char *desc)
778
779
{
    printf(" %s\t", shortflag);
780
    if (strlenpt(shortflag) < 8)
781
782
783
784
	printf("\t");

#ifdef HAVE_GETOPT_LONG
    printf("%s\t", longflag);
785
    if (strlenpt(longflag) < 8)
786
	printf("\t\t");
787
    else if (strlenpt(longflag) < 16)
788
789
790
	printf("\t");
#endif

791
792
793
    if (desc != NULL)
	printf("%s", _(desc));
    printf("\n");
794
795
}

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

927
928
929
/* 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
930
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
931
{
932
#ifdef REVISION
933
    printf(" GNU nano from git, %s\n", REVISION);
934
#else
935
    printf(_(" GNU nano, version %s\n"), VERSION);
936
#endif
937
    printf(" (C) 1999..2016 Free Software Foundation, Inc.\n");
938
    printf(_(" (C) 2014..%s the contributors to nano\n"), "2016");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
939
    printf(
940
	_(" Email: nano@nano-editor.org	Web: https://nano-editor.org/"));
941
    printf(_("\n Compiled options:"));
942

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

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

1061
1062
/* 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
1063
1064
1065
1066
 * 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. */
1067
void do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
1068
{
1069
1070
    int i;

1071
1072
    /* If the file hasn't been modified, pretend the user chose not to
     * save. */
1073
    if (!openfile->modified)
1074
	i = 0;
1075
1076
1077
    /* 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))
1078
	i = 1;
1079
    /* Otherwise, ask the user whether or not to save. */
1080
1081
    else {
	/* If the TEMP_FILE flag is set, and the current file doesn't
1082
1083
	 * have a name, warn the user before prompting for a name. */
	if (ISSET(TEMP_FILE))
1084
	    warn_and_shortly_pause(_("No file name"));
1085

1086
1087
	i = do_yesno_prompt(FALSE, _("Save modified buffer?  "
			"(Answering \"No\" will DISCARD changes.) "));
1088
    }
1089

1090
#ifdef DEBUG
1091
    dump_filestruct(openfile->fileage);
1092
#endif
1093

1094
1095
    /* If the user chose not to save, or if the user chose to save and
     * the save succeeded, we're ready to exit. */
1096
1097
1098
1099
1100
1101
1102
1103
1104
    if (i == 0 || (i == 1 && do_writeout(TRUE)))
	close_and_go();
    else if (i != 1)
	statusbar(_("Cancelled"));
}

/* Close the current buffer, and terminate nano if it was the last. */
void close_and_go(void)
{
1105
#ifndef NANO_TINY
1106
1107
1108
    /* If there is a lockfile, remove it. */
    if (ISSET(LOCKING) && openfile->lock_filename)
	delete_lockfile(openfile->lock_filename);
1109
#endif
1110
#ifndef DISABLE_MULTIBUFFER
1111
    /* If there are no more open file buffers, jump off a cliff. */
1112
    if (!close_buffer())
1113
#endif
1114
	finish();
1115
1116
}

1117
/* Another placeholder for function mapping. */
1118
1119
1120
1121
void do_cancel(void)
{
    ;
}
1122

1123
1124
1125
1126
1127
1128
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? */
1129

1130
/* Things which need to be run regardless of whether
1131
 * we finished the stdin pipe correctly or not. */
1132
1133
1134
1135
1136
void finish_stdin_pager(void)
{
    FILE *f;
    int ttystdin;

1137
    /* Read whatever we did get from stdin. */
1138
    f = fopen("/dev/stdin", "rb");
1139
1140
    if (f == NULL)
	nperror("fopen");
1141

1142
    read_file(f, 0, "stdin", TRUE, FALSE);
1143
1144
    openfile->edittop = openfile->fileage;

1145
1146
    ttystdin = open("/dev/tty", O_RDONLY);
    if (!ttystdin)
1147
	die(_("Couldn't reopen stdin from keyboard, sorry\n"));
1148
1149
1150

    dup2(ttystdin,0);
    close(ttystdin);
1151
1152
    if (!pager_input_aborted)
	tcgetattr(0, &oldterm);
1153
    if (!pager_sig_failed && sigaction(SIGINT, &pager_oldaction, NULL) == -1)
1154
	nperror("sigaction");
1155
1156
1157
1158
    terminal_init();
    doupdate();
}

1159
/* Cancel reading from stdin like a pager. */
1160
1161
RETSIGTYPE cancel_stdin_pager(int signal)
{
1162
    pager_input_aborted = TRUE;
1163
1164
}

1165
/* Let nano read stdin for the first file at least. */
1166
1167
1168
void stdin_pager(void)
{
    endwin();
1169
1170
    if (!pager_input_aborted)
	tcsetattr(0, TCSANOW, &oldterm);
1171
1172
    fprintf(stderr, _("Reading from stdin, ^C to abort\n"));

1173
#ifndef NANO_TINY
1174
1175
    /* Enable interpretation of the special control keys so that
     * we get SIGINT when Ctrl-C is pressed. */
1176
    enable_signals();
1177
#endif
1178

1179
    /* Set things up so that SIGINT will cancel the new process. */
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
    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");
	}
    }
1190

1191
1192
1193
1194
    open_buffer("", FALSE);
    finish_stdin_pager();
}

1195
/* Initialize the signal handlers. */
1196
1197
void signal_init(void)
{
1198
    /* Trap SIGINT and SIGQUIT because we want them to do useful things. */
1199
1200
1201
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
1202
    sigaction(SIGQUIT, &act, NULL);
1203

1204
    /* Trap SIGHUP and SIGTERM because we want to write the file out. */
1205
    act.sa_handler = handle_hupterm;
1206
    sigaction(SIGHUP, &act, NULL);
1207
    sigaction(SIGTERM, &act, NULL);
1208

1209
#ifndef NANO_TINY
1210
    /* Trap SIGWINCH because we want to handle window resizes. */
1211
1212
    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);
1213
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1214

1215
    /* Trap normal suspend (^Z) so we can handle it ourselves. */
1216
1217
1218
1219
    if (!ISSET(SUSPEND)) {
	act.sa_handler = SIG_IGN;
	sigaction(SIGTSTP, &act, NULL);
    } else {
1220
1221
	/* Block all other signals in the suspend and continue handlers.
	 * If we don't do this, other stuff interrupts them! */
1222
	sigfillset(&act.sa_mask);
Chris Allegretta's avatar
Chris Allegretta committed
1223

1224
1225
	act.sa_handler = do_suspend;
	sigaction(SIGTSTP, &act, NULL);
1226

1227
	act.sa_handler = do_continue;
1228
1229
1230
	sigaction(SIGCONT, &act, NULL);
    }
}
1231

1232
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
1233
RETSIGTYPE handle_hupterm(int signal)
1234
{
1235
    die(_("Received SIGHUP or SIGTERM\n"));
1236
}
1237

1238
/* Handler for SIGTSTP (suspend). */
1239
RETSIGTYPE do_suspend(int signal)
1240
{
1241
1242
1243
1244
1245
#ifndef DISABLE_MOUSE
    /* Turn mouse support off. */
    disable_mouse_support();
#endif

1246
    /* Move the cursor to the last line of the screen. */
1247
    move(LINES - 1, 0);
1248
    endwin();
1249

1250
1251
    /* Display our helpful message. */
    printf(_("Use \"fg\" to return to nano.\n"));
1252
    fflush(stdout);
1253

1254
    /* Restore the old terminal settings. */
1255
    tcsetattr(0, TCSANOW, &oldterm);
1256

1257
    /* Trap SIGHUP and SIGTERM so we can properly deal with them while
1258
     * suspended. */
1259
1260
1261
1262
    act.sa_handler = handle_hupterm;
    sigaction(SIGHUP, &act, NULL);
    sigaction(SIGTERM, &act, NULL);

1263
    /* Do what mutt does: send ourselves a SIGSTOP. */
1264
1265
    kill(0, SIGSTOP);
}
1266

1267
/* The version of above function that is bound to a key. */
1268
void do_suspend_void(void)
1269
1270
1271
{
    if (ISSET(SUSPEND))
	do_suspend(0);
1272
1273
1274
1275
    else {
	statusbar(_("Suspension is not enabled"));
	beep();
    }
1276
1277
}

1278
/* Handler for SIGCONT (continue after suspend). */
1279
RETSIGTYPE do_continue(int signal)
1280
{
1281
1282
1283
1284
1285
1286
#ifndef DISABLE_MOUSE
    /* Turn mouse support back on if it was on before. */
    if (ISSET(USE_MOUSE))
	enable_mouse_support();
#endif

1287
#ifndef NANO_TINY
1288
1289
1290
    /* Perhaps the user resized the window while we slept.  So act as if,
     * and restore the terminal to its previous state in the process. */
    regenerate_screen();
1291
#else
1292
1293
1294
    /* Restore the terminal to its previous state. */
    terminal_init();

1295
    /* Wipe statusbar; redraw titlebar and edit window (and help lines). */
1296
    blank_statusbar();
1297
    wnoutrefresh(bottomwin);
1298
    total_refresh();
1299
1300
1301
#endif
}

1302
#ifndef NANO_TINY
1303
/* Handler for SIGWINCH (window size change). */
1304
RETSIGTYPE handle_sigwinch(int signal)
1305
1306
1307
1308
1309
1310
1311
{
    /* Let the input routine know that a SIGWINCH has occurred. */
    sigwinch_counter++;
}

/* Reinitialize and redraw the screen completely. */
void regenerate_screen(void)
1312
1313
{
    const char *tty = ttyname(0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1314
    int fd, result = 0;
1315
1316
    struct winsize win;

1317
    if (tty == NULL)
1318
1319
1320
1321
1322
1323
1324
1325
1326
	return;
    fd = open(tty, O_RDWR);
    if (fd == -1)
	return;
    result = ioctl(fd, TIOCGWINSZ, &win);
    close(fd);
    if (result == -1)
	return;

1327
1328
1329
1330
    /* 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. */
1331
#ifdef REDEFINING_MACROS_OK
1332
1333
    COLS = win.ws_col;
    LINES = win.ws_row;
1334
#endif
1335
    editwincols = COLS - margin;
1336

1337
1338
1339
1340
1341
1342
1343
1344
#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
1345
1346
1347
    /* Do the equivalent of what Minimum Profit does: Leave and
     * immediately reenter curses mode. */
    endwin();
1348
    doupdate();
1349
#endif
1350

1351
1352
1353
    /* Restore the terminal to its previous state. */
    terminal_init();

1354
1355
1356
1357
1358
    /* Do the equivalent of what both mutt and Minimum Profit do:
     * Reinitialize all the windows based on the new screen
     * dimensions. */
    window_init();

1359
    /* Redraw the contents of the windows that need it. */
1360
1361
    total_refresh();
}
1362

1363
1364
1365
/* 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)
1366
1367
{
    sigset_t winch;
1368

1369
1370
    sigemptyset(&winch);
    sigaddset(&winch, SIGWINCH);
1371
    sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
1372
}
1373
#endif /* !NANO_TINY */
1374

1375
#ifndef NANO_TINY
1376
/* Handle the global toggle specified in flag. */
1377
void do_toggle(int flag)
1378
{
1379
    bool enabled;
1380

1381
1382
    if (ISSET(RESTRICTED) && (flag == SUSPEND || flag == MULTIBUFFER ||
			flag == BACKUP_FILE || flag == NO_COLOR_SYNTAX)) {
1383
	show_restricted_warning();
1384
1385
1386
	return;
    }

1387
    TOGGLE(flag);
Chris Allegretta's avatar
Chris Allegretta committed
1388

1389
    switch (flag) {
1390
#ifndef DISABLE_MOUSE
1391
	case USE_MOUSE:
1392
1393
	    mouse_init();
	    break;
1394
#endif
1395
1396
	case MORE_SPACE:
	case NO_HELP:
1397
	    window_init();
1398
	    total_refresh();
1399
	    break;
1400
	case SUSPEND:
1401
	    signal_init();
1402
	    break;
1403
	case WHITESPACE_DISPLAY:
1404
	    titlebar(NULL);
1405
1406
	    edit_refresh();
	    break;
1407
#ifndef DISABLE_COLOR
1408
	case NO_COLOR_SYNTAX:
1409
1410
1411
#endif
#ifdef ENABLE_LINENUMBERS
	case LINE_NUMBERS:
1412
#endif
1413
	case SOFTWRAP:
1414
	    refresh_needed = TRUE;
1415
	    break;
1416
    }
Chris Allegretta's avatar
Chris Allegretta committed
1417

1418
    enabled = ISSET(flag);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1419

1420
    if (flag == NO_HELP
1421
#ifndef DISABLE_WRAPPING
1422
	|| flag == NO_WRAP
1423
#endif
1424
#ifndef DISABLE_COLOR
1425
	|| flag == NO_COLOR_SYNTAX
1426
1427
#endif
	)
Chris Allegretta's avatar
Chris Allegretta committed
1428
	enabled = !enabled;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1429

1430
1431
    statusline(HUSH, "%s %s", _(flagtostr(flag)),
		enabled ? _("enabled") : _("disabled"));
Chris Allegretta's avatar
Chris Allegretta committed
1432
}
1433

1434
/* Bleh. */
1435
1436
void do_toggle_void(void)
{
1437
    ;
1438
}
1439
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
1440

1441
1442
/* Disable extended input and output processing in our terminal
 * settings. */
1443
void disable_extended_io(void)
1444
1445
1446
1447
1448
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag &= ~IEXTEN;
1449
    term.c_oflag &= ~OPOST;
1450
1451
1452
    tcsetattr(0, TCSANOW, &term);
}

1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
/* 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);
}

1464
#ifndef NANO_TINY
1465
1466
/* Enable interpretation of the special control keys in our terminal
 * settings. */
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
void enable_signals(void)
{
    struct termios term;

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

1477
1478
/* Disable interpretation of the flow control characters in our terminal
 * settings. */
1479
1480
1481
1482
1483
void disable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1484
    term.c_iflag &= ~IXON;
1485
1486
1487
    tcsetattr(0, TCSANOW, &term);
}

1488
1489
/* Enable interpretation of the flow control characters in our terminal
 * settings. */
1490
1491
1492
1493
1494
void enable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1495
    term.c_iflag |= IXON;
1496
1497
1498
    tcsetattr(0, TCSANOW, &term);
}

1499
1500
1501
1502
1503
1504
1505
1506
/* 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. */
1507
1508
void terminal_init(void)
{
1509
1510
1511
1512
#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
1513
1514
1515
     * 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. */
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
    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();
1530
#ifdef USE_SLANG
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1531
1532
	if (!ISSET(PRESERVE))
	    disable_flow_control();
1533
1534
1535
1536
1537
1538

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

1541
1542
1543
/* Ask ncurses for a keycode, or assign a default one. */
int get_keycode(const char *keyname, const int standard)
{
1544
#ifdef HAVE_KEY_DEFINED
1545
1546
    const char *keyvalue = tigetstr(keyname);

1547
    if (keyvalue != 0 && keyvalue != (char *)-1)
1548
	return key_defined(keyvalue);
1549
    else
1550
#endif
1551
1552
	return standard;
}
1553

1554
1555
1556
/* Say that an unbound key was struck, and if possible which one. */
void unbound_key(int code)
{
1557
    if (!is_byte(code))
1558
	statusline(ALERT, _("Unbound key"));
1559
    else if (meta_key) {
1560
1561
1562
1563
	if (code == '[')
	    statusline(ALERT, _("Unbindable key: M-["));
	else
	    statusline(ALERT, _("Unbound key: M-%c"), toupper(code));
1564
    } else if (code < 0x20)
1565
	statusline(ALERT, _("Unbound key: ^%c"), code + 0x40);
1566
    else
1567
	statusline(ALERT, _("Unbound key: %c"), code);
1568
1569
}

1570
1571
1572
/* Read in a keystroke.  Act on the keystroke if it is a shortcut or a toggle;
 * otherwise, insert it into the edit buffer.  If allow_funcs is FALSE, don't
 * do anything with the keystroke -- just return it. */
1573
int do_input(bool allow_funcs)
1574
1575
{
    int input;
1576
	/* The keystroke we read in: a character or a shortcut. */
1577
    static char *puddle = NULL;
1578
1579
	/* The input buffer for actual characters. */
    static size_t depth = 0;
1580
	/* The length of the input buffer. */
1581
    bool preserve = FALSE;
1582
	/* Whether to preserve the contents of the cutbuffer. */
1583
    const sc *s;
1584
1585
    bool have_shortcut;

1586
    /* Read in a keystroke. */
1587
    input = get_kbinput(edit);
1588

1589
1590
1591
1592
1593
#ifndef NANO_TINY
    if (input == KEY_WINCH)
	return KEY_WINCH;
#endif

1594
#ifndef DISABLE_MOUSE
1595
    if (input == KEY_MOUSE) {
1596
	/* We received a mouse click. */
1597
	if (do_mouse() == 1)
1598
1599
	    /* The click was on a shortcut -- read in the character
	     * that it was converted into. */
1600
	    input = get_kbinput(edit);
1601
1602
	else
	    /* The click was invalid or has been handled -- get out. */
1603
	    return ERR;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1604
    }
1605
1606
1607
#endif

    /* Check for a shortcut in the main list. */
1608
    s = get_shortcut(&input);
1609
1610
1611

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

1614
1615
    /* 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. */
1616
    if (!have_shortcut) {
1617
	if (is_ascii_cntrl_char(input) || meta_key || !is_byte(input)) {
1618
	    unbound_key(input);
1619
	    input = ERR;
1620
1621
1622
	}
    }

1623
1624
1625
    if (!allow_funcs)
	return input;

1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
    /* If the keystroke isn't a shortcut nor a toggle, it's a normal text
     * character: add the character to the input buffer -- or display a
     * warning when we're in view mode. */
     if (input != ERR && !have_shortcut) {
	if (ISSET(VIEW_MODE))
	    print_view_warning();
	else {
	    /* Store the byte, and leave room for a terminating zero. */
	    puddle = charealloc(puddle, depth + 2);
	    puddle[depth++] = (char)input;
1636
	}
1637
1638
1639
1640
1641
1642
#ifndef NANO_TINY
	if (openfile->mark_set && openfile->kind_of_mark == SOFTMARK) {
	    openfile->mark_set = FALSE;
	    refresh_needed = TRUE;
	}
#endif
1643
    }
1644

1645
1646
1647
1648
1649
    /* 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 output
     * all available characters in the input puddle.  Note that this
     * puddle will be empty if we're in view mode. */
    if (have_shortcut || get_key_buffer_len() == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1650
#ifndef DISABLE_WRAPPING
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
	/* If we got a shortcut or toggle, and it's not the shortcut
	 * for verbatim input, turn off prepending of wrapped text. */
	if (have_shortcut && s->scfunc != do_verbatim_input)
	    wrap_reset();
#endif

	if (puddle != NULL) {
	    /* Insert all bytes in the input buffer into the edit buffer
	     * at once, filtering out any low control codes. */
	    puddle[depth] = '\0';
	    do_output(puddle, depth, FALSE);

	    /* Empty the input buffer. */
	    free(puddle);
	    puddle = NULL;
	    depth = 0;
1667
	}
1668
    }
1669

1670
1671
    if (have_shortcut) {
	const subnfunc *f = sctofunc(s);
1672

1673
1674
1675
1676
	if (ISSET(VIEW_MODE) && f && !f->viewok) {
	    print_view_warning();
	    return ERR;
	}
1677

1678
1679
1680
	/* If the function associated with this shortcut is
	 * cutting or copying text, remember this. */
	if (s->scfunc == do_cut_text_void
1681
#ifndef NANO_TINY
1682
		|| s->scfunc == do_copy_text || s->scfunc == do_cut_till_eof
1683
#endif
1684
		)
1685
	    preserve = TRUE;
1686

1687
#ifndef NANO_TINY
1688
1689
1690
1691
1692
	if (s->scfunc == do_toggle_void) {
	    do_toggle(s->toggle);
	    if (s->toggle != CUT_TO_END)
		preserve = TRUE;
	} else
1693
#endif
1694
	{
1695
#ifndef NANO_TINY
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
	    /* If Shifted movement occurs, set the mark. */
	    if (shift_held && !openfile->mark_set) {
		openfile->mark_set = TRUE;
		openfile->mark_begin = openfile->current;
		openfile->mark_begin_x = openfile->current_x;
		openfile->kind_of_mark = SOFTMARK;
	    }
#endif
	    /* Execute the function of the shortcut. */
	    s->scfunc();
1706
#ifndef NANO_TINY
1707
1708
	    /* If Shiftless movement occurred, discard a soft mark. */
	    if (openfile->mark_set && !shift_held &&
1709
				openfile->kind_of_mark == SOFTMARK) {
1710
1711
1712
1713
		openfile->mark_set = FALSE;
		openfile->mark_begin = NULL;
		refresh_needed = TRUE;
	    }
1714
#endif
1715
#ifndef DISABLE_COLOR
1716
1717
	    if (f && !f->viewok)
		reset_multis(openfile->current, FALSE);
1718
#endif
1719
	    if (!refresh_needed && (s->scfunc == do_delete || s->scfunc == do_backspace))
1720
		update_line(openfile->current, openfile->current_x);
1721
	}
1722
    }
1723

1724
1725
1726
    /* 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)
1727
1728
	cutbuffer_reset();

1729
1730
1731
    return input;
}

1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
void xon_complaint(void)
{
    statusbar(_("XON ignored, mumble mumble"));
}

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


1743
#ifndef DISABLE_MOUSE
1744
/* Handle a mouse click on the edit window or the shortcut list. */
1745
int do_mouse(void)
1746
1747
{
    int mouse_x, mouse_y;
1748
    int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
1749

1750
1751
1752
1753
    if (retval != 0)
	/* The click is wrong or already handled. */
	return retval;

1754
    /* We can click on the edit window to move the cursor. */
1755
    if (wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) {
1756
1757
1758
	bool sameline;
	    /* Did they click on the line with the cursor?  If they
	     * clicked on the cursor, we set the mark. */
1759
	filestruct *current_save = openfile->current;
1760
#ifndef NANO_TINY
1761
	size_t current_x_save = openfile->current_x;
1762
#endif
1763
1764
1765

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

1766
#ifdef DEBUG
1767
	fprintf(stderr, "mouse_y = %d, current_y = %ld\n", mouse_y, (long)openfile->current_y);
1768
1769
#endif

1770
#ifndef NANO_TINY
1771
	if (ISSET(SOFTWRAP)) {
1772
	    size_t i = 0;
1773
	    for (openfile->current = openfile->edittop;
1774
1775
			openfile->current->next && i < mouse_y;
			openfile->current = openfile->current->next, i++) {
1776
1777
1778
1779
		openfile->current_y = i;
		i += strlenpt(openfile->current->data) / COLS;
	    }
#ifdef DEBUG
1780
1781
	    fprintf(stderr, "do_mouse(): moving to current_y = %ld, index i = %lu\n",
			(long)openfile->current_y, (unsigned long)i);
1782
1783
1784
1785
1786
1787
1788
	    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
1789
1790
		fprintf(stderr, "do_mouse(): i > mouse_y, mouse_x = %d, current_x to = %lu\n",
			mouse_x, (unsigned long)openfile->current_x);
1791
1792
#endif
	    } else {
1793
		openfile->current_x = actual_x(openfile->current->data, mouse_x);
1794
#ifdef DEBUG
1795
1796
		fprintf(stderr, "do_mouse(): i <= mouse_y, mouse_x = %d, setting current_x to = %lu\n",
			mouse_x, (unsigned long)openfile->current_x);
1797
1798
#endif
	    }
1799
1800
1801
	} else
#endif /* NANO_TINY */
	{
1802
1803
	    /* Move to where the click occurred. */
	    for (; openfile->current_y < mouse_y && openfile->current !=
1804
			openfile->filebot; openfile->current_y++)
1805
1806
		openfile->current = openfile->current->next;
	    for (; openfile->current_y > mouse_y && openfile->current !=
1807
			openfile->fileage; openfile->current_y--)
1808
1809
1810
		openfile->current = openfile->current->prev;

	    openfile->current_x = actual_x(openfile->current->data,
1811
		get_page_start(xplustabs()) + mouse_x);
1812
	}
1813

1814
#ifndef NANO_TINY
1815
1816
1817
1818
1819
	/* 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();
1820
	else
1821
#endif
1822
1823
	    /* The cursor moved; clean the cutbuffer on the next cut. */
	    cutbuffer_reset();
1824

1825
	edit_redraw(current_save);
1826
1827
    }

1828
    /* No more handling is needed. */
1829
    return 2;
1830
1831
1832
}
#endif /* !DISABLE_MOUSE */

1833
/* The user typed output_len multibyte characters.  Add them to the edit
1834
 * buffer, filtering out all ASCII control characters if allow_cntrls is
1835
1836
 * TRUE. */
void do_output(char *output, size_t output_len, bool allow_cntrls)
1837
{
1838
1839
1840
1841
1842
    size_t current_len, i = 0;
#ifndef NANO_TINY
    size_t orig_lenpt = 0;
#endif

1843
1844
    char *char_buf = charalloc(mb_cur_max());
    int char_buf_len;
1845

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

1848
    current_len = strlen(openfile->current->data);
1849
1850

#ifndef NANO_TINY
1851
1852
    if (ISSET(SOFTWRAP))
	orig_lenpt = strlenpt(openfile->current->data);
1853
#endif
1854

1855
    while (i < output_len) {
1856
1857
	/* If control codes are allowed, encode a verbatim null as a newline,
	 * and let a verbatim ^J create a whole new line. */
1858
1859
	if (allow_cntrls) {
	    if (output[i] == '\0')
1860
		output[i] = '\n';
1861
	    else if (output[i] == '\n') {
1862
		do_enter();
1863
1864
1865
		i++;
		continue;
	    }
1866
1867
	}

1868
	/* Get the next multibyte character. */
1869
	char_buf_len = parse_mbchar(output + i, char_buf, NULL);
1870
1871

	i += char_buf_len;
1872

1873
	/* If controls are not allowed, ignore an ASCII control character. */
1874
	if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -	char_buf_len)))
1875
1876
	    continue;

1877
	/* If we're adding to the magicline, create a new magicline. */
1878
	if (!ISSET(NO_NEWLINES) && openfile->filebot == openfile->current) {
1879
	    new_magicline();
1880
1881
1882
	    if (margin > 0)
		refresh_needed = TRUE;
	}
1883

1884
	assert(openfile->current_x <= current_len);
1885

1886
1887
1888
	/* Make room for the new character and copy it into the line. */
	openfile->current->data = charealloc(openfile->current->data,
					current_len + char_buf_len + 1);
1889
1890
	charmove(openfile->current->data + openfile->current_x + char_buf_len,
			openfile->current->data + openfile->current_x,
1891
			current_len - openfile->current_x + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1892
	strncpy(openfile->current->data + openfile->current_x, char_buf,
1893
			char_buf_len);
1894
	current_len += char_buf_len;
1895
	openfile->totsize++;
1896
1897
	set_modified();

1898
#ifndef NANO_TINY
1899
	add_undo(ADD);
1900

1901
	/* Note that current_x has not yet been incremented. */
1902
1903
	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
1904
	    openfile->mark_begin_x += char_buf_len;
1905
1906
#endif

1907
	openfile->current_x += char_buf_len;
1908

1909
1910
1911
1912
#ifndef NANO_TINY
	update_undo(ADD);
#endif

1913
#ifndef DISABLE_WRAPPING
1914
	/* If we're wrapping text, we need to call edit_refresh(). */
1915
	if (!ISSET(NO_WRAP))
1916
	    if (do_wrap(openfile->current))
1917
		refresh_needed = TRUE;
1918
#endif
1919
    }
1920

1921
#ifndef NANO_TINY
1922
1923
    ensure_line_is_visible();

1924
1925
    /* If the number of screen rows that a softwrapped line occupies
     * has changed, we need a full refresh. */
1926
    if (ISSET(SOFTWRAP) && refresh_needed == FALSE)
1927
	if (strlenpt(openfile->current->data) / editwincols != orig_lenpt / editwincols)
1928
	    refresh_needed = TRUE;
1929
#endif
1930

1931
    free(char_buf);
1932

1933
1934
    openfile->placewewant = xplustabs();

1935
#ifndef DISABLE_COLOR
1936
    reset_multis(openfile->current, FALSE);
1937
#endif
1938

1939
    if (!refresh_needed)
1940
	update_line(openfile->current, openfile->current_x);
1941
1942
}

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

2036
2037
2038
    /* Back up the terminal settings so that they can be restored. */
    tcgetattr(0, &oldterm);

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
2070
2071
2072
    /* If the executable's name starts with 'r', activate restricted mode. */
    if (*(tail(argv[0])) == 'r')
	SET(RESTRICTED);

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

2290
2291
2292
    /* 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. */
2293
2294
2295
    if (ISSET(RESTRICTED)) {
	UNSET(SUSPEND);
	UNSET(BACKUP_FILE);
2296
#ifndef DISABLE_NANORC
2297
	no_rcfiles = TRUE;
2298
2299
	UNSET(HISTORYLOG);
	UNSET(POS_HISTORY);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2300
#endif
2301
2302
    }

2303
2304
2305
    /* 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();
2306

Chris Allegretta's avatar
Chris Allegretta committed
2307
/* We've read through the command line options.  Now back up the flags
2308
2309
 * and values that are set, and read the rcfile(s).  If the values
 * haven't changed afterward, restore the backed-up values. */
2310
#ifndef DISABLE_NANORC
2311
    if (!no_rcfiles) {
Chris Allegretta's avatar
Chris Allegretta committed
2312
2313
2314
#ifndef DISABLE_OPERATINGDIR
	char *operating_dir_cpy = operating_dir;
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2315
#ifndef DISABLE_WRAPJUSTIFY
2316
	ssize_t wrap_at_cpy = wrap_at;
Chris Allegretta's avatar
Chris Allegretta committed
2317
#endif
2318
#ifndef NANO_TINY
2319
	char *backup_dir_cpy = backup_dir;
2320
	char *word_chars_cpy = word_chars;
2321
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2322
2323
2324
2325
2326
2327
#ifndef DISABLE_JUSTIFY
	char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
	char *alt_speller_cpy = alt_speller;
#endif
2328
	ssize_t tabsize_cpy = tabsize;
2329
2330
2331
2332
	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
2333

2334
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
2335
	operating_dir = NULL;
2336
#endif
2337
#ifndef NANO_TINY
2338
	backup_dir = NULL;
2339
	word_chars = NULL;
2340
#endif
2341
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
2342
	quotestr = NULL;
2343
2344
#endif
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
2345
	alt_speller = NULL;
2346
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2347

2348
	do_rcfiles();
Chris Allegretta's avatar
Chris Allegretta committed
2349

2350
#ifdef DEBUG
2351
2352
	fprintf(stderr, "After rebinding keys...\n");
	print_sclist();
2353
2354
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2355
2356
2357
2358
2359
2360
#ifndef DISABLE_OPERATINGDIR
	if (operating_dir_cpy != NULL) {
	    free(operating_dir);
	    operating_dir = operating_dir_cpy;
	}
#endif
2361
#ifndef DISABLE_WRAPJUSTIFY
2362
	if (fill_used)
Chris Allegretta's avatar
Chris Allegretta committed
2363
2364
	    wrap_at = wrap_at_cpy;
#endif
2365
#ifndef NANO_TINY
2366
2367
2368
2369
	if (backup_dir_cpy != NULL) {
	    free(backup_dir);
	    backup_dir = backup_dir_cpy;
	}
2370
2371
2372
2373
	if (word_chars_cpy != NULL) {
	    free(word_chars);
	    word_chars = word_chars_cpy;
	}
2374
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
#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
2387
	if (tabsize_cpy != -1)
Chris Allegretta's avatar
Chris Allegretta committed
2388
	    tabsize = tabsize_cpy;
2389
2390
2391

	for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
	    flags[i] |= flags_cpy[i];
Chris Allegretta's avatar
Chris Allegretta committed
2392
    }
2393
2394
2395
2396
2397
2398
#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
2399
#endif /* !DISABLE_NANORC */
Chris Allegretta's avatar
Chris Allegretta committed
2400

2401
#ifndef DISABLE_WRAPPING
2402
2403
2404
    /* 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)
2405
	UNSET(NO_WRAP);
2406
#endif
2407

2408
2409
2410
    /* If we're using bold text instead of reverse video text, set it up
     * now. */
    if (ISSET(BOLD_TEXT))
2411
	hilite_attribute = A_BOLD;
2412

2413
#ifndef DISABLE_HISTORIES
2414
    /* Set up the search/replace history. */
2415
    history_init();
2416
    /* Verify that the home directory and ~/.nano subdir exist. */
2417
2418
2419
2420
2421
    if (ISSET(HISTORYLOG) || ISSET(POS_HISTORY)) {
	get_homedir();
	if (homedir == NULL || check_dotnano() == 0) {
	    UNSET(HISTORYLOG);
	    UNSET(POS_HISTORY);
2422
2423
	}
    }
2424
2425
2426
2427
    if (ISSET(HISTORYLOG))
	load_history();
    if (ISSET(POS_HISTORY))
	load_poshistory();
2428
#endif /* !DISABLE_HISTORIES */
2429

2430
#ifndef NANO_TINY
2431
    /* Set up the backup directory (unless we're using restricted mode,
2432
2433
2434
2435
     * 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. */
2436
2437
    if (!ISSET(RESTRICTED))
	init_backup_dir();
2438
#endif
2439

2440
2441
2442
2443
2444
2445
#ifndef DISABLE_OPERATINGDIR
    /* Set up the operating directory.  This entails chdir()ing there,
     * so that file reads and writes will be based there. */
    init_operating_dir();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2446
#ifndef DISABLE_JUSTIFY
2447
    /* If punct wasn't specified, set its default value. */
2448
    if (punct == NULL)
2449
	punct = mallocstrcpy(NULL, "!.?");
2450

2451
    /* If brackets wasn't specified, set its default value. */
2452
    if (brackets == NULL)
2453
	brackets = mallocstrcpy(NULL, "\"')>]}");
2454

2455
    /* If quotestr wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2456
    if (quotestr == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2457
	quotestr = mallocstrcpy(NULL,
Chris Allegretta's avatar
Chris Allegretta committed
2458
#ifdef HAVE_REGEX_H
2459
		"^([ \t]*[#:>|}])+"
Chris Allegretta's avatar
Chris Allegretta committed
2460
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2461
		"> "
Chris Allegretta's avatar
Chris Allegretta committed
2462
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2463
		);
2464
#ifdef HAVE_REGEX_H
2465
    quoterc = regcomp(&quotereg, quotestr, NANO_REG_EXTENDED);
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479

    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
2480
#endif /* !DISABLE_JUSTIFY */
2481

2482
2483
#ifndef DISABLE_SPELLER
    /* If we don't have an alternative spell checker after reading the
2484
     * command line and/or rcfile(s), check $SPELL for one, as Pico
2485
     * does (unless we're using restricted mode, in which case spell
2486
2487
     * checking is disabled, since it would allow reading from or
     * writing to files not specified on the command line). */
2488
    if (!ISSET(RESTRICTED) && alt_speller == NULL) {
2489
2490
2491
2492
2493
2494
	char *spellenv = getenv("SPELL");
	if (spellenv != NULL)
	    alt_speller = mallocstrcpy(NULL, spellenv);
    }
#endif

2495
2496
2497
2498
2499
#ifndef NANO_TINY
    /* If matchbrackets wasn't specified, set its default value. */
    if (matchbrackets == NULL)
	matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");

2500
2501
2502
2503
    /* 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 ".". */
2504
    if (whitespace == NULL) {
2505
#ifdef ENABLE_UTF8
2506
	if (using_utf8()) {
2507
	    whitespace = mallocstrcpy(NULL, "\xC2\xBB\xC2\xB7");
2508
2509
	    whitespace_len[0] = 2;
	    whitespace_len[1] = 2;
2510
2511
2512
	} else
#endif
	{
2513
2514
2515
2516
	    whitespace = mallocstrcpy(NULL, ">.");
	    whitespace_len[0] = 1;
	    whitespace_len[1] = 1;
	}
2517
    }
Benno Schulenberg's avatar
Benno Schulenberg committed
2518
#endif /* !NANO_TINY */
2519

2520
    /* Initialize the search string. */
2521
2522
    last_search = mallocstrcpy(NULL, "");

2523
    /* If tabsize wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2524
    if (tabsize == -1)
2525
	tabsize = WIDTH_OF_TAB;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2526

2527
2528
2529
    /* Initialize curses mode.  If this fails, get out. */
    if (initscr() == NULL)
	exit(1);
2530
2531

    /* Set up the terminal state. */
2532
    terminal_init();
2533

2534
#ifdef __linux__
2535
2536
    /* Check whether we're running on a Linux console. */
    console = (getenv("DISPLAY") == NULL);
2537
#endif
2538

2539
2540
2541
#ifdef DEBUG
    fprintf(stderr, "Main: set up windows\n");
#endif
2542

2543
2544
2545
    /* Initialize all the windows based on the current screen
     * dimensions. */
    window_init();
2546

2547
2548
    editwincols = COLS;

2549
    /* Set up the signal handlers. */
2550
    signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
2551

2552
#ifndef DISABLE_MOUSE
2553
    /* Initialize mouse support. */
2554
    mouse_init();
2555
#endif
2556

2557
2558
2559
#ifndef DISABLE_COLOR
    set_colorpairs();
#else
2560
    interface_color_pair[TITLE_BAR] = hilite_attribute;
2561
    interface_color_pair[LINE_NUMBER] = hilite_attribute;
2562
2563
2564
    interface_color_pair[STATUS_BAR] = hilite_attribute;
    interface_color_pair[KEY_COMBO] = hilite_attribute;
    interface_color_pair[FUNCTION_TAG] = A_NORMAL;
2565
2566
#endif

2567
    /* Ask ncurses for the key codes for Control+Left/Right/Up/Down. */
2568
2569
2570
2571
    controlleft = get_keycode("kLFT5", CONTROL_LEFT);
    controlright = get_keycode("kRIT5", CONTROL_RIGHT);
    controlup = get_keycode("kUP5", CONTROL_UP);
    controldown = get_keycode("kDN5", CONTROL_DOWN);
2572
#ifndef NANO_TINY
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
    /* Ask for the codes for Shift+Control+Left/Right/Up/Down. */
    shiftcontrolleft = get_keycode("kLFT6", SHIFT_CONTROL_LEFT);
    shiftcontrolright = get_keycode("kRIT6", SHIFT_CONTROL_RIGHT);
    shiftcontrolup = get_keycode("kUP6", SHIFT_CONTROL_UP);
    shiftcontroldown = get_keycode("kDN6", SHIFT_CONTROL_DOWN);
    /* Ask for the codes for Shift+Alt+Left/Right/Up/Down. */
    shiftaltleft = get_keycode("kLFT4", SHIFT_ALT_LEFT);
    shiftaltright = get_keycode("kRIT4", SHIFT_ALT_RIGHT);
    shiftaltup = get_keycode("kUP4", SHIFT_ALT_UP);
    shiftaltdown = get_keycode("kDN4", SHIFT_ALT_DOWN);
2583
2584
#endif

2585
2586
2587
2588
2589
#ifndef USE_SLANG
    /* Tell ncurses to pass the Esc key quickly. */
    set_escdelay(50);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2590
#ifdef DEBUG
2591
    fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
2592
#endif
2593

2594
2595
2596
    /* 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. */
2597
    if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
2598
	parse_line_column(&argv[optind][1], &startline, &startcol);
2599
2600
2601
	optind++;
    }

2602
    /* If one of the arguments is a dash, read text from standard input. */
2603
    if (optind < argc && !strcmp(argv[optind], "-")) {
2604
	stdin_pager();
2605
2606
2607
2608
	set_modified();
	optind++;
    }

2609
#ifndef DISABLE_MULTIBUFFER
2610
2611
2612
2613
2614
2615
    old_multibuffer = ISSET(MULTIBUFFER);
    SET(MULTIBUFFER);

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

2619
	for (; i < argc; i++) {
2620
2621
	    /* If there's a +LINE or +LINE,COLUMN flag here, it is followed
	     * by at least one other argument: the filename it applies to. */
2622
	    if (i < argc - 1 && argv[i][0] == '+')
2623
		parse_line_column(&argv[i][1], &iline, &icol);
2624
	    else {
2625
2626
2627
		/* If opening fails, don't try to position the cursor. */
		if (!open_buffer(argv[i], FALSE))
		    continue;
2628

2629
		/* If a position was given on the command line, go there. */
2630
		if (iline > 0 || icol > 0) {
2631
		    do_gotolinecolumn(iline, icol, FALSE, FALSE);
2632
2633
		    iline = 0;
		    icol = 0;
2634
		}
2635
#ifndef DISABLE_HISTORIES
2636
		else if (ISSET(POS_HISTORY)) {
2637
		    ssize_t savedposline, savedposcol;
2638
		    /* If edited before, restore the last cursor position. */
2639
		    if (check_poshistory(argv[i], &savedposline, &savedposcol))
2640
			do_gotolinecolumn(savedposline, savedposcol,
2641
						FALSE, FALSE);
2642
		}
2643
#endif
2644
2645
	    }
	}
2646
    }
Benno Schulenberg's avatar
Benno Schulenberg committed
2647
#endif /* !DISABLE_MULTIBUFFER */
2648

2649
    /* Now read the first file on the command line into a new buffer. */
2650
    if (optind < argc)
2651
	open_buffer(argv[optind], FALSE);
2652

2653
2654
2655
    /* 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. */
2656
    if (openfile == NULL) {
2657
	open_buffer("", FALSE);
2658
	UNSET(VIEW_MODE);
Chris Allegretta's avatar
Chris Allegretta committed
2659
    }
2660

2661
#ifndef DISABLE_MULTIBUFFER
2662
2663
    if (!old_multibuffer)
	UNSET(MULTIBUFFER);
Chris Allegretta's avatar
Chris Allegretta committed
2664
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2665

2666
    /* If a starting position was given on the command line, go there. */
2667
    if (startline > 0 || startcol > 0)
2668
	do_gotolinecolumn(startline, startcol, FALSE, FALSE);
2669
#ifndef DISABLE_HISTORIES
2670
    else if (ISSET(POS_HISTORY)) {
2671
	ssize_t savedposline, savedposcol;
2672
	/* If the file was edited before, restore the last cursor position. */
2673
	if (check_poshistory(argv[optind], &savedposline, &savedposcol))
2674
	    do_gotolinecolumn(savedposline, savedposcol, FALSE, FALSE);
2675
    }
2676
#endif
2677

2678
#ifdef DEBUG
2679
    fprintf(stderr, "Main: show buffer contents, and enter main loop\n");
2680
2681
#endif

2682
    display_buffer();
Robert Siemborski's avatar
Robert Siemborski committed
2683

2684
    while (TRUE) {
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
#ifdef ENABLE_LINENUMBERS
	int needed_margin = digits(openfile->filebot->lineno) + 1;

	/* Only enable line numbers when there is enough room for them. */
	if (ISSET(LINE_NUMBERS) && needed_margin < COLS - 3) {
	    if (needed_margin != margin) {
		margin = needed_margin;
		editwincols = COLS - margin;
		/* The margin has changed -- schedule a full refresh. */
		refresh_needed = TRUE;
	    }
2696
2697
2698
	} else
#endif
	{
2699
2700
2701
	    margin = 0;
	    editwincols = COLS;
	}
2702

2703
2704
2705
	if (currmenu != MMAIN)
	    display_main_list();

2706
2707
2708
2709
2710
2711
2712
	lastmessage = HUSH;

	/* Update the displayed current cursor position only when there
	 * are no keys waiting in the input buffer. */
	if (ISSET(CONST_UPDATE) && get_key_buffer_len() == 0)
	    do_cursorpos(TRUE);

2713
2714
2715
2716
2717
2718
2719
2720
	/* Refresh either just the cursor or the entire edit window. */
	if (!refresh_needed) {
	    reset_cursor();
	    curs_set(1);
	    wnoutrefresh(edit);
	} else
	    edit_refresh();

2721
	focusing = TRUE;
2722

2723
2724
2725
	/* Forget any earlier statusbar x position. */
	reinit_statusbar_x();

2726
	/* Read in and interpret keystrokes. */
2727
	do_input(TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
2728
    }
2729

2730
    /* We should never get here. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2731
    assert(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2732
}