nano.c 71.1 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-2011, 2013-2018 Free Software Foundation, Inc.    *
5
 *   Copyright (C) 2014-2017 Benno Schulenberg                            *
6
 *                                                                        *
7
8
9
10
 *   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
11
 *                                                                        *
12
13
14
15
 *   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
16
17
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
18
 *   along with this program.  If not, see http://www.gnu.org/licenses/.  *
Chris Allegretta's avatar
Chris Allegretta committed
19
20
21
 *                                                                        *
 **************************************************************************/

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

Chris Allegretta's avatar
Chris Allegretta committed
25
#include <ctype.h>
26
27
28
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
29
#if defined(__linux__) || !defined(NANO_TINY)
30
31
#include <sys/ioctl.h>
#endif
32
#ifdef ENABLE_UTF8
33
#include <langinfo.h>
34
#endif
35
#include <locale.h>
36
#include <string.h>
37
#ifdef HAVE_TERMIOS_H
38
#include <termios.h>
39
#endif
40
#include <unistd.h>
41
42
43
#ifdef __linux__
#include <sys/vt.h>
#endif
44

45
46
47
48
49
50
#ifdef ENABLE_MULTIBUFFER
#define read_them_all TRUE
#else
#define read_them_all FALSE
#endif

51
#ifdef ENABLE_MOUSE
52
static int oldinterval = -1;
53
		/* Used to store the user's original mouse click interval. */
54
#endif
55
#ifdef ENABLE_NANORC
56
static bool no_rcfiles = FALSE;
57
		/* Should we ignore all rcfiles? */
58
#endif
59
#ifdef HAVE_TERMIOS_H
60
static struct termios oldterm;
61
		/* The user's original terminal settings. */
62
63
64
65
#else
# define tcsetattr(...)
# define tcgetattr(...)
#endif
66
static struct sigaction act;
67
		/* Used to set up all our fun signal handlers. */
Chris Allegretta's avatar
Chris Allegretta committed
68

69
static bool input_was_aborted = FALSE;
70
		/* Whether reading from standard input was aborted via ^C. */
71

72
/* Create a new linestruct node.  Note that we do not set prevnode->next. */
73
filestruct *make_new_node(filestruct *prevnode)
74
{
75
	filestruct *newnode = nmalloc(sizeof(filestruct));
76

77
78
79
80
	newnode->data = NULL;
	newnode->prev = prevnode;
	newnode->next = NULL;
	newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1;
81

82
#ifdef ENABLE_COLOR
83
	newnode->multidata = NULL;
84
85
#endif

86
	return newnode;
87
88
}

89
/* Make a copy of a linestruct node. */
90
filestruct *copy_node(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
91
{
92
	filestruct *dst = nmalloc(sizeof(filestruct));
93

94
95
96
97
	dst->data = mallocstrcpy(NULL, src->data);
	dst->next = src->next;
	dst->prev = src->prev;
	dst->lineno = src->lineno;
98

99
#ifdef ENABLE_COLOR
100
	dst->multidata = NULL;
101
#endif
102

103
	return dst;
Chris Allegretta's avatar
Chris Allegretta committed
104
105
}

106
/* Splice a new node into an existing linked list of linestructs. */
107
void splice_node(filestruct *afterthis, filestruct *newnode)
Chris Allegretta's avatar
Chris Allegretta committed
108
{
109
110
111
112
113
114
115
116
117
	newnode->next = afterthis->next;
	newnode->prev = afterthis;
	if (afterthis->next != NULL)
		afterthis->next->prev = newnode;
	afterthis->next = newnode;

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

120
/* Disconnect a node from a linked list of linestructs and delete it. */
121
void unlink_node(filestruct *fileptr)
122
{
123
124
125
126
	if (fileptr->prev != NULL)
		fileptr->prev->next = fileptr->next;
	if (fileptr->next != NULL)
		fileptr->next->prev = fileptr->prev;
127

128
129
130
	/* Update filebot when removing a node at the end of file. */
	if (openfile && openfile->filebot == fileptr)
		openfile->filebot = fileptr->prev;
131

132
	delete_node(fileptr);
133
}
134

135
/* Free the data structures in the given node. */
136
137
void delete_node(filestruct *fileptr)
{
138
	free(fileptr->data);
139
#ifdef ENABLE_COLOR
140
	free(fileptr->multidata);
141
#endif
142
	free(fileptr);
143
144
}

145
/* Duplicate an entire linked list of linestructs. */
146
filestruct *copy_filestruct(const filestruct *src)
147
{
148
	filestruct *head, *copy;
149

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

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

160
161
		src = src->next;
	}
162

163
	copy->next = NULL;
164

165
	return head;
Chris Allegretta's avatar
Chris Allegretta committed
166
167
}

168
/* Free an entire linked list of linestructs. */
169
void free_filestruct(filestruct *src)
170
{
171
172
	if (src == NULL)
		return;
173

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

179
	delete_node(src);
180
181
}

182
183
/* Renumber the lines in a buffer, starting with the given line. */
void renumber(filestruct *line)
Chris Allegretta's avatar
Chris Allegretta committed
184
{
185
	ssize_t number;
186

187
	if (line == NULL) {
188
#ifndef NANO_TINY
189
		statusline(ALERT, "Trying to renumber nothing -- please report a bug");
190
#endif
191
		return;
192
	}
Chris Allegretta's avatar
Chris Allegretta committed
193

194
	number = (line->prev == NULL) ? 0 : line->prev->lineno;
195

196
	while (line != NULL) {
197
		line->lineno = ++number;
198
199
		line = line->next;
	}
Chris Allegretta's avatar
Chris Allegretta committed
200
201
}

202
203
/* Partition the current buffer so that it appears to begin at (top, top_x)
 * and appears to end at (bot, bot_x). */
204
partition *partition_filestruct(filestruct *top, size_t top_x,
205
		filestruct *bot, size_t bot_x)
206
{
207
	partition *p = nmalloc(sizeof(partition));
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243

	/* If the top and bottom of the partition are different from the top
	 * and bottom of the buffer, save the latter and then set them
	 * to top and bot. */
	if (top != openfile->fileage) {
		p->fileage = openfile->fileage;
		openfile->fileage = top;
	} else
		p->fileage = NULL;
	if (bot != openfile->filebot) {
		p->filebot = openfile->filebot;
		openfile->filebot = bot;
	} else
		p->filebot = NULL;

	/* 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. */
	p->top_prev = top->prev;
	top->prev = NULL;
	p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
	p->top_data[top_x] = '\0';

	/* 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. */
	p->bot_next = bot->next;
	bot->next = NULL;
	p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);

	/* Remove all text after bot_x at the bottom of the partition. */
	bot->data[bot_x] = '\0';

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

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

246
247
/* Unpartition the current buffer so that it stretches from (fileage, 0)
 * to (filebot, $) again. */
248
249
void unpartition_filestruct(partition **p)
{
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
	/* 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. */
	openfile->fileage->prev = (*p)->top_prev;
	if (openfile->fileage->prev != NULL)
		openfile->fileage->prev->next = openfile->fileage;
	openfile->fileage->data = charealloc(openfile->fileage->data,
				strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
	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));
	free((*p)->top_data);

	/* 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. */
	openfile->filebot->next = (*p)->bot_next;
	if (openfile->filebot->next != NULL)
		openfile->filebot->next->prev = openfile->filebot;
	openfile->filebot->data = charealloc(openfile->filebot->data,
				strlen(openfile->filebot->data) + strlen((*p)->bot_data) + 1);
	strcat(openfile->filebot->data, (*p)->bot_data);
	free((*p)->bot_data);

	/* Restore the top and bottom of the buffer, if they were
	 * different from the top and bottom of the partition. */
	if ((*p)->fileage != NULL)
		openfile->fileage = (*p)->fileage;
	if ((*p)->filebot != NULL)
		openfile->filebot = (*p)->filebot;

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

286
/* Move all the text between (top, top_x) and (bot, bot_x) in the
287
 * current buffer to a new buffer beginning with file_top and ending
288
289
 * with file_bot.  If no text is between (top, top_x) and (bot, bot_x),
 * don't do anything. */
290
void extract_buffer(filestruct **file_top, filestruct **file_bot,
291
		filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
292
{
293
294
	filestruct *top_save;
	bool edittop_inside;
295
#ifndef NANO_TINY
296
297
	bool mark_inside = FALSE;
	bool same_line = FALSE;
298
#endif
299

300
301
302
	/* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
	if (top == bot && top_x == bot_x)
		return;
303

304
305
306
307
308
309
310
	/* Partition the buffer so that it contains only the text from
	 * (top, top_x) to (bot, bot_x), keep track of whether the top of
	 * the edit window is inside the partition, and keep track of
	 * whether the mark begins inside the partition. */
	filepart = partition_filestruct(top, top_x, bot, bot_x);
	edittop_inside = (openfile->edittop->lineno >= openfile->fileage->lineno &&
						openfile->edittop->lineno <= openfile->filebot->lineno);
311
#ifndef NANO_TINY
312
313
314
315
316
317
318
319
	if (openfile->mark) {
		mark_inside = (openfile->mark->lineno >= openfile->fileage->lineno &&
						openfile->mark->lineno <= openfile->filebot->lineno &&
						(openfile->mark != openfile->fileage ||
												openfile->mark_x >= top_x) &&
						(openfile->mark != openfile->filebot ||
												openfile->mark_x <= bot_x));
		same_line = (openfile->mark == openfile->fileage);
320
	}
321
#endif
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
	/* Subtract the number of characters in the text from the file size. */
	openfile->totsize -= get_totsize(top, bot);

	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. */
		*file_top = openfile->fileage;
		*file_bot = openfile->filebot;

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

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

		/* 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. */
		(*file_bot)->next = openfile->fileage->next;
		if ((*file_bot)->next != NULL) {
			(*file_bot)->next->prev = *file_bot;
			*file_bot = openfile->filebot;
		}

		delete_node(openfile->fileage);
355

356
357
		/* Renumber, starting at the last line of the original buffer. */
		renumber(file_bot_save);
358
	}
359

360
361
362
363
	/* Since the text has now been saved, remove it from the buffer. */
	openfile->fileage = make_new_node(NULL);
	openfile->fileage->data = mallocstrcpy(NULL, "");
	openfile->filebot = openfile->fileage;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
364

365
366
367
368
369
	/* 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;
370
#ifndef NANO_TINY
371
372
373
374
375
376
	if (mark_inside) {
		openfile->mark = openfile->current;
		openfile->mark_x = openfile->current_x;
	} else if (same_line)
		/* Update the pointer to this partially cut line. */
		openfile->mark = openfile->current;
377
#endif
378

379
	top_save = openfile->fileage;
380

381
382
383
	/* Unpartition the buffer so that it contains all the text
	 * again, minus the saved text. */
	unpartition_filestruct(&filepart);
384

385
386
387
388
389
390
	/* If the top of the edit window was inside the old partition, put
	 * it in range of current. */
	if (edittop_inside) {
		adjust_viewport(STATIONARY);
		refresh_needed = TRUE;
	}
391

392
393
	/* Renumber, starting with the beginning line of the old partition. */
	renumber(top_save);
394

395
396
397
	/* If the text doesn't end with a magicline, and it should, add one. */
	if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
		new_magicline();
398
}
399

400
/* Meld the given buffer into the current file buffer
401
 * at the current cursor position. */
402
void ingraft_buffer(filestruct *somebuffer)
403
{
404
405
406
	filestruct *top_save;
	size_t current_x_save = openfile->current_x;
	bool edittop_inside;
407
#ifndef NANO_TINY
408
	bool right_side_up = FALSE, single_line = FALSE;
409
#endif
410

411
#ifndef NANO_TINY
412
413
414
415
416
417
418
419
420
421
	/* Keep track of whether the mark begins inside the partition and
	 * will need adjustment. */
	if (openfile->mark) {
		filestruct *top, *bot;
		size_t top_x, bot_x;

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

		single_line = (top == bot);
422
423
	}
#endif
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445

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

	/* Put the top and bottom of the current buffer at the top and
	 * bottom of the passed buffer. */
	openfile->fileage = somebuffer;
	openfile->filebot = openfile->fileage;
	while (openfile->filebot->next != NULL)
		openfile->filebot = openfile->filebot->next;

	/* Put the cursor at the end of the pasted text. */
	openfile->current = openfile->filebot;
	openfile->current_x = strlen(openfile->filebot->data);

	/* Refresh the mark's pointer, and compensate the mark's
	 * x coordinate for the change in the current line. */
	if (openfile->fileage == openfile->filebot) {
446
#ifndef NANO_TINY
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
		if (openfile->mark && single_line) {
			openfile->mark = openfile->current;
			if (!right_side_up)
				openfile->mark_x += openfile->current_x;
		}
#endif
		/* When the pasted stuff contains no newline, adjust the cursor's
		 * x coordinate for the text that is before the pasted stuff. */
		openfile->current_x += current_x_save;
	}
#ifndef NANO_TINY
	else if (openfile->mark && single_line) {
		if (right_side_up)
			openfile->mark = openfile->fileage;
		else {
			openfile->mark = openfile->current;
			openfile->mark_x += openfile->current_x - current_x_save;
		}
465
	}
466
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
467

468
469
	/* 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
470

471
472
473
474
	/* 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. */
	if (edittop_inside)
		openfile->edittop = openfile->fileage;
475

476
	top_save = openfile->fileage;
477

478
479
480
	/* Unpartition the buffer so that it contains all the text
	 * again, plus the copied text. */
	unpartition_filestruct(&filepart);
481

482
483
	/* Renumber, starting with the beginning line of the old partition. */
	renumber(top_save);
484

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

490
491
492
/* Meld a copy of the given buffer into the current file buffer. */
void copy_from_buffer(filestruct *somebuffer)
{
493
	filestruct *the_copy = copy_filestruct(somebuffer);
494

495
	ingraft_buffer(the_copy);
496
497
}

498
/* Create a new openfile node. */
499
500
openfilestruct *make_new_opennode(void)
{
501
	return (openfilestruct *)nmalloc(sizeof(openfilestruct));
502
503
}

504
#ifdef ENABLE_MULTIBUFFER
505
/* Unlink a node from the rest of the circular list, and delete it. */
506
507
void unlink_opennode(openfilestruct *fileptr)
{
508
#ifdef ENABLE_MULTIBUFFER
509
510
	if (fileptr == firstfile)
		firstfile = firstfile->next;
511

512
513
	fileptr->prev->next = fileptr->next;
	fileptr->next->prev = fileptr->prev;
514
#endif
515
	delete_opennode(fileptr);
516
517
}

518
/* Free all the memory in the given open-file node. */
519
520
void delete_opennode(openfilestruct *fileptr)
{
521
522
	free(fileptr->filename);
	free_filestruct(fileptr->fileage);
523
#ifndef NANO_TINY
524
525
526
527
	free(fileptr->current_stat);
	free(fileptr->lock_filename);
	/* Free the undo stack. */
	discard_until(NULL, fileptr, TRUE);
528
#endif
529
	free(fileptr);
530
}
531
#endif
532

533
/* Display a warning about a key disabled in view mode. */
534
void print_view_warning(void)
535
{
536
	statusbar(_("Key is invalid in view mode"));
537
538
}

539
540
541
/* Indicate that something is disabled in restricted mode. */
void show_restricted_warning(void)
{
542
543
	statusbar(_("This function is disabled in restricted mode"));
	beep();
544
545
}

546
#ifndef ENABLE_HELP
547
548
549
/* Indicate that help texts are unavailable. */
void say_there_is_no_help(void)
{
550
	statusbar(_("Help is not available"));
551
552
553
}
#endif

554
/* Make nano exit gracefully. */
555
void finish(void)
Chris Allegretta's avatar
Chris Allegretta committed
556
{
557
558
559
560
561
562
563
	/* Blank the statusbar and (if applicable) the shortcut list,
	 * and move the cursor to the last line of the screen. */
	blank_statusbar();
	blank_bottombars();
	wrefresh(bottomwin);
	curs_set(1);
	endwin();
564

565
566
	/* Restore the old terminal settings. */
	tcsetattr(0, TCSANOW, &oldterm);
567

568
#ifdef ENABLE_HISTORIES
569
570
571
572
573
574
	/* If the user wants history persistence, write the relevant files. */
	if (ISSET(HISTORYLOG))
		save_history();
	if (ISSET(POS_HISTORY)) {
		update_poshistory(openfile->filename, openfile->current->lineno, xplustabs() + 1);
	}
575
576
577
#endif

#ifdef DEBUG
578
	thanks_for_all_the_fish();
579
580
#endif

581
582
	/* Get out. */
	exit(0);
583
584
}

585
/* Make nano die gracefully. */
586
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
587
{
588
	va_list ap;
589
	openfilestruct *firstone = openfile;
Chris Allegretta's avatar
Chris Allegretta committed
590

591
	/* Switch on the cursor and leave curses mode. */
592
593
	curs_set(1);
	endwin();
Chris Allegretta's avatar
Chris Allegretta committed
594

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

598
	/* Display the dying message. */
599
600
601
	va_start(ap, msg);
	vfprintf(stderr, msg, ap);
	va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
602

603
	while (openfile) {
604
#ifndef NANO_TINY
605
606
607
		/* If the current buffer has a lockfile, remove it. */
		if (ISSET(LOCKING) && openfile->lock_filename)
			delete_lockfile(openfile->lock_filename);
608
#endif
609
610
611
612
		/* If the current buffer was modified, ensure it is unpartitioned,
		 * then save it.  When in restricted mode, we don't save anything,
		 * because it would write files not mentioned on the command line. */
		if (openfile->modified && !ISSET(RESTRICTED)) {
613
614
			if (filepart != NULL)
				unpartition_filestruct(&filepart);
615

616
			emergency_save(openfile->filename, openfile->current_stat);
617
		}
Chris Allegretta's avatar
Chris Allegretta committed
618

619
		filepart = NULL;
620
#ifdef ENABLE_MULTIBUFFER
621
		openfile = openfile->next;
622
#endif
623
624
		if (openfile == firstone)
			break;
625
626
	}

627
628
	/* Abandon the building. */
	exit(1);
629
630
}

631
632
633
/* Save the current buffer under the given name.
 * If necessary, the name is modified to be unique. */
void emergency_save(const char *die_filename, struct stat *die_stat)
634
{
635
636
637
	char *targetname;
	bool failed = TRUE;

638
	/* If the buffer has no name, simply call it "nano". */
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
	if (*die_filename == '\0')
		die_filename = "nano";

	targetname = get_next_filename(die_filename, ".save");

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

	if (!failed)
		fprintf(stderr, _("\nBuffer written to %s\n"), targetname);
	else if (*targetname != '\0')
		fprintf(stderr, _("\nBuffer not written to %s: %s\n"), targetname,
				strerror(errno));
	else
		fprintf(stderr, _("\nBuffer not written: %s\n"),
				_("Too many backup files?"));
655

656
#ifndef NANO_TINY
657
658
	/* Try to chmod/chown the saved file to the values of the original file,
	 * but ignore any failure as we are in a hurry to get out. */
659
660
661
662
663
	if (die_stat) {
		IGNORE_CALL_RESULT(chmod(targetname, die_stat->st_mode));
		IGNORE_CALL_RESULT(chown(targetname, die_stat->st_uid,
												die_stat->st_gid));
	}
664
#endif
665

666
	free(targetname);
667
}
668

669
/* Initialize the three window portions nano uses. */
670
void window_init(void)
671
{
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
	/* When resizing, first delete the existing windows. */
	if (edit != NULL) {
		if (topwin != NULL)
			delwin(topwin);
		delwin(edit);
		delwin(bottomwin);
	}

	/* If the terminal is very flat, don't set up a titlebar. */
	if (LINES < 3) {
		topwin = NULL;
		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);

		editwinrows = LINES - toprows - bottomrows;

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

	/* Turn the keypad on for the windows, if necessary. */
	if (!ISSET(REBIND_KEYPAD)) {
		keypad(topwin, TRUE);
		keypad(edit, TRUE);
		keypad(bottomwin, TRUE);
	}
709

710
#ifdef ENABLED_WRAPORJUSTIFY
711
712
713
714
715
716
	/* Set up the wrapping point, accounting for screen width when negative. */
	fill = wrap_at;
	if (fill <= 0)
		fill += COLS;
	if (fill < 0)
		fill = 0;
717
#endif
718
719
}

720
#ifdef ENABLE_MOUSE
721
722
void disable_mouse_support(void)
{
723
724
	mousemask(0, NULL);
	mouseinterval(oldinterval);
725
726
727
728
}

void enable_mouse_support(void)
{
729
730
	mousemask(ALL_MOUSE_EVENTS, NULL);
	oldinterval = mouseinterval(50);
731
732
}

733
/* Switch mouse support on or off, as needed. */
734
void mouse_init(void)
735
{
736
737
738
739
	if (ISSET(USE_MOUSE))
		enable_mouse_support();
	else
		disable_mouse_support();
740
}
741
#endif /* ENABLE_MOUSE */
742

743
/* Print one usage string to the screen.  This cuts down on duplicate
744
 * strings to translate, and leaves out the parts that shouldn't be
745
 * translatable (i.e. the flag names). */
746
void print_opt(const char *shortflag, const char *longflag, const char *desc)
747
{
748
749
750
751
752
753
754
755
756
757
758
759
760
	printf(" %s\t", shortflag);
	if (strlenpt(shortflag) < 8)
		printf("\t");

	printf("%s\t", longflag);
	if (strlenpt(longflag) < 8)
		printf("\t\t");
	else if (strlenpt(longflag) < 16)
		printf("\t");

	if (desc != NULL)
		printf("%s", _(desc));
	printf("\n");
761
762
}

763
/* Explain how to properly use nano and its command-line options. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
764
void usage(void)
Chris Allegretta's avatar
Chris Allegretta committed
765
{
766
767
768
769
770
771
772
	printf(_("Usage: nano [OPTIONS] [[+LINE[,COLUMN]] FILE]...\n\n"));
	/* TRANSLATORS: The next two strings are part of the --help output.
	 * It's best to keep its lines within 80 characters. */
	printf(_("To place the cursor on a specific line of a file, put the line number with\n"
				"a '+' before the filename.  The column number can be added after a comma.\n"));
	printf(_("When a filename is '-', nano reads data from standard input.\n\n"));
	printf(_("Option\t\tGNU long option\t\tMeaning\n"));
773
#ifndef NANO_TINY
774
775
776
777
778
779
780
781
782
783
784
	print_opt("-A", "--smarthome",
		/* TRANSLATORS: The next forty or so strings are option descriptions
		 * for the --help output.  Try to keep them at most 40 characters. */
				N_("Enable smart home key"));
	if (!ISSET(RESTRICTED)) {
		print_opt("-B", "--backup", N_("Save backups of existing files"));
		print_opt(_("-C <dir>"), _("--backupdir=<dir>"),
				N_("Directory for saving unique backup files"));
	}
#endif
	print_opt("-D", "--boldtext", N_("Use bold instead of reverse video text"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
785
#ifndef NANO_TINY
786
	print_opt("-E", "--tabstospaces", N_("Convert typed tabs to spaces"));
787
#endif
788
#ifdef ENABLE_MULTIBUFFER
789
790
791
	if (!ISSET(RESTRICTED))
		print_opt("-F", "--multibuffer",
				N_("Read a file into a new buffer by default"));
Chris Allegretta's avatar
Chris Allegretta committed
792
#endif
793
#ifndef NANO_TINY
794
	print_opt("-G", "--locking", N_("Use (vim-style) lock files"));
795
#endif
796
#ifdef ENABLE_HISTORIES
797
798
799
	if (!ISSET(RESTRICTED))
		print_opt("-H", "--historylog",
				N_("Log & read search/replace string history"));
800
#endif
801
#ifdef ENABLE_NANORC
802
803
	if (!ISSET(RESTRICTED))
		print_opt("-I", "--ignorercfiles", N_("Don't look at nanorc files"));
Chris Allegretta's avatar
Chris Allegretta committed
804
#endif
805
806
807
808
	print_opt("-K", "--rebindkeypad",
		N_("Fix numeric keypad key confusion problem"));
	print_opt("-L", "--nonewlines",
		N_("Don't add newlines to the ends of files"));
809
#ifdef ENABLED_WRAPORJUSTIFY
810
811
	print_opt("-M", "--trimblanks",
		N_("Trim tail spaces when hard-wrapping"));
812
#endif
813
#ifndef NANO_TINY
814
815
	print_opt("-N", "--noconvert",
		N_("Don't convert files from DOS/Mac format"));
816
#endif
817
	print_opt("-O", "--morespace", N_("Use one more line for editing"));
818
#ifdef ENABLE_HISTORIES
819
820
821
	if (!ISSET(RESTRICTED))
		print_opt("-P", "--positionlog",
				N_("Log & read location of cursor position"));
822
#endif
823
#ifdef ENABLE_JUSTIFY
824
	print_opt(_("-Q <str>"), _("--quotestr=<str>"), N_("Quoting string"));
825
#endif
826
827
	if (!ISSET(RESTRICTED))
		print_opt("-R", "--restricted", N_("Restricted mode"));
828
#ifndef NANO_TINY
829
	print_opt("-S", "--smooth", N_("Scroll by line instead of half-screen"));
830
#endif
831
832
833
834
	print_opt(_("-T <#cols>"), _("--tabsize=<#cols>"),
		N_("Set width of a tab to #cols columns"));
	print_opt("-U", "--quickblank", N_("Do quick statusbar blanking"));
	print_opt("-V", "--version", N_("Print version information and exit"));
835
#ifndef NANO_TINY
836
837
838
839
	print_opt("-W", "--wordbounds",
		N_("Detect word boundaries more accurately"));
	print_opt(_("-X <str>"), _("--wordchars=<str>"),
		N_("Which other characters are word parts"));
840
#endif
841
#ifdef ENABLE_COLOR
842
843
844
	if (!ISSET(RESTRICTED))
		print_opt(_("-Y <name>"), _("--syntax=<name>"),
				N_("Syntax definition to use for coloring"));
845
846
#endif
#ifndef NANO_TINY
847
	print_opt("-a", "--atblanks", N_("When soft-wrapping, do it at whitespace"));
848
#endif
849
850
851
	print_opt("-c", "--constantshow", N_("Constantly show cursor position"));
	print_opt("-d", "--rebinddelete",
		N_("Fix Backspace/Delete confusion problem"));
852
#ifdef ENABLE_BROWSER
853
854
	if (!ISSET(RESTRICTED))
		print_opt("-g", "--showcursor", N_("Show cursor in file browser"));
855
#endif
856
	print_opt("-h", "--help", N_("Show this help text and exit"));
857
#ifndef NANO_TINY
858
859
	print_opt("-i", "--autoindent", N_("Automatically indent new lines"));
	print_opt("-k", "--cutfromcursor", N_("Cut from cursor to end of line"));
860
#endif
861
#ifdef ENABLE_LINENUMBERS
862
	print_opt("-l", "--linenumbers", N_("Show line numbers in front of the text"));
863
#endif
864
#ifdef ENABLE_MOUSE
865
	print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
Chris Allegretta's avatar
Chris Allegretta committed
866
#endif
867
	print_opt("-n", "--noread", N_("Do not read the file (only write it)"));
868
#ifdef ENABLE_OPERATINGDIR
869
870
	print_opt(_("-o <dir>"), _("--operatingdir=<dir>"),
		N_("Set operating directory"));
Chris Allegretta's avatar
Chris Allegretta committed
871
#endif
872
	print_opt("-p", "--preserve", N_("Preserve XON (^Q) and XOFF (^S) keys"));
873
#ifdef ENABLED_WRAPORJUSTIFY
874
875
	print_opt(_("-r <#cols>"), _("--fill=<#cols>"),
		N_("Set hard-wrapping point at column #cols"));
876
#endif
877
#ifdef ENABLE_SPELLER
878
879
880
	if (!ISSET(RESTRICTED))
		print_opt(_("-s <prog>"), _("--speller=<prog>"),
				N_("Enable alternate speller"));
881
#endif
882
	print_opt("-t", "--tempfile", N_("Auto save on exit, don't prompt"));
883
#ifndef NANO_TINY
884
	print_opt("-u", "--unix", N_("Save a file by default in Unix format"));
885
#endif
886
	print_opt("-v", "--view", N_("View mode (read-only)"));
887
#ifdef ENABLE_WRAPPING
888
	print_opt("-w", "--nowrap", N_("Don't hard-wrap long lines"));
Chris Allegretta's avatar
Chris Allegretta committed
889
#endif
890
891
892
	print_opt("-x", "--nohelp", N_("Don't show the two help lines"));
	if (!ISSET(RESTRICTED))
		print_opt("-z", "--suspend", N_("Enable suspension"));
893
#ifndef NANO_TINY
894
	print_opt("-$", "--softwrap", N_("Enable soft line wrapping"));
895
#endif
Chris Allegretta's avatar
Chris Allegretta committed
896
897
}

898
899
900
/* 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
901
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
902
{
903
#ifdef REVISION
904
	printf(" GNU nano from git, %s\n", REVISION);
905
#else
906
	printf(_(" GNU nano, version %s\n"), VERSION);
907
#endif
908
909
	printf(" (C) 1999-2011, 2013-2018 Free Software Foundation, Inc.\n");
	printf(_(" (C) 2014-%s the contributors to nano\n"), "2018");
910
911
	printf(_(" Email: nano@nano-editor.org	Web: https://nano-editor.org/"));
	printf(_("\n Compiled options:"));
912

913
#ifdef NANO_TINY
914
	printf(" --enable-tiny");
915
#ifdef ENABLE_BROWSER
916
	printf(" --enable-browser");
917
#endif
918
#ifdef ENABLE_COLOR
919
	printf(" --enable-color");
920
#endif
921
#ifdef ENABLE_EXTRA
922
	printf(" --enable-extra");
923
#endif
924
#ifdef ENABLE_HELP
925
	printf(" --enable-help");
926
#endif
927
#ifdef ENABLE_HISTORIES
928
	printf(" --enable-histories");
929
#endif
930
#ifdef ENABLE_JUSTIFY
931
	printf(" --enable-justify");
932
#endif
933
#ifdef HAVE_LIBMAGIC
934
	printf(" --enable-libmagic");
935
#endif
936
#ifdef ENABLE_LINENUMBERS
937
	printf(" --enable-linenumbers");
938
#endif
939
#ifdef ENABLE_MOUSE
940
	printf(" --enable-mouse");
941
#endif
942
#ifdef ENABLE_NANORC
943
	printf(" --enable-nanorc");
944
#endif
945
#ifdef ENABLE_MULTIBUFFER
946
	printf(" --enable-multibuffer");
947
#endif
948
#ifdef ENABLE_OPERATINGDIR
949
	printf(" --enable-operatingdir");
950
#endif
951
#ifdef ENABLE_SPELLER
952
	printf(" --enable-speller");
953
#endif
954
#ifdef ENABLE_TABCOMP
955
	printf(" --enable-tabcomp");
956
#endif
957
#ifdef ENABLE_WRAPPING
958
	printf(" --enable-wrapping");
959
960
#endif
#else /* !NANO_TINY */
961
#ifndef ENABLE_BROWSER
962
	printf(" --disable-browser");
963
#endif
964
#ifndef ENABLE_COLOR
965
	printf(" --disable-color");
966
#endif
967
#ifndef ENABLE_COMMENT
968
	printf(" --disable-comment");
969
#endif
970
#ifndef ENABLE_EXTRA
971
	printf(" --disable-extra");
972
#endif
973
#ifndef ENABLE_HELP
974
	printf(" --disable-help");
975
#endif
976
#ifndef ENABLE_HISTORIES
977
	printf(" --disable-histories");
978
#endif
979
#ifndef ENABLE_JUSTIFY
980
	printf(" --disable-justify");
981
#endif
982
#ifndef HAVE_LIBMAGIC
983
	printf(" --disable-libmagic");
984
#endif
985
#ifndef ENABLE_LINENUMBERS
986
	printf(" --disable-linenumbers");
987
#endif
988
#ifndef ENABLE_MOUSE
989
	printf(" --disable-mouse");
990
#endif
991
#ifndef ENABLE_MULTIBUFFER
992
	printf(" --disable-multibuffer");
993
#endif
994
#ifndef ENABLE_NANORC
995
	printf(" --disable-nanorc");
996
#endif
997
#ifndef ENABLE_OPERATINGDIR
998
	printf(" --disable-operatingdir");
999
#endif
1000
#ifndef ENABLE_SPELLER
1001
	printf(" --disable-speller");
1002
#endif
1003
#ifndef ENABLE_TABCOMP
1004
	printf(" --disable-tabcomp");
1005
#endif
1006
#ifndef ENABLE_WORDCOMPLETION
1007
	printf(" --disable-wordcomp");
1008
#endif
1009
#ifndef ENABLE_WRAPPING
1010
	printf(" --disable-wrapping");
1011
#endif
1012
1013
#endif /* !NANO_TINY */

1014
#ifdef DISABLE_ROOTWRAPPING
1015
	printf(" --disable-wrapping-as-root");
1016
#endif
1017
#ifdef DEBUG
1018
	printf(" --enable-debug");
1019
#endif
1020
#ifndef ENABLE_NLS
1021
	printf(" --disable-nls");
1022
#endif
1023
#ifdef ENABLE_UTF8
1024
	printf(" --enable-utf8");
1025
#else
1026
	printf(" --disable-utf8");
1027
1028
#endif
#ifdef USE_SLANG
1029
	printf(" --with-slang");
1030
#endif
1031
	printf("\n");
Chris Allegretta's avatar
Chris Allegretta committed
1032
1033
}

1034
1035
/* 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
1036
1037
1038
1039
 * 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. */
1040
void do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
1041
{
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
	int i;

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

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

1063
#ifdef DEBUG
1064
	dump_filestruct(openfile->fileage);
1065
#endif
1066

1067
1068
1069
1070
1071
1072
	/* If the user chose not to save, or if the user chose to save and
	 * the save succeeded, we're ready to exit. */
	if (i == 0 || (i == 1 && do_writeout(TRUE, TRUE) > 0))
		close_and_go();
	else if (i != 1)
		statusbar(_("Cancelled"));
1073
1074
1075
1076
1077
}

/* Close the current buffer, and terminate nano if it was the last. */
void close_and_go(void)
{
1078
#ifndef NANO_TINY
1079
1080
1081
	/* If there is a lockfile, remove it. */
	if (ISSET(LOCKING) && openfile->lock_filename)
		delete_lockfile(openfile->lock_filename);
1082
#endif
1083
#ifdef ENABLE_MULTIBUFFER
1084
1085
	/* If there are no more open file buffers, jump off a cliff. */
	if (!close_buffer())
1086
#endif
1087
		finish();
1088
1089
}

1090
1091
/* Make a note that reading from stdin was concluded with ^C. */
RETSIGTYPE make_a_note(int signal)
1092
{
1093
	input_was_aborted = TRUE;
1094
1095
}

1096
1097
/* Read whatever comes from standard input into a new buffer. */
bool scoop_stdin(void)
1098
{
1099
1100
1101
	struct sigaction oldaction, newaction;
		/* Original and temporary handlers for SIGINT. */
	bool setup_failed = FALSE;
1102
		/* Whether setting up the temporary SIGINT handler failed. */
1103
1104
	FILE *stream;
	int thetty;
1105

1106
1107
1108
	/* Exit from curses mode and put the terminal into its original state. */
	endwin();
	tcsetattr(0, TCSANOW, &oldterm);
1109

1110
	fprintf(stderr, _("Reading from stdin, ^C to abort\n"));
1111

1112
#ifndef NANO_TINY
1113
1114
1115
1116
1117
1118
1119
1120
	/* Enable interpretation of the special control keys so that
	 * we get SIGINT when Ctrl-C is pressed. */
	enable_signals();
#endif

	/* Set things up so that SIGINT will cancel the reading. */
	if (sigaction(SIGINT, NULL, &newaction) == -1) {
		setup_failed = TRUE;
1121
		perror("sigaction");
1122
1123
1124
1125
	} else {
		newaction.sa_handler = make_a_note;
		if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
			setup_failed = TRUE;
1126
			perror("sigaction");
1127
		}
1128
	}
1129

1130
1131
1132
1133
	/* Open standard input. */
	stream = fopen("/dev/stdin", "rb");
	if (stream == NULL) {
		int errnumber = errno;
1134

1135
1136
1137
1138
1139
1140
1141
		terminal_init();
		doupdate();
		statusline(ALERT, _("Failed to open stdin: %s"), strerror(errnumber));
		return FALSE;
	}

	/* Read the input into a new buffer. */
1142
	open_buffer("", TRUE);
1143
	read_file(stream, 0, "stdin", TRUE);
1144
	openfile->edittop = openfile->fileage;
1145

1146
1147
1148
1149
1150
1151
	/* Reconnect the tty as the input source. */
	thetty = open("/dev/tty", O_RDONLY);
	if (!thetty)
		die(_("Couldn't reopen stdin from keyboard, sorry\n"));
	dup2(thetty, 0);
	close(thetty);
1152

1153
1154
1155
	/* If things went well, store the current state of the terminal. */
	if (!input_was_aborted)
		tcgetattr(0, &oldterm);
1156

1157
1158
	/* If it was changed, restore the handler for SIGINT. */
	if (!setup_failed && sigaction(SIGINT, &oldaction, NULL) == -1)
1159
		perror("sigaction");
1160

1161
1162
1163
1164
1165
1166
1167
	terminal_init();
	doupdate();

	if (!ISSET(VIEW_MODE) && openfile->totsize > 0)
		set_modified();

	return TRUE;
1168
1169
}

1170
/* Register half a dozen signal handlers. */
1171
1172
void signal_init(void)
{
1173
1174
1175
1176
	/* Trap SIGINT and SIGQUIT because we want them to do useful things. */
	memset(&act, 0, sizeof(struct sigaction));
	act.sa_handler = SIG_IGN;
	sigaction(SIGINT, &act, NULL);
1177
#ifdef SIGQUIT
1178
	sigaction(SIGQUIT, &act, NULL);
1179
#endif
1180

1181
1182
	/* Trap SIGHUP and SIGTERM because we want to write the file out. */
	act.sa_handler = handle_hupterm;
1183
#ifdef SIGHUP
1184
	sigaction(SIGHUP, &act, NULL);
1185
#endif
1186
	sigaction(SIGTERM, &act, NULL);
1187

1188
#ifndef NANO_TINY
1189
1190
1191
	/* Trap SIGWINCH because we want to handle window resizes. */
	act.sa_handler = handle_sigwinch;
	sigaction(SIGWINCH, &act, NULL);
1192
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1193

1194
1195
1196
1197
	if (ISSET(SUSPEND)) {
		/* Block all other signals in the suspend and continue handlers.
		 * If we don't do this, other stuff interrupts them! */
		sigfillset(&act.sa_mask);
1198
#ifdef SIGTSTP
1199
1200
1201
		/* Trap a normal suspend (^Z) so we can handle it ourselves. */
		act.sa_handler = do_suspend;
		sigaction(SIGTSTP, &act, NULL);
1202
1203
#endif
#ifdef SIGCONT
1204
1205
		act.sa_handler = do_continue;
		sigaction(SIGCONT, &act, NULL);
1206
#endif
1207
	} else {
1208
#ifdef SIGTSTP
1209
1210
		act.sa_handler = SIG_IGN;
		sigaction(SIGTSTP, &act, NULL);
1211
#endif
1212
	}
1213
}
1214

1215
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
1216
RETSIGTYPE handle_hupterm(int signal)
1217
{
1218
	die(_("Received SIGHUP or SIGTERM\n"));
1219
}
1220

1221
/* Handler for SIGTSTP (suspend). */
1222
RETSIGTYPE do_suspend(int signal)
1223
{
1224
#ifdef ENABLE_MOUSE
1225
	disable_mouse_support();
1226
1227
#endif

1228
1229
1230
	/* Move the cursor to the last line of the screen. */
	move(LINES - 1, 0);
	endwin();
1231

1232
1233
1234
	/* Display our helpful message. */
	printf(_("Use \"fg\" to return to nano.\n"));
	fflush(stdout);
1235

1236
1237
	/* Restore the old terminal settings. */
	tcsetattr(0, TCSANOW, &oldterm);
1238

1239
#ifdef SIGSTOP
1240
1241
	/* Do what mutt does: send ourselves a SIGSTOP. */
	kill(0, SIGSTOP);
1242
#endif
1243
}
1244

1245
/* Put nano to sleep (if suspension is enabled). */
1246
void do_suspend_void(void)
1247
{
1248
1249
1250
1251
1252
1253
	if (ISSET(SUSPEND))
		do_suspend(0);
	else {
		statusbar(_("Suspension is not enabled"));
		beep();
	}
1254
1255
}

1256
/* Handler for SIGCONT (continue after suspend). */
1257
RETSIGTYPE do_continue(int signal)
1258
{
1259
#ifdef ENABLE_MOUSE
1260
1261
	if (ISSET(USE_MOUSE))
		enable_mouse_support();
1262
1263
#endif

1264
#ifndef NANO_TINY
1265
1266
	/* Perhaps the user resized the window while we slept. */
	the_window_resized = TRUE;
1267
#else
1268
1269
	/* Put the terminal in the desired state again. */
	terminal_init();
1270
#endif
1271
1272
	/* Tickle the input routine so it will update the screen. */
	ungetch(KEY_FLUSH);
1273
1274
}

1275
#ifndef NANO_TINY
1276
/* Handler for SIGWINCH (window size change). */
1277
RETSIGTYPE handle_sigwinch(int signal)
1278
{
1279
1280
	/* Let the input routine know that a SIGWINCH has occurred. */
	the_window_resized = TRUE;
1281
1282
1283
1284
}

/* Reinitialize and redraw the screen completely. */
void regenerate_screen(void)
1285
{
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
	const char *tty = ttyname(0);
	int fd, result = 0;
	struct winsize win;

	/* Reset the trigger. */
	the_window_resized = FALSE;

	if (tty == NULL)
		return;
	fd = open(tty, O_RDWR);
	if (fd == -1)
		return;
	result = ioctl(fd, TIOCGWINSZ, &win);
	close(fd);
	if (result == -1)
		return;

	/* 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. */
1307
#ifdef REDEFINING_MACROS_OK
1308
1309
	COLS = win.ws_col;
	LINES = win.ws_row;
1310
#endif
1311
	editwincols = COLS - margin;
1312

1313
1314
	/* Ensure that firstcolumn is the starting column of its chunk. */
	ensure_firstcolumn_is_aligned();
1315

1316
#ifdef USE_SLANG
1317
1318
1319
1320
1321
1322
	/* 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();
1323
#else
1324
1325
1326
1327
	/* Do the equivalent of what Minimum Profit does: leave and immediately
	 * reenter curses mode. */
	endwin();
	doupdate();
1328
#endif
1329

1330
1331
1332
1333
1334
	/* Put the terminal in the desired state again, recreate the subwindows
	 * with their (new) sizes, and redraw the contents of these windows. */
	terminal_init();
	window_init();
	total_refresh();
1335
}
1336

1337
1338
1339
/* 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)
1340
{
1341
	sigset_t winch;
1342

1343
1344
1345
	sigemptyset(&winch);
	sigaddset(&winch, SIGWINCH);
	sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
1346
}
1347

1348
/* Handle the global toggle specified in flag. */
1349
void do_toggle(int flag)
1350
{
1351
	bool enabled;
1352

1353
1354
1355
1356
1357
	if (ISSET(RESTRICTED) && (flag == SUSPEND || flag == MULTIBUFFER ||
						flag == BACKUP_FILE || flag == NO_COLOR_SYNTAX)) {
		show_restricted_warning();
		return;
	}
1358

1359
	TOGGLE(flag);
Chris Allegretta's avatar
Chris Allegretta committed
1360

1361
	switch (flag) {
1362
#ifdef ENABLE_MOUSE
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
		case USE_MOUSE:
			mouse_init();
			break;
#endif
		case MORE_SPACE:
		case NO_HELP:
			window_init();
			focusing = FALSE;
			total_refresh();
			break;
		case SUSPEND:
			signal_init();
			break;
		case SOFTWRAP:
			if (!ISSET(SOFTWRAP))
				openfile->firstcolumn = 0;
			refresh_needed = TRUE;
			break;
		case WHITESPACE_DISPLAY:
1382
			titlebar(NULL);  /* Fall through. */
1383
#ifdef ENABLE_COLOR
1384
		case NO_COLOR_SYNTAX:
1385
#endif
1386
1387
1388
			refresh_needed = TRUE;
			break;
	}
Chris Allegretta's avatar
Chris Allegretta committed
1389

1390
	enabled = ISSET(flag);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1391

1392
1393
	if (flag == NO_HELP || flag == NO_WRAP || flag == NO_COLOR_SYNTAX)
		enabled = !enabled;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1394

1395
1396
	statusline(HUSH, "%s %s", _(flagtostr(flag)),
						enabled ? _("enabled") : _("disabled"));
Chris Allegretta's avatar
Chris Allegretta committed
1397
}
1398
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
1399

1400
1401
/* Disable extended input and output processing in our terminal
 * settings. */
1402
void disable_extended_io(void)
1403
{
1404
#ifdef HAVE_TERMIOS_H
1405
	struct termios term = {0};
1406

1407
1408
1409
1410
	tcgetattr(0, &term);
	term.c_lflag &= ~IEXTEN;
	term.c_oflag &= ~OPOST;
	tcsetattr(0, TCSANOW, &term);
1411
#endif
1412
1413
}

1414
1415
1416
1417
/* Disable interpretation of the special control keys in our terminal
 * settings. */
void disable_signals(void)
{
1418
#ifdef HAVE_TERMIOS_H
1419
	struct termios term = {0};
1420

1421
1422
1423
	tcgetattr(0, &term);
	term.c_lflag &= ~ISIG;
	tcsetattr(0, TCSANOW, &term);
1424
#endif
1425
1426
}

1427
#ifndef NANO_TINY
1428
1429
/* Enable interpretation of the special control keys in our terminal
 * settings. */
1430
1431
void enable_signals(void)
{
1432
#ifdef HAVE_TERMIOS_H
1433
	struct termios term = {0};
1434

1435
1436
1437
	tcgetattr(0, &term);
	term.c_lflag |= ISIG;
	tcsetattr(0, TCSANOW, &term);
1438
#endif
1439
1440
1441
}
#endif

1442
1443
/* Disable interpretation of the flow control characters in our terminal
 * settings. */
1444
1445
void disable_flow_control(void)
{
1446
#ifdef HAVE_TERMIOS_H
1447
	struct termios term;
1448

1449
1450
1451
	tcgetattr(0, &term);
	term.c_iflag &= ~IXON;
	tcsetattr(0, TCSANOW, &term);
1452
#endif
1453
1454
}

1455
1456
/* Enable interpretation of the flow control characters in our terminal
 * settings. */
1457
1458
void enable_flow_control(void)
{
1459
#ifdef HAVE_TERMIOS_H
1460
	struct termios term;
1461

1462
1463
1464
	tcgetattr(0, &term);
	term.c_iflag |= IXON;
	tcsetattr(0, TCSANOW, &term);
1465
#endif
1466
1467
}

1468
1469
1470
1471
1472
1473
1474
1475
/* 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. */
1476
1477
void terminal_init(void)
{
1478
#ifdef USE_SLANG
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
	/* 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
	 * 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. */
	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();
1498
#ifdef USE_SLANG
1499
1500
		if (!ISSET(PRESERVE))
			disable_flow_control();
1501

1502
1503
1504
1505
		tcgetattr(0, &newterm);
		newterm_set = TRUE;
	} else
		tcsetattr(0, TCSANOW, &newterm);
1506
#endif
1507
1508
}

1509
1510
1511
/* Ask ncurses for a keycode, or assign a default one. */
int get_keycode(const char *keyname, const int standard)
{
1512
#ifdef HAVE_KEY_DEFINED
1513
	const char *keyvalue = tigetstr(keyname);
1514

1515
1516
1517
	if (keyvalue != 0 && keyvalue != (char *)-1 && key_defined(keyvalue))
		return key_defined(keyvalue);
	else
1518
#endif
1519
		return standard;
1520
}
1521

1522
1523
1524
/* Say that an unbound key was struck, and if possible which one. */
void unbound_key(int code)
{
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
	if (!is_byte(code))
		statusline(ALERT, _("Unbound key"));
	else if (meta_key) {
		if (code == '[')
			statusline(ALERT, _("Unbindable key: M-["));
		else
			statusline(ALERT, _("Unbound key: M-%c"), toupper(code));
	} else if (code < 0x20)
		statusline(ALERT, _("Unbound key: ^%c"), code + 0x40);
	else if (currmenu != MHELP)
		statusline(ALERT, _("Unbound key: %c"), code);
1536
1537
}

1538
1539
1540
1541
#ifdef ENABLE_MOUSE
/* Handle a mouse click on the edit window or the shortcut list. */
int do_mouse(void)
{
1542
	int click_row, click_col;
1543
	int retval = get_mouseinput(&click_row, &click_col, TRUE);
1544
1545
1546
1547
1548
1549

	/* If the click is wrong or already handled, we're done. */
	if (retval != 0)
		return retval;

	/* If the click was in the edit window, put the cursor in that spot. */
1550
	if (wmouse_trafo(edit, &click_row, &click_col, FALSE)) {
1551
		filestruct *current_save = openfile->current;
1552
		ssize_t row_count = click_row - openfile->current_y;
1553
1554
1555
		size_t leftedge;
#ifndef NANO_TINY
		size_t current_x_save = openfile->current_x;
1556
		bool sameline = (click_row == openfile->current_y);
1557
1558
1559
1560
1561
1562
1563
1564
			/* Whether the click was on the row where the cursor is. */

		if (ISSET(SOFTWRAP))
			leftedge = leftedge_for(xplustabs(), openfile->current);
		else
#endif
			leftedge = get_page_start(xplustabs());

1565
		/* Move current up or down to the row that was clicked on. */
1566
1567
1568
1569
1570
1571
		if (row_count < 0)
			go_back_chunks(-row_count, &openfile->current, &leftedge);
		else
			go_forward_chunks(row_count, &openfile->current, &leftedge);

		openfile->current_x = actual_x(openfile->current->data,
1572
								actual_last_column(leftedge, click_col));
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591

#ifndef NANO_TINY
		/* 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();
		else
#endif
			/* The cursor moved; clean the cutbuffer on the next cut. */
			cutbuffer_reset();

		edit_redraw(current_save, CENTERING);
	}

	/* No more handling is needed. */
	return 2;
}
#endif /* ENABLE_MOUSE */

1592
/* Return TRUE when the given function is a cursor-moving command. */
1593
bool wanted_to_move(void (*func)(void))
1594
1595
1596
1597
1598
{
	return func == do_left || func == do_right ||
			func == do_up || func == do_down ||
			func == do_home || func == do_end ||
			func == do_prev_word_void || func == do_next_word_void ||
1599
#ifdef ENABLE_JUSTIFY
1600
			func == do_para_begin_void || func == do_para_end_void ||
1601
#endif
1602
1603
1604
1605
1606
			func == do_prev_block || func == do_next_block ||
			func == do_page_up || func == do_page_down ||
			func == to_first_line || func == to_last_line;
}

1607
1608
1609
1610
1611
/* Return TRUE when the given shortcut is valid in view mode. */
bool okay_for_view(const sc *shortcut)
{
	const subnfunc *func = sctofunc(shortcut);

1612
	return (func != NULL && func->viewok);
1613
1614
}

1615
1616
1617
/* 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. */
1618
int do_input(bool allow_funcs)
1619
{
1620
1621
1622
1623
1624
1625
1626
1627
	int input;
		/* The keystroke we read in: a character or a shortcut. */
	static char *puddle = NULL;
		/* The input buffer for actual characters. */
	static size_t depth = 0;
		/* The length of the input buffer. */
	bool retain_cuts = FALSE;
		/* Whether to conserve the current contents of the cutbuffer. */
1628
	const sc *shortcut;
1629
1630
1631

	/* Read in a keystroke, and show the cursor while waiting. */
	input = get_kbinput(edit, VISIBLE);
1632

1633
#ifndef NANO_TINY
1634
1635
	if (input == KEY_WINCH)
		return KEY_WINCH;
1636
1637
#endif

1638
#ifdef ENABLE_MOUSE
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
	if (input == KEY_MOUSE) {
		/* We received a mouse click. */
		if (do_mouse() == 1)
			/* The click was on a shortcut -- read in the character
			 * that it was converted into. */
			input = get_kbinput(edit, BLIND);
		else
			/* The click was invalid or has been handled -- get out. */
			return ERR;
	}
1649
1650
#endif

1651
	/* Check for a shortcut in the main list. */
1652
	shortcut = get_shortcut(&input);
1653

1654
1655
	/* 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. */
1656
	if (shortcut == NULL) {
1657
1658
1659
1660
		if (is_ascii_cntrl_char(input) || meta_key || !is_byte(input)) {
			unbound_key(input);
			input = ERR;
		}
1661
	}
1662

1663
1664
1665
1666
1667
1668
	if (!allow_funcs)
		return input;

	/* 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. */
1669
	if (input != ERR && shortcut == NULL) {
1670
1671
1672
1673
1674
1675
1676
		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;
		}
1677
#ifndef NANO_TINY
1678
1679
1680
1681
		if (openfile->mark && openfile->kind_of_mark == SOFTMARK) {
			openfile->mark = NULL;
			refresh_needed = TRUE;
		}
1682
#endif
1683
1684
	}

1685
1686
1687
1688
	/* 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. */
1689
	if (shortcut || get_key_buffer_len() == 0) {
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
		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;
		}
1701
	}
1702

1703
	if (shortcut == NULL)
1704
1705
		pletion_line = NULL;
	else {
1706
1707
1708
		if (ISSET(VIEW_MODE) && !okay_for_view(shortcut)) {
			print_view_warning();
			return ERR;
1709
1710
1711
1712
		}

		/* If the function associated with this shortcut is
		 * cutting or copying text, remember this. */
1713
		if (shortcut->func == do_cut_text_void
1714
#ifndef NANO_TINY
1715
1716
				|| shortcut->func == do_copy_text
				|| shortcut->func == do_cut_till_eof
1717
#endif
1718
1719
				)
			retain_cuts = TRUE;
1720

1721
#ifdef ENABLE_WORDCOMPLETION
1722
		if (shortcut->func != complete_a_word)
1723
			pletion_line = NULL;
1724
#endif
1725
#ifdef ENABLE_NANORC
1726
		if (shortcut->func == (functionptrtype)implant) {
1727
			implant(shortcut->expansion);
1728
1729
1730
			return 42;
		}
#endif
1731
#ifndef NANO_TINY
1732
		if (shortcut->func == do_toggle_void) {
1733
1734
			do_toggle(shortcut->toggle);
			if (shortcut->toggle != CUT_FROM_CURSOR)
1735
1736
				retain_cuts = TRUE;
		} else
1737
#endif
1738
		{
1739
#ifdef ENABLE_WRAPPING
1740
			filestruct *was_next = openfile->current->next;
1741
#endif
1742
#ifndef NANO_TINY
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
			filestruct *was_current = openfile->current;
			size_t was_x = openfile->current_x;

			/* If Shifted movement occurs, set the mark. */
			if (shift_held && !openfile->mark) {
				openfile->mark = openfile->current;
				openfile->mark_x = openfile->current_x;
				openfile->kind_of_mark = SOFTMARK;
			}
#endif
			/* Execute the function of the shortcut. */
1754
			shortcut->func();
1755

1756
#ifndef NANO_TINY
1757
1758
1759
			/* When the marked region changes without Shift being held,
			 * discard a soft mark.  And when the marked region covers a
			 * different set of lines, reset  the "last line too" flag. */
1760
1761
1762
1763
			if (openfile->mark) {
				if (!shift_held && openfile->kind_of_mark == SOFTMARK &&
									(openfile->current != was_current ||
									openfile->current_x != was_x ||
1764
									wanted_to_move(shortcut->func))) {
1765
1766
1767
1768
1769
					openfile->mark = NULL;
					refresh_needed = TRUE;
				} else if (openfile->current != was_current)
					also_the_last = FALSE;
			}
1770
#endif
1771
#ifdef ENABLE_WRAPPING
1772
1773
1774
			/* If the cursor moved to another line and this was not caused
			 * by adding characters to the buffer, clear the prepend flag. */
			if (openfile->current->next != was_next &&
1775
1776
							shortcut->func != do_tab &&
							shortcut->func != do_verbatim_input)
1777
				wrap_reset();
1778
#endif
1779
#ifdef ENABLE_COLOR
1780
1781
			if (!refresh_needed && !okay_for_view(shortcut))
				check_the_multis(openfile->current);
1782
#endif
1783
1784
			if (!refresh_needed && (shortcut->func == do_delete ||
									shortcut->func == do_backspace))
1785
1786
				update_line(openfile->current, openfile->current_x);
		}
1787
1788
	}

1789
1790
1791
1792
	/* 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 (!retain_cuts)
		cutbuffer_reset();
1793

1794
	return input;
1795
1796
}

1797
/* The user typed output_len multibyte characters.  Add them to the edit
1798
 * buffer, filtering out all ASCII control characters if allow_cntrls is
1799
1800
 * TRUE. */
void do_output(char *output, size_t output_len, bool allow_cntrls)
1801
{
1802
1803
1804
1805
	char onechar[MAXCHARLEN];
	int char_len;
	size_t current_len = strlen(openfile->current->data);
	size_t i = 0;
1806
#ifndef NANO_TINY
1807
	size_t original_row = 0, old_amount = 0;
1808

1809
1810
1811
1812
1813
	if (ISSET(SOFTWRAP)) {
		if (openfile->current_y == editwinrows - 1)
			original_row = chunk_for(xplustabs(), openfile->current);
		old_amount = number_of_chunks_in(openfile->current);
	}
1814
#endif
1815

1816
1817
1818
1819
	while (i < output_len) {
		/* Encode an embedded NUL byte as 0x0A. */
		if (output[i] == '\0')
			output[i] = '\n';
1820

1821
1822
		/* Get the next multibyte character. */
		char_len = parse_mbchar(output + i, onechar, NULL);
1823

1824
		i += char_len;
1825

1826
		/* If controls are not allowed, ignore an ASCII control character. */
1827
		if (!allow_cntrls && is_ascii_cntrl_char(*(output + i - char_len)))
1828
			continue;
1829

1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
		/* Make room for the new character and copy it into the line. */
		openfile->current->data = charealloc(openfile->current->data,
										current_len + char_len + 1);
		charmove(openfile->current->data + openfile->current_x + char_len,
						openfile->current->data + openfile->current_x,
						current_len - openfile->current_x + 1);
		strncpy(openfile->current->data + openfile->current_x, onechar,
						char_len);
		current_len += char_len;
		openfile->totsize++;
		set_modified();
1841

1842
#ifndef NANO_TINY
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
		add_undo(ADD);

		/* Note that current_x has not yet been incremented. */
		if (openfile->current == openfile->mark &&
						openfile->current_x < openfile->mark_x)
			openfile->mark_x += char_len;

		/* When the cursor is on the top row and not on the first chunk
		 * of a line, adding text there might change the preceding chunk
		 * and thus require an adjustment of firstcolumn. */
		if (openfile->current == openfile->edittop &&
						openfile->firstcolumn > 0) {
			ensure_firstcolumn_is_aligned();
			refresh_needed = TRUE;
		}
1858
1859
#endif

1860
		openfile->current_x += char_len;
1861

1862
#ifndef NANO_TINY
1863
		update_undo(ADD);
1864
1865
#endif

1866
1867
1868
1869
1870
1871
		/* If we've added text to the magicline, create a new magicline. */
		if (openfile->filebot == openfile->current && !ISSET(NO_NEWLINES)) {
			new_magicline();
			if (margin > 0)
				refresh_needed = TRUE;
		}
1872

1873
#ifdef ENABLE_WRAPPING
1874
1875
1876
		/* If text gets wrapped, the edit window needs a refresh. */
		if (!ISSET(NO_WRAP) && do_wrap(openfile->current))
			refresh_needed = TRUE;
1877
#endif
1878
	}
1879

1880
#ifndef NANO_TINY
1881
1882
1883
1884
1885
1886
1887
1888
1889
	/* If the number of screen rows that a softwrapped line occupies has
	 * changed, we need a full refresh.  And if we were on the last line
	 * of the edit window, and we moved one screen row, we're now below
	 * the last line of the edit window, so we need a full refresh too. */
	if (ISSET(SOFTWRAP) && refresh_needed == FALSE &&
				(number_of_chunks_in(openfile->current) != old_amount ||
				(openfile->current_y == editwinrows - 1 &&
				chunk_for(xplustabs(), openfile->current) != original_row)))
		refresh_needed = TRUE;
1890
#endif
1891

1892
	openfile->placewewant = xplustabs();
1893

1894
#ifdef ENABLE_COLOR
1895
1896
	if (!refresh_needed)
		check_the_multis(openfile->current);
1897
#endif
1898

1899
1900
	if (!refresh_needed)
		update_line(openfile->current, openfile->current_x);
1901
1902
}

1903
int main(int argc, char **argv)
Chris Allegretta's avatar
Chris Allegretta committed
1904
{
1905
	int stdin_flags, optchr;
1906
#if defined(ENABLED_WRAPORJUSTIFY) && defined(ENABLE_NANORC)
1907
1908
	bool fill_used = FALSE;
		/* Was the fill option used on the command line? */
1909
#endif
1910
#ifdef ENABLE_WRAPPING
1911
1912
	bool forced_wrapping = FALSE;
		/* Should long lines be automatically hard wrapped? */
1913
#endif
1914
1915
	const struct option long_options[] = {
		{"boldtext", 0, NULL, 'D'},
1916
#ifdef ENABLE_MULTIBUFFER
1917
		{"multibuffer", 0, NULL, 'F'},
Chris Allegretta's avatar
Chris Allegretta committed
1918
#endif
1919
#ifdef ENABLE_NANORC
1920
		{"ignorercfiles", 0, NULL, 'I'},
1921
#endif
1922
1923
		{"rebindkeypad", 0, NULL, 'K'},
		{"nonewlines", 0, NULL, 'L'},
1924
1925
1926
#ifdef ENABLED_WRAPORJUSTIFY
		{"trimblanks", 0, NULL, 'M'},
#endif
1927
		{"morespace", 0, NULL, 'O'},
1928
#ifdef ENABLE_JUSTIFY
1929
		{"quotestr", 1, NULL, 'Q'},
1930
#endif
1931
1932
1933
1934
		{"restricted", 0, NULL, 'R'},
		{"tabsize", 1, NULL, 'T'},
		{"quickblank", 0, NULL, 'U'},
		{"version", 0, NULL, 'V'},
1935
#ifdef ENABLE_COLOR
1936
		{"syntax", 1, NULL, 'Y'},
1937
#endif
1938
1939
		{"constantshow", 0, NULL, 'c'},
		{"rebinddelete", 0, NULL, 'd'},
1940
#ifdef ENABLE_BROWSER
1941
		{"showcursor", 0, NULL, 'g'},
1942
#endif
1943
		{"help", 0, NULL, 'h'},
1944
#ifdef ENABLE_LINENUMBERS
1945
		{"linenumbers", 0, NULL, 'l'},
1946
#endif
1947
#ifdef ENABLE_MOUSE
1948
		{"mouse", 0, NULL, 'm'},
1949
#endif
1950
		{"noread", 0, NULL, 'n'},
1951
#ifdef ENABLE_OPERATINGDIR
1952
		{"operatingdir", 1, NULL, 'o'},
1953
#endif
1954
1955
		{"preserve", 0, NULL, 'p'},
		{"quiet", 0, NULL, 'q'},
1956
#ifdef ENABLED_WRAPORJUSTIFY
1957
		{"fill", 1, NULL, 'r'},
1958
#endif
1959
#ifdef ENABLE_SPELLER
1960
		{"speller", 1, NULL, 's'},
1961
#endif
1962
1963
		{"tempfile", 0, NULL, 't'},
		{"view", 0, NULL, 'v'},
1964
#ifdef ENABLE_WRAPPING
1965
		{"nowrap", 0, NULL, 'w'},
1966
#endif
1967
1968
		{"nohelp", 0, NULL, 'x'},
		{"suspend", 0, NULL, 'z'},
1969
#ifndef NANO_TINY
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
		{"smarthome", 0, NULL, 'A'},
		{"backup", 0, NULL, 'B'},
		{"backupdir", 1, NULL, 'C'},
		{"tabstospaces", 0, NULL, 'E'},
		{"locking", 0, NULL, 'G'},
		{"historylog", 0, NULL, 'H'},
		{"noconvert", 0, NULL, 'N'},
		{"positionlog", 0, NULL, 'P'},
		{"smooth", 0, NULL, 'S'},
		{"wordbounds", 0, NULL, 'W'},
		{"wordchars", 1, NULL, 'X'},
		{"atblanks", 0, NULL, 'a'},
		{"autoindent", 0, NULL, 'i'},
		{"cutfromcursor", 0, NULL, 'k'},
		{"unix", 0, NULL, 'u'},
		{"softwrap", 0, NULL, '$'},
#endif
		{NULL, 0, NULL, 0}
	};

1990
#ifdef __linux__
1991
1992
	struct vt_stat dummy;

1993
	/* Check whether we're running on a Linux console. */
1994
	on_a_vt = (ioctl(0, VT_GETSTATE, &dummy) == 0);
1995
1996
#endif

1997
1998
1999
2000
2001
2002
2003
	/* Back up the terminal settings so that they can be restored. */
	tcgetattr(0, &oldterm);

	/* Get the state of standard input and ensure it uses blocking mode. */
	stdin_flags = fcntl(0, F_GETFL, 0);
	if (stdin_flags != -1)
		fcntl(0, F_SETFL, stdin_flags & ~O_NONBLOCK);
2004

2005
#ifdef ENABLE_UTF8
2006
2007
2008
2009
	/* If setting the locale is successful and it uses UTF-8, we need
	 * to use the multibyte functions for text processing. */
	if (setlocale(LC_ALL, "") != NULL &&
				strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
2010
#ifdef USE_SLANG
2011
		SLutf8_enable(1);
2012
#endif
2013
2014
		utf8_init();
	}
2015
#else
2016
	setlocale(LC_ALL, "");
2017
2018
#endif

2019
#ifdef ENABLE_NLS
2020
2021
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
Chris Allegretta's avatar
Chris Allegretta committed
2022
2023
#endif

2024
#if defined(ENABLE_UTF8) && !defined(NANO_TINY)
2025
2026
2027
	if (MB_CUR_MAX > MAXCHARLEN)
		fprintf(stderr, "Unexpected large character size: %i bytes"
						" -- please report a bug\n", (int)MB_CUR_MAX);
2028
#endif
2029

2030
#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAPPING)
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
	/* If we don't have rcfile support, --disable-wrapping-as-root is
	 * used, and we're root, turn wrapping off. */
	if (geteuid() == NANO_ROOT_UID)
		SET(NO_WRAP);
#endif

	/* If the executable's name starts with 'r', activate restricted mode. */
	if (*(tail(argv[0])) == 'r')
		SET(RESTRICTED);

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

	/* 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. */
	if (ISSET(RESTRICTED)) {
		UNSET(SUSPEND);
		UNSET(BACKUP_FILE);
2266
#ifdef ENABLE_NANORC
2267
2268
2269
		no_rcfiles = TRUE;
		UNSET(HISTORYLOG);
		UNSET(POS_HISTORY);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2270
#endif
2271
	}
2272

2273
2274
2275
	/* 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();
2276

2277
#ifdef ENABLE_NANORC
2278
2279
	if (!no_rcfiles) {
		/* Back up the command-line options, then read the rcfile(s). */
2280
#ifdef ENABLE_OPERATINGDIR
2281
		char *operating_dir_cpy = operating_dir;
Chris Allegretta's avatar
Chris Allegretta committed
2282
#endif
2283
#ifdef ENABLED_WRAPORJUSTIFY
2284
		ssize_t wrap_at_cpy = wrap_at;
Chris Allegretta's avatar
Chris Allegretta committed
2285
#endif
2286
#ifndef NANO_TINY
2287
2288
		char *backup_dir_cpy = backup_dir;
		char *word_chars_cpy = word_chars;
2289
#endif
2290
#ifdef ENABLE_JUSTIFY
2291
		char *quotestr_cpy = quotestr;
Chris Allegretta's avatar
Chris Allegretta committed
2292
#endif
2293
#ifdef ENABLE_SPELLER
2294
		char *alt_speller_cpy = alt_speller;
Chris Allegretta's avatar
Chris Allegretta committed
2295
#endif
2296
2297
2298
		ssize_t tabsize_cpy = tabsize;
		unsigned flags_cpy[sizeof(flags) / sizeof(flags[0])];
		size_t i;
2299

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

2302
#ifdef ENABLE_OPERATINGDIR
2303
		operating_dir = NULL;
2304
#endif
2305
#ifndef NANO_TINY
2306
2307
		backup_dir = NULL;
		word_chars = NULL;
2308
#endif
2309
#ifdef ENABLE_JUSTIFY
2310
		quotestr = NULL;
2311
#endif
2312
#ifdef ENABLE_SPELLER
2313
		alt_speller = NULL;
2314
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2315

2316
		do_rcfiles();
Chris Allegretta's avatar
Chris Allegretta committed
2317

2318
#ifdef DEBUG
2319
2320
		fprintf(stderr, "After rebinding keys...\n");
		print_sclist();
2321
2322
#endif

2323
		/* If the backed-up command-line options have a value, restore them. */
2324
#ifdef ENABLE_OPERATINGDIR
2325
2326
2327
2328
		if (operating_dir_cpy != NULL) {
			free(operating_dir);
			operating_dir = operating_dir_cpy;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2329
#endif
2330
#ifdef ENABLED_WRAPORJUSTIFY
2331
2332
		if (fill_used)
			wrap_at = wrap_at_cpy;
Chris Allegretta's avatar
Chris Allegretta committed
2333
#endif
2334
#ifndef NANO_TINY
2335
2336
2337
2338
2339
2340
2341
2342
		if (backup_dir_cpy != NULL) {
			free(backup_dir);
			backup_dir = backup_dir_cpy;
		}
		if (word_chars_cpy != NULL) {
			free(word_chars);
			word_chars = word_chars_cpy;
		}
2343
#endif
2344
#ifdef ENABLE_JUSTIFY
2345
2346
2347
2348
		if (quotestr_cpy != NULL) {
			free(quotestr);
			quotestr = quotestr_cpy;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2349
#endif
2350
#ifdef ENABLE_SPELLER
2351
2352
2353
2354
		if (alt_speller_cpy != NULL) {
			free(alt_speller);
			alt_speller = alt_speller_cpy;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2355
#endif
2356
2357
		if (tabsize_cpy != -1)
			tabsize = tabsize_cpy;
2358

2359
2360
2361
2362
		/* Simply OR the boolean flags from rcfile and command line. */
		for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
			flags[i] |= flags_cpy[i];
	}
2363
#ifdef DISABLE_ROOTWRAPPING
2364
2365
2366
2367
	/* 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);
2368
#endif
2369
#endif /* ENABLE_NANORC */
Chris Allegretta's avatar
Chris Allegretta committed
2370

2371
#ifdef ENABLE_WRAPPING
2372
2373
2374
2375
	/* 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)
		UNSET(NO_WRAP);
2376
#endif
2377

2378
2379
2380
	/* If the user wants bold instead of reverse video for hilited text... */
	if (ISSET(BOLD_TEXT))
		hilite_attribute = A_BOLD;
2381

2382
#ifdef ENABLE_HISTORIES
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
	/* Initialize the pointers for the Search/Replace/Execute histories. */
	history_init();

	/* If we need history files, verify that we have a directory for them,
	 * and when not, cancel the options. */
	if ((ISSET(HISTORYLOG) || ISSET(POS_HISTORY)) && !have_statedir()) {
		UNSET(HISTORYLOG);
		UNSET(POS_HISTORY);
	}

	/* If the user wants history persistence, read the relevant files. */
	if (ISSET(HISTORYLOG))
		load_history();
	if (ISSET(POS_HISTORY))
		load_poshistory();
2398
#endif /* ENABLE_HISTORIES */
2399

2400
#ifndef NANO_TINY
2401
2402
2403
2404
2405
	/* If backups are enabled and a backup directory was specified and
	 * we're not in restricted mode, make sure the path exists and is
	 * a directory, so that backup files can be saved there. */
	if (ISSET(BACKUP_FILE) && backup_dir != NULL && !ISSET(RESTRICTED))
		init_backup_dir();
2406
#endif
2407

2408
#ifdef ENABLE_OPERATINGDIR
2409
2410
2411
2412
	/* Set up the operating directory.  This entails chdir()ing there,
	 * so that file reads and writes will be based there. */
	if (operating_dir != NULL)
		init_operating_dir();
2413
2414
#endif

2415
#ifdef ENABLE_JUSTIFY
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
	/* If punct wasn't specified, set its default value. */
	if (punct == NULL)
		punct = mallocstrcpy(NULL, "!.?");

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

	/* If quotestr wasn't specified, set its default value. */
	if (quotestr == NULL)
		quotestr = mallocstrcpy(NULL, "^([ \t]*[#:>|}])+");
	quoterc = regcomp(&quotereg, quotestr, NANO_REG_EXTENDED);

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

		quoteerr = charalloc(size);
		regerror(quoterc, &quotereg, quoteerr, size);
	}
2439
#endif /* ENABLE_JUSTIFY */
2440

2441
#ifdef ENABLE_SPELLER
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
	/* If we don't have an alternative spell checker after reading the
	 * command line and/or rcfile(s), check $SPELL for one, as Pico
	 * does (unless we're using restricted mode, in which case spell
	 * checking is disabled, since it would allow reading from or
	 * writing to files not specified on the command line). */
	if (!ISSET(RESTRICTED) && alt_speller == NULL) {
		const char *spellenv = getenv("SPELL");
		if (spellenv != NULL)
			alt_speller = mallocstrcpy(NULL, spellenv);
	}
2452
2453
#endif

2454
#ifndef NANO_TINY
2455
2456
2457
	/* If matchbrackets wasn't specified, set its default value. */
	if (matchbrackets == NULL)
		matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");
2458

2459
2460
	/* If the whitespace option wasn't specified, set its default value. */
	if (whitespace == NULL) {
2461
#ifdef ENABLE_UTF8
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
		if (using_utf8()) {
			/* A tab is shown as a Right-Pointing Double Angle Quotation Mark
			 * (U+00BB), and a space as a Middle Dot (U+00B7). */
			whitespace = mallocstrcpy(NULL, "\xC2\xBB\xC2\xB7");
			whitespace_len[0] = 2;
			whitespace_len[1] = 2;
		} else
#endif
		{
			whitespace = mallocstrcpy(NULL, ">.");
			whitespace_len[0] = 1;
			whitespace_len[1] = 1;
		}
2475
	}
Benno Schulenberg's avatar
Benno Schulenberg committed
2476
#endif /* !NANO_TINY */
2477

2478
2479
2480
	/* Initialize the search string. */
	last_search = mallocstrcpy(NULL, "");
	UNSET(BACKWARDS_SEARCH);
2481

2482
2483
2484
	/* If tabsize wasn't specified, set its default value. */
	if (tabsize == -1)
		tabsize = WIDTH_OF_TAB;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2485

2486
2487
2488
	/* Initialize curses mode.  If this fails, get out. */
	if (initscr() == NULL)
		exit(1);
2489

2490
2491
2492
2493
2494
2495
2496
#ifdef ENABLE_COLOR
	set_colorpairs();
#else
	interface_color_pair[TITLE_BAR] = hilite_attribute;
	interface_color_pair[LINE_NUMBER] = hilite_attribute;
	interface_color_pair[SELECTED_TEXT] = hilite_attribute;
	interface_color_pair[STATUS_BAR] = hilite_attribute;
2497
	interface_color_pair[ERROR_MESSAGE] = hilite_attribute;
2498
2499
2500
2501
	interface_color_pair[KEY_COMBO] = hilite_attribute;
	interface_color_pair[FUNCTION_TAG] = A_NORMAL;
#endif

2502
2503
	/* Set up the terminal state. */
	terminal_init();
2504

2505
#ifdef DEBUG
2506
	fprintf(stderr, "Main: set up windows\n");
2507
#endif
2508

2509
2510
2511
	/* Create the three subwindows, based on the current screen dimensions. */
	window_init();
	curs_set(0);
2512

2513
	editwincols = COLS;
2514

2515
2516
	/* Set up the signal handlers. */
	signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
2517

2518
#ifdef ENABLE_MOUSE
2519
2520
	/* Initialize mouse support. */
	mouse_init();
2521
#endif
2522

2523
2524
2525
2526
2527
2528
2529
2530
	/* Ask ncurses for the key codes for Control+Left/Right/Up/Down. */
	controlleft = get_keycode("kLFT5", CONTROL_LEFT);
	controlright = get_keycode("kRIT5", CONTROL_RIGHT);
	controlup = get_keycode("kUP5", CONTROL_UP);
	controldown = get_keycode("kDN5", CONTROL_DOWN);
	/* Ask for the codes for Control+Home/End. */
	controlhome = get_keycode("kHOM5", CONTROL_HOME);
	controlend = get_keycode("kEND5", CONTROL_END);
2531
#ifndef NANO_TINY
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
	/* 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+Control+Home/End. */
	shiftcontrolhome = get_keycode("kHOM6", SHIFT_CONTROL_HOME);
	shiftcontrolend = get_keycode("kEND6", SHIFT_CONTROL_END);
	/* Ask for the codes for Alt+Left/Right/Up/Down. */
	altleft = get_keycode("kLFT3", ALT_LEFT);
	altright = get_keycode("kRIT3", ALT_RIGHT);
	altup = get_keycode("kUP3", ALT_UP);
	altdown = get_keycode("kDN3", ALT_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);
2550
2551
#endif

2552
#ifdef HAVE_SET_ESCDELAY
2553
2554
	/* Tell ncurses to pass the Esc key quickly. */
	set_escdelay(50);
2555
2556
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2557
#ifdef DEBUG
2558
	fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
2559
#endif
2560

2561
	/* Read the files mentioned on the command line into new buffers. */
2562
	while (optind < argc && (!openfile || read_them_all)) {
2563
		ssize_t givenline = 0, givencol = 0;
2564

2565
2566
2567
2568
2569
		/* If there's a +LINE[,COLUMN] argument here, eat it up. */
		if (optind < argc - 1 && argv[optind][0] == '+') {
			if (!parse_line_column(&argv[optind++][1], &givenline, &givencol))
				statusline(ALERT, _("Invalid line or column number"));
		}
2570

2571
2572
2573
		/* If the filename is a dash, read from standard input; otherwise,
		 * open the file; skip positioning the cursor if either failed. */
		if (strcmp(argv[optind], "-") == 0) {
2574
			optind++;
2575
2576
			if (!scoop_stdin())
				continue;
2577
		} else if (!open_buffer(argv[optind++], TRUE))
2578
2579
2580
2581
2582
			continue;

		/* If a position was given on the command line, go there. */
		if (givenline != 0 || givencol != 0)
			do_gotolinecolumn(givenline, givencol, FALSE, FALSE);
2583
#ifdef ENABLE_HISTORIES
2584
2585
2586
2587
2588
2589
		else if (ISSET(POS_HISTORY) && openfile->filename[0] != '\0') {
			ssize_t savedline, savedcol;
			/* If edited before, restore the last cursor position. */
			if (has_old_position(argv[optind - 1], &savedline, &savedcol))
				do_gotolinecolumn(savedline, savedcol, FALSE, FALSE);
		}
2590
#endif
2591
	}
2592

2593
2594
2595
2596
	/* If no filenames were given, or all of them were invalid things like
	 * directories, then open a blank buffer and allow editing.  Otherwise,
	 * switch from the last opened file to the next, that is: the first. */
	if (openfile == NULL) {
2597
		open_buffer("", TRUE);
2598
2599
		UNSET(VIEW_MODE);
	}
2600
#ifdef ENABLE_MULTIBUFFER
2601
2602
	else
		openfile = openfile->next;
Chris Allegretta's avatar
Chris Allegretta committed
2603
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2604

2605
#ifdef DEBUG
2606
	fprintf(stderr, "Main: show title bar, and enter main loop\n");
2607
2608
#endif

2609
	prepare_for_display();
Robert Siemborski's avatar
Robert Siemborski committed
2610

2611
2612
	if (rcfile_with_errors != NULL)
		statusline(ALERT, _("Mistakes in '%s'"), rcfile_with_errors);
2613

2614
	while (TRUE) {
2615
#ifdef ENABLE_LINENUMBERS
2616
		int needed_margin = digits(openfile->filebot->lineno) + 1;
2617

2618
2619
2620
		/* Suppress line numbers when there is not enough room for them. */
		if (!ISSET(LINE_NUMBERS) || needed_margin > COLS - 4)
			needed_margin = 0;
2621

2622
2623
2624
		if (needed_margin != margin) {
			margin = needed_margin;
			editwincols = COLS - margin;
2625

2626
#ifndef NANO_TINY
2627
2628
			/* Ensure that firstcolumn is the starting column of its chunk. */
			ensure_firstcolumn_is_aligned();
2629
#endif
2630
2631
2632
			/* The margin has changed -- schedule a full refresh. */
			refresh_needed = TRUE;
		}
2633
#endif
2634
2635
		if (currmenu != MMAIN)
			display_main_list();
2636

2637
2638
		lastmessage = HUSH;
		as_an_at = TRUE;
2639

2640
2641
2642
2643
		/* Update the displayed current cursor position only when there
		 * are no keys waiting in the input buffer. */
		if (ISSET(CONSTANT_SHOW) && get_key_buffer_len() == 0)
			do_cursorpos(FALSE);
2644

2645
2646
2647
2648
2649
2650
		/* Refresh just the cursor position or the entire edit window. */
		if (!refresh_needed) {
			place_the_cursor();
			wnoutrefresh(edit);
		} else
			edit_refresh();
2651

2652
		errno = 0;
2653
		focusing = TRUE;
2654

2655
2656
		/* Forget any earlier statusbar x position. */
		reinit_statusbar_x();
2657

2658
2659
2660
		/* Read in and interpret keystrokes. */
		do_input(TRUE);
	}
Chris Allegretta's avatar
Chris Allegretta committed
2661
}