nano.c 57.7 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   nano.c                                                               *
 *                                                                        *
5
 *   Copyright (C) 1999-2004 Chris Allegretta                             *
6
 *   Copyright (C) 2005-2006 David Lawrence Ramsey                        *
Chris Allegretta's avatar
Chris Allegretta committed
7
8
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
9
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
10
11
 *   any later version.                                                   *
 *                                                                        *
12
13
14
15
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
Chris Allegretta's avatar
Chris Allegretta committed
16
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
19
20
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
21
22
23
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

Chris Allegretta's avatar
Chris Allegretta committed
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <ctype.h>
#include <locale.h>
36
#include <termios.h>
Chris Allegretta's avatar
Chris Allegretta committed
37
38
39
40
41

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

42
43
44
45
#ifdef ENABLE_NANORC
static bool no_rcfiles = FALSE;
	/* Should we ignore all rcfiles? */
#endif
46
47
48
49
static struct termios oldterm;
	/* The user's original terminal settings. */
static struct sigaction act;
	/* For all our fun signal handlers. */
Chris Allegretta's avatar
Chris Allegretta committed
50

51
52
53
/* Create a new filestruct node.  Note that we specifically do not set
 * prevnode->next equal to the new line. */
filestruct *make_new_node(filestruct *prevnode)
54
{
55
56
57
58
59
60
61
62
    filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));

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

    return newnode;
63
64
}

65
66
/* Make a copy of a filestruct node. */
filestruct *copy_node(const filestruct *src)
Chris Allegretta's avatar
Chris Allegretta committed
67
{
68
    filestruct *dst;
Chris Allegretta's avatar
Chris Allegretta committed
69

70
    assert(src != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
71

72
    dst = (filestruct *)nmalloc(sizeof(filestruct));
73

74
75
76
77
    dst->data = mallocstrcpy(NULL, src->data);
    dst->next = src->next;
    dst->prev = src->prev;
    dst->lineno = src->lineno;
78

79
    return dst;
Chris Allegretta's avatar
Chris Allegretta committed
80
81
}

82
83
84
/* Splice a node into an existing filestruct. */
void splice_node(filestruct *begin, filestruct *newnode, filestruct
	*end)
Chris Allegretta's avatar
Chris Allegretta committed
85
{
86
    assert(newnode != NULL && begin != NULL);
87

88
89
90
91
92
93
    newnode->next = end;
    newnode->prev = begin;
    begin->next = newnode;
    if (end != NULL)
	end->prev = newnode;
}
94

95
96
97
98
/* Unlink a node from the rest of the filestruct. */
void unlink_node(const filestruct *fileptr)
{
    assert(fileptr != NULL);
99

100
101
102
103
104
    if (fileptr->prev != NULL)
	fileptr->prev->next = fileptr->next;
    if (fileptr->next != NULL)
	fileptr->next->prev = fileptr->prev;
}
105

106
107
108
109
/* Delete a node from the filestruct. */
void delete_node(filestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->data != NULL);
110

111
112
    if (fileptr->data != NULL)
	free(fileptr->data);
113

114
    free(fileptr);
115
116
}

117
118
/* Duplicate a whole filestruct. */
filestruct *copy_filestruct(const filestruct *src)
119
{
120
    filestruct *head, *copy;
121

122
    assert(src != NULL);
123

124
125
126
127
    copy = copy_node(src);
    copy->prev = NULL;
    head = copy;
    src = src->next;
128

129
130
131
132
    while (src != NULL) {
	copy->next = copy_node(src);
	copy->next->prev = copy;
	copy = copy->next;
Chris Allegretta's avatar
Chris Allegretta committed
133

134
135
	src = src->next;
    }
136

137
    copy->next = NULL;
138

139
    return head;
Chris Allegretta's avatar
Chris Allegretta committed
140
141
}

142
/* Free a filestruct. */
143
void free_filestruct(filestruct *src)
144
{
145
146
147
148
149
150
    assert(src != NULL);

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

152
    delete_node(src);
153
154
}

155
/* Renumber all entries in a filestruct, starting with fileptr. */
156
void renumber(filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
157
{
158
    ssize_t line;
159

160
    assert(fileptr != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
161

162
    line = (fileptr->prev == NULL) ? 0 : fileptr->prev->lineno;
163

164
    assert(fileptr != fileptr->next);
165

166
    for (; fileptr != NULL; fileptr = fileptr->next)
167
	fileptr->lineno = ++line;
Chris Allegretta's avatar
Chris Allegretta committed
168
169
}

170
171
172
173
/* Partition a filestruct so it begins at (top, top_x) and ends at (bot,
 * bot_x). */
partition *partition_filestruct(filestruct *top, size_t top_x,
	filestruct *bot, size_t bot_x)
174
{
175
    partition *p;
176

177
    assert(top != NULL && bot != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
178

179
180
    /* Initialize the partition. */
    p = (partition *)nmalloc(sizeof(partition));
181

182
183
184
    /* 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. */
185
186
187
    if (top != openfile->fileage) {
	p->fileage = openfile->fileage;
	openfile->fileage = top;
188
    } else
189
	p->fileage = NULL;
190
191
192
    if (bot != openfile->filebot) {
	p->filebot = openfile->filebot;
	openfile->filebot = bot;
193
194
    } else
	p->filebot = NULL;
195

196
197
198
199
200
201
202
    /* Save the line above the top of the partition, detach the top of
     * the partition from it, and save the text before top_x in
     * top_data. */
    p->top_prev = top->prev;
    top->prev = NULL;
    p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
    p->top_data[top_x] = '\0';
203

204
205
206
207
208
209
    /* Save the line below the bottom of the partition, detach the
     * bottom of the partition from it, and save the text after bot_x in
     * bot_data. */
    p->bot_next = bot->next;
    bot->next = NULL;
    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
210

211
212
    /* Remove all text after bot_x at the bottom of the partition. */
    null_at(&bot->data, bot_x);
213

214
    /* Remove all text before top_x at the top of the partition. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
215
216
    charmove(top->data, top->data + top_x, strlen(top->data) -
	top_x + 1);
217
    align(&top->data);
218

219
220
221
    /* Return the partition. */
    return p;
}
222

223
/* Unpartition a filestruct so it begins at (fileage, 0) and ends at
224
 * (filebot, strlen(filebot->data)) again. */
225
226
227
void unpartition_filestruct(partition **p)
{
    char *tmp;
228

229
    assert(p != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
230

231
232
233
    /* 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. */
234
235
236
237
238
239
240
    tmp = mallocstrcpy(NULL, openfile->fileage->data);
    openfile->fileage->prev = (*p)->top_prev;
    if (openfile->fileage->prev != NULL)
	openfile->fileage->prev->next = openfile->fileage;
    openfile->fileage->data = charealloc(openfile->fileage->data,
	strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
    strcpy(openfile->fileage->data, (*p)->top_data);
241
    free((*p)->top_data);
242
    strcat(openfile->fileage->data, tmp);
243
    free(tmp);
244

245
246
247
    /* 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. */
248
249
250
251
252
253
    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);
254
    free((*p)->bot_data);
255

256
257
258
    /* Restore the top and bottom of the filestruct, if they were
     * different from the top and bottom of the partition. */
    if ((*p)->fileage != NULL)
259
	openfile->fileage = (*p)->fileage;
260
    if ((*p)->filebot != NULL)
261
	openfile->filebot = (*p)->filebot;
262

263
264
265
266
    /* Uninitialize the partition. */
    free(*p);
    *p = NULL;
}
267

268
269
270
271
272
273
274
275
/* 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;
276
    bool edittop_inside;
277
#ifndef NANO_TINY
278
279
    bool mark_inside = FALSE;
#endif
280

281
    assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
282

283
284
285
    /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
    if (top == bot && top_x == bot_x)
	return;
286

287
288
    /* 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
289
     * the edit window is inside the partition, and keep track of
290
291
     * whether the mark begins inside the partition. */
    filepart = partition_filestruct(top, top_x, bot, bot_x);
292
293
294
    edittop_inside = (openfile->edittop->lineno >=
	openfile->fileage->lineno && openfile->edittop->lineno <=
	openfile->filebot->lineno);
295
#ifndef NANO_TINY
296
    if (openfile->mark_set)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
297
	mark_inside = (openfile->mark_begin->lineno >=
298
		openfile->fileage->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
299
		openfile->mark_begin->lineno <=
300
		openfile->filebot->lineno &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
301
302
303
304
		(openfile->mark_begin != openfile->fileage ||
		openfile->mark_begin_x >= top_x) &&
		(openfile->mark_begin != openfile->filebot ||
		openfile->mark_begin_x <= bot_x));
305
#endif
306

307
308
    /* Get the number of characters in the text, and subtract it from
     * totsize. */
309
    openfile->totsize -= get_totsize(top, bot);
310

311
312
313
314
    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. */
315
316
	*file_top = openfile->fileage;
	*file_bot = openfile->filebot;
317
318
319

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

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

330
331
332
	/* 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. */
333
	(*file_bot)->next = openfile->fileage->next;
334
335
	if ((*file_bot)->next != NULL) {
	    (*file_bot)->next->prev = *file_bot;
336
	    *file_bot = openfile->filebot;
337
	}
338
339
340
341
342

	/* Renumber starting with the line after the original
	 * file_bot. */
	if (file_bot_save->next != NULL)
	    renumber(file_bot_save->next);
343
    }
344

345
    /* Since the text has now been saved, remove it from the filestruct.
346
347
     * If the mark begins inside the partition, set the beginning of the
     * mark to where the saved text used to start. */
348
349
350
    openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct));
    openfile->fileage->data = mallocstrcpy(NULL, "");
    openfile->filebot = openfile->fileage;
351
#ifndef NANO_TINY
352
    if (mark_inside) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
353
354
	openfile->mark_begin = openfile->fileage;
	openfile->mark_begin_x = top_x;
355
356
    }
#endif
357

358
    /* Restore the current line and cursor position. */
359
360
    openfile->current = openfile->fileage;
    openfile->current_x = top_x;
361

362
    top_save = openfile->fileage;
363

364
365
366
    /* Unpartition the filestruct so that it contains all the text
     * again, minus the saved text. */
    unpartition_filestruct(&filepart);
367

368
369
370
371
    /* If the top of the edit window was inside the old partition, put
     * it in range of current. */
    if (edittop_inside)
	edit_update(
372
#ifndef NANO_TINY
373
374
375
376
		ISSET(SMOOTH_SCROLL) ? NONE :
#endif
		CENTER);

377
378
379
    /* Renumber starting with the beginning line of the old
     * partition. */
    renumber(top_save);
380

381
382
383
    /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
     * a magicline, add a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
384
385
	new_magicline();
}
386

387
388
389
390
391
392
/* Copy all the text from the filestruct beginning with file_top and
 * ending with file_bot to the current filestruct at the current cursor
 * position. */
void copy_from_filestruct(filestruct *file_top, filestruct *file_bot)
{
    filestruct *top_save;
393
    bool edittop_inside;
394

395
    assert(file_top != NULL && file_bot != NULL);
396

397
    /* Partition the filestruct so that it contains no text, and keep
398
399
     * track of whether the top of the edit window is inside the
     * partition. */
400
401
    filepart = partition_filestruct(openfile->current,
	openfile->current_x, openfile->current, openfile->current_x);
402
    edittop_inside = (openfile->edittop == openfile->fileage);
403

404
405
    /* Put the top and bottom of the filestruct at copies of file_top
     * and file_bot. */
406
407
408
409
    openfile->fileage = copy_filestruct(file_top);
    openfile->filebot = openfile->fileage;
    while (openfile->filebot->next != NULL)
	openfile->filebot = openfile->filebot->next;
410

411
    /* Restore the current line and cursor position. */
412
413
414
415
    openfile->current = openfile->filebot;
    openfile->current_x = strlen(openfile->filebot->data);
    if (openfile->fileage == openfile->filebot)
	openfile->current_x += strlen(filepart->top_data);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
416

417
    /* Get the number of characters in the copied text, and add it to
418
419
420
     * totsize. */
    openfile->totsize += get_totsize(openfile->fileage,
	openfile->filebot);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
421

422
423
424
    /* 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. */
425
    openfile->current_y += openfile->filebot->lineno - 1;
426

427
    top_save = openfile->fileage;
428

429
430
431
432
433
    /* If the top of the edit window is inside the partition, set it to
     * where the copied text now starts. */
    if (edittop_inside)
	openfile->edittop = openfile->fileage;

434
    /* Unpartition the filestruct so that it contains all the text
435
     * again, plus the copied text. */
436
    unpartition_filestruct(&filepart);
437

438
439
440
    /* Renumber starting with the beginning line of the old
     * partition. */
    renumber(top_save);
441

442
443
444
    /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
     * a magicline, add a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
445
	new_magicline();
Chris Allegretta's avatar
Chris Allegretta committed
446
447
}

448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
/* Create a new openfilestruct node. */
openfilestruct *make_new_opennode(void)
{
    openfilestruct *newnode =
	(openfilestruct *)nmalloc(sizeof(openfilestruct));

    newnode->filename = NULL;
    newnode->fileage = NULL;
    newnode->filebot = NULL;
    newnode->edittop = NULL;
    newnode->current = NULL;

    return newnode;
}

/* Splice a node into an existing openfilestruct. */
void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
	openfilestruct *end)
{
    assert(newnode != NULL && begin != NULL);

    newnode->next = end;
    newnode->prev = begin;
    begin->next = newnode;

    if (end != NULL)
	end->prev = newnode;
}

/* Unlink a node from the rest of the openfilestruct, and delete it. */
void unlink_opennode(openfilestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);

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

    delete_opennode(fileptr);
}

/* Delete a node from the openfilestruct. */
void delete_opennode(openfilestruct *fileptr)
{
    assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);

    free(fileptr->filename);
    free_filestruct(fileptr->fileage);
495
#ifndef NANO_TINY
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
    if (fileptr->current_stat != NULL)
	free(fileptr->current_stat);
#endif

    free(fileptr);
}

#ifdef DEBUG
/* Deallocate all memory associated with this and later files, including
 * the lines of text. */
void free_openfilestruct(openfilestruct *src)
{
    assert(src != NULL);

    while (src != src->next) {
	src = src->next;
	delete_opennode(src->prev);
    }

    delete_opennode(src);
}
#endif

519
/* Display a warning about a key disabled in view mode. */
520
void print_view_warning(void)
521
{
522
    statusbar(_("Key illegal in VIEW mode"));
523
524
}

525
526
/* What we do when we're all set to exit. */
void finish(void)
Chris Allegretta's avatar
Chris Allegretta committed
527
{
528
529
530
531
    if (!ISSET(NO_HELP))
	blank_bottombars();
    else
	blank_statusbar();
532

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
533
    wrefresh(bottomwin);
534

535
    endwin();
536

537
538
    /* Restore the old terminal settings. */
    tcsetattr(0, TCSANOW, &oldterm);
539

540
#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
541
    if (!no_rcfiles && ISSET(HISTORYLOG))
542
543
544
545
546
547
548
549
	save_history();
#endif

#ifdef DEBUG
    thanks_for_all_the_fish();
#endif

    exit(0);
550
551
}

552
553
/* Die (gracefully?). */
void die(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
554
{
555
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
556

557
    endwin();
Chris Allegretta's avatar
Chris Allegretta committed
558

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

562
563
564
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
565

566
    /* Save the current file buffer if it's been modified. */
567
    if (openfile->modified) {
568
569
570
571
	/* If we've partitioned the filestruct, unpartition it now. */
	if (filepart != NULL)
	    unpartition_filestruct(&filepart);

572
	die_save_file(openfile->filename);
Chris Allegretta's avatar
Chris Allegretta committed
573
574
    }

575
576
#ifdef ENABLE_MULTIBUFFER
    /* Save all of the other modified file buffers, if any. */
577
578
    if (openfile != NULL) {
	openfilestruct *tmp = openfile;
Chris Allegretta's avatar
Chris Allegretta committed
579

580
581
	while (tmp != openfile->next) {
	    openfile = openfile->next;
582

583
	    /* Save the current file buffer if it's been modified. */
584
	    if (openfile->modified)
585
		die_save_file(openfile->filename);
586
	}
587
    }
588
589
590
591
#endif

    /* Get out. */
    exit(1);
592
593
}

594
595
/* Save the current file under the name spacified in die_filename, which
 * is modified to be unique if necessary. */
596
void die_save_file(const char *die_filename)
597
{
598
599
    char *retval;
    bool failed = TRUE;
600

601
602
603
604
605
    /* 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;
606

607
608
609
610
    /* If we can't save, we have REAL bad problems, but we might as well
     * TRY. */
    if (die_filename[0] == '\0')
	die_filename = "nano";
611

612
613
    retval = get_next_filename(die_filename, ".save");
    if (retval[0] != '\0')
614
615
	failed = (write_file(retval, NULL, TRUE, OVERWRITE,
		TRUE) == -1);
616

617
618
619
620
621
622
623
624
    if (!failed)
	fprintf(stderr, _("\nBuffer written to %s\n"), retval);
    else if (retval[0] != '\0')
	fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
		strerror(errno));
    else
	fprintf(stderr, _("\nBuffer not written: %s\n"),
		_("Too many backup files?"));
625

626
627
    free(retval);
}
628

629
/* Initialize the three window portions nano uses. */
630
void window_init(void)
631
{
632
    /* If the screen height is too small, get out. */
633
    editwinrows = LINES - 5 + no_more_space() + no_help();
634
    if (COLS < MIN_EDITOR_COLS || editwinrows < MIN_EDITOR_ROWS)
635
	die(_("Window size is too small for nano...\n"));
636

637
#ifndef DISABLE_WRAPJUSTIFY
638
    /* Set up fill, based on the screen width. */
639
640
641
642
643
644
    fill = wrap_at;
    if (fill <= 0)
	fill += COLS;
    if (fill < 0)
	fill = 0;
#endif
645

646
647
648
649
650
651
    if (topwin != NULL)
	delwin(topwin);
    if (edit != NULL)
	delwin(edit);
    if (bottomwin != NULL)
	delwin(bottomwin);
652

653
654
655
    /* Set up the windows. */
    topwin = newwin(2 - no_more_space(), COLS, 0, 0);
    edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0);
656
657
    bottomwin = newwin(3 - no_help(), COLS, editwinrows + (2 -
	no_more_space()), 0);
658

659
    /* Turn the keypad on for the windows, if necessary. */
660
    if (!ISSET(REBIND_KEYPAD)) {
661
	keypad(topwin, TRUE);
662
663
664
	keypad(edit, TRUE);
	keypad(bottomwin, TRUE);
    }
665
666
}

667
#ifndef DISABLE_MOUSE
668
/* Initialize mouse support. */
669
void mouse_init(void)
670
{
671
672
673
674
675
676
    if (ISSET(USE_MOUSE)) {
	mousemask(BUTTON1_RELEASED, NULL);
	mouseinterval(50);
    } else
	mousemask(0, NULL);
}
677
678
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
679
#ifdef HAVE_GETOPT_LONG
680
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, longflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
681
#else
682
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, desc)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
683
684
#endif

685
/* Print one usage string to the screen.  This cuts down on duplicate
686
 * strings to translate, and leaves out the parts that shouldn't be
Chris Allegretta's avatar
Chris Allegretta committed
687
 * translatable (the flag names). */
688
void print_opt_full(const char *shortflag
689
690
691
692
#ifdef HAVE_GETOPT_LONG
	, const char *longflag
#endif
	, const char *desc)
693
694
695
696
697
698
699
700
701
702
703
704
705
{
    printf(" %s\t", shortflag);
    if (strlen(shortflag) < 8)
	printf("\t");

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

706
707
708
    if (desc != NULL)
	printf("%s", _(desc));
    printf("\n");
709
710
}

711
/* Explain how to properly use nano and its command line options. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
712
void usage(void)
Chris Allegretta's avatar
Chris Allegretta committed
713
{
714
    printf(_("Usage: nano [OPTIONS] [[+LINE[,COLUMN]] FILE]...\n\n"));
715
    printf(
716
717
#ifdef HAVE_GETOPT_LONG
	_("Option\t\tGNU long option\t\tMeaning\n")
Chris Allegretta's avatar
Chris Allegretta committed
718
#else
719
	_("Option\t\tMeaning\n")
720
#endif
721
	);
722
    print_opt("-h, -?", "--help", N_("Show this message"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
723
    print_opt(_("+LINE[,COLUMN]"), "",
724
	N_("Start at line LINE, column COLUMN"));
725
#ifndef NANO_TINY
726
727
    print_opt("-A", "--smarthome", N_("Enable smart home key"));
    print_opt("-B", "--backup", N_("Save backups of existing files"));
728
    print_opt(_("-C <dir>"), _("--backupdir=<dir>"),
729
	N_("Directory for saving unique backup files"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
730
#endif
731
    print_opt("-D", "--boldtext",
732
	N_("Use bold instead of reverse video text"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
733
#ifndef NANO_TINY
734
    print_opt("-E", "--tabstospaces",
735
	N_("Convert typed tabs to spaces"));
736
#endif
737
#ifdef ENABLE_MULTIBUFFER
738
    print_opt("-F", "--multibuffer", N_("Enable multiple file buffers"));
Chris Allegretta's avatar
Chris Allegretta committed
739
740
#endif
#ifdef ENABLE_NANORC
741
#ifndef NANO_TINY
742
    print_opt("-H", "--historylog",
743
	N_("Log & read search/replace string history"));
744
#endif
745
    print_opt("-I", "--ignorercfiles",
746
	N_("Don't look at nanorc files"));
Chris Allegretta's avatar
Chris Allegretta committed
747
#endif
748
    print_opt("-K", "--rebindkeypad",
749
	N_("Fix numeric keypad key confusion problem"));
750
    print_opt("-L", "--nonewlines",
751
	N_("Don't add newlines to the ends of files"));
752
#ifndef NANO_TINY
753
    print_opt("-N", "--noconvert",
754
	N_("Don't convert files from DOS/Mac format"));
755
#endif
756
    print_opt("-O", "--morespace", N_("Use more space for editing"));
757
#ifndef DISABLE_JUSTIFY
758
    print_opt(_("-Q <str>"), _("--quotestr=<str>"),
759
	N_("Quoting string"));
760
#endif
761
    print_opt("-R", "--restricted", N_("Restricted mode"));
762
#ifndef NANO_TINY
763
    print_opt("-S", "--smooth", N_("Smooth scrolling"));
764
#endif
765
    print_opt(_("-T <#cols>"), _("--tabsize=<#cols>"),
766
	N_("Set width of a tab in cols to #cols"));
767
#ifndef NANO_TINY
768
    print_opt("-U", "--quickblank", N_("Do quick statusbar blanking"));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
769
#endif
770
    print_opt("-V", "--version",
771
	N_("Print version information and exit"));
772
#ifndef NANO_TINY
773
    print_opt("-W", "--wordbounds",
774
775
	N_("Detect word boundaries more accurately"));
#endif
776
#ifdef ENABLE_COLOR
777
    print_opt(_("-Y <str>"), _("--syntax=<str>"),
778
	N_("Syntax definition to use"));
779
#endif
780
781
    print_opt("-c", "--const", N_("Constantly show cursor position"));
    print_opt("-d", "--rebinddelete",
782
	N_("Fix Backspace/Delete confusion problem"));
783
#ifndef NANO_TINY
784
    print_opt("-i", "--autoindent",
785
	N_("Automatically indent new lines"));
786
    print_opt("-k", "--cut", N_("Cut from cursor to end of line"));
787
#endif
788
    print_opt("-l", "--nofollow",
789
	N_("Don't follow symbolic links, overwrite"));
790
#ifndef DISABLE_MOUSE
791
    print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
Chris Allegretta's avatar
Chris Allegretta committed
792
#endif
793
#ifndef DISABLE_OPERATINGDIR
794
    print_opt(_("-o <dir>"), _("--operatingdir=<dir>"),
795
	N_("Set operating directory"));
Chris Allegretta's avatar
Chris Allegretta committed
796
#endif
797
    print_opt("-p", "--preserve",
798
	N_("Preserve XON (^Q) and XOFF (^S) keys"));
799
#ifndef DISABLE_WRAPJUSTIFY
800
    print_opt(_("-r <#cols>"), _("--fill=<#cols>"),
801
	N_("Set fill cols to (wrap lines at) #cols"));
802
#endif
803
#ifndef DISABLE_SPELLER
804
    print_opt(_("-s <prog>"), _("--speller=<prog>"),
805
	N_("Enable alternate speller"));
806
#endif
807
    print_opt("-t", "--tempfile",
808
	N_("Auto save on exit, don't prompt"));
809
    print_opt("-v", "--view", N_("View (read only) mode"));
810
#ifndef DISABLE_WRAPPING
811
    print_opt("-w", "--nowrap", N_("Don't wrap long lines"));
Chris Allegretta's avatar
Chris Allegretta committed
812
#endif
813
814
    print_opt("-x", "--nohelp", N_("Don't show the two help lines"));
    print_opt("-z", "--suspend", N_("Enable suspension"));
Chris Allegretta's avatar
Chris Allegretta committed
815

816
    /* This is a special case. */
817
818
    print_opt("-a, -b, -e,", "", NULL);
    print_opt("-f, -g, -j", "", N_("(ignored, for Pico compatibility)"));
819

Chris Allegretta's avatar
Chris Allegretta committed
820
821
822
    exit(0);
}

823
824
825
/* 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
826
void version(void)
Chris Allegretta's avatar
Chris Allegretta committed
827
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
828
829
830
831
    printf(_(" GNU nano version %s (compiled %s, %s)\n"), VERSION,
	__TIME__, __DATE__);
    printf(
	_(" Email: nano@nano-editor.org	Web: http://www.nano-editor.org/"));
832
    printf(_("\n Compiled options:"));
833

834
#ifdef DISABLE_BROWSER
835
    printf(" --disable-browser");
836
#endif
837
838
#ifdef DISABLE_HELP
    printf(" --disable-help");
839
840
#endif
#ifdef DISABLE_JUSTIFY
841
    printf(" --disable-justify");
842
#endif
843
#ifdef DISABLE_MOUSE
844
    printf(" --disable-mouse");
845
#endif
846
847
848
#ifndef ENABLE_NLS
    printf(" --disable-nls");
#endif
849
850
851
#ifdef DISABLE_OPERATINGDIR
    printf(" --disable-operatingdir");
#endif
852
853
854
855
856
857
858
859
860
#ifdef DISABLE_SPELLER
    printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
    printf(" --disable-tabcomp");
#endif
#ifdef DISABLE_WRAPPING
    printf(" --disable-wrapping");
#endif
861
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
862
863
    printf(" --disable-wrapping-as-root");
#endif
864
865
866
#ifdef ENABLE_COLOR
    printf(" --enable-color");
#endif
867
868
869
870
871
872
#ifdef DEBUG
    printf(" --enable-debug");
#endif
#ifdef NANO_EXTRA
    printf(" --enable-extra");
#endif
873
874
875
876
877
878
#ifdef ENABLE_MULTIBUFFER
    printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
    printf(" --enable-nanorc");
#endif
879
#ifdef NANO_TINY
880
881
    printf(" --enable-tiny");
#endif
882
#ifdef ENABLE_UTF8
883
884
    printf(" --enable-utf8");
#endif
885
886
887
888
#ifdef USE_SLANG
    printf(" --with-slang");
#endif
    printf("\n");
Chris Allegretta's avatar
Chris Allegretta committed
889
890
}

891
892
893
/* Return 1 if the MORE_SPACE flag is set, and 0 otherwise.  This is
 * used to calculate the relative screen position while taking this flag
 * into account, since it adds one line to the edit window. */
894
895
896
897
898
int no_more_space(void)
{
    return ISSET(MORE_SPACE) ? 1 : 0;
}

899
900
901
/* Return 2 if the NO_HELP flag is set, and 0 otherwise.  This is used
 * to calculate the relative screen position while taking this flag into
 * account, since it removes two lines from the edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
902
903
int no_help(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
904
    return ISSET(NO_HELP) ? 2 : 0;
Chris Allegretta's avatar
Chris Allegretta committed
905
906
}

907
/* Indicate a disabled function on the statusbar. */
908
void nano_disabled_msg(void)
909
{
Chris Allegretta's avatar
Chris Allegretta committed
910
    statusbar(_("Sorry, support for this function has been disabled"));
911
912
}

913
void do_exit(void)
Chris Allegretta's avatar
Chris Allegretta committed
914
{
915
916
    int i;

917
918
    /* If the file hasn't been modified, pretend the user chose not to
     * save. */
919
    if (!openfile->modified)
920
	i = 0;
921
    /* If the TEMP_FILE flag is set, pretend the user chose to save. */
922
    else if (ISSET(TEMP_FILE))
923
	i = 1;
924
    /* Otherwise, ask the user whether or not to save. */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
925
    else
926
	i = do_yesno_prompt(FALSE,
927
928
		_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));

929
#ifdef DEBUG
930
    dump_filestruct(openfile->fileage);
931
#endif
932

933
934
    /* If the user chose not to save, or if the user chose to save and
     * the save succeeded, we're ready to exit. */
935
    if (i == 0 || (i == 1 && do_writeout(TRUE) == 0)) {
936
#ifdef ENABLE_MULTIBUFFER
937
	/* Exit only if there are no more open file buffers. */
938
	if (!close_buffer())
939
#endif
940
	    finish();
941
    /* If the user canceled, we go on. */
942
    } else if (i != 1)
943
944
945
946
947
	statusbar(_("Cancelled"));

    display_main_list();
}

948
/* Initialize the signal handlers. */
949
950
void signal_init(void)
{
951
952
    /* Trap SIGINT and SIGQUIT because we want them to do useful
     * things. */
953
954
955
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);
956
    sigaction(SIGQUIT, &act, NULL);
957

958
    /* Trap SIGHUP and SIGTERM because we want to write the file out. */
959
    act.sa_handler = handle_hupterm;
960
    sigaction(SIGHUP, &act, NULL);
961
    sigaction(SIGTERM, &act, NULL);
962

963
#ifndef NANO_TINY
964
    /* Trap SIGWINCH because we want to handle window resizes. */
965
966
    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);
967
    allow_pending_sigwinch(FALSE);
968
#endif
Chris Allegretta's avatar
Chris Allegretta committed
969

970
    /* Trap normal suspend (^Z) so we can handle it ourselves. */
971
972
973
974
    if (!ISSET(SUSPEND)) {
	act.sa_handler = SIG_IGN;
	sigaction(SIGTSTP, &act, NULL);
    } else {
975
976
	/* Block all other signals in the suspend and continue handlers.
	 * If we don't do this, other stuff interrupts them! */
977
	sigfillset(&act.sa_mask);
Chris Allegretta's avatar
Chris Allegretta committed
978

979
980
	act.sa_handler = do_suspend;
	sigaction(SIGTSTP, &act, NULL);
981

982
	act.sa_handler = do_continue;
983
984
985
	sigaction(SIGCONT, &act, NULL);
    }
}
986

987
/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
988
RETSIGTYPE handle_hupterm(int signal)
989
{
990
    die(_("Received SIGHUP or SIGTERM\n"));
991
}
992

993
/* Handler for SIGTSTP (suspend). */
994
RETSIGTYPE do_suspend(int signal)
995
{
996
    /* Temporarily leave curses mode. */
997
    endwin();
998

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
999
    printf("\n\n\n\n\n\n%s\n", _("Use \"fg\" to return to nano."));
1000
    fflush(stdout);
1001

1002
    /* Restore the old terminal settings. */
1003
    tcsetattr(0, TCSANOW, &oldterm);
1004

1005
    /* Trap SIGHUP and SIGTERM so we can properly deal with them while
1006
     * suspended. */
1007
1008
1009
1010
    act.sa_handler = handle_hupterm;
    sigaction(SIGHUP, &act, NULL);
    sigaction(SIGTERM, &act, NULL);

1011
    /* Do what mutt does: send ourselves a SIGSTOP. */
1012
1013
    kill(0, SIGSTOP);
}
1014

1015
/* Handler for SIGCONT (continue after suspend). */
1016
RETSIGTYPE do_continue(int signal)
1017
{
1018
#ifndef NANO_TINY
1019
    /* Perhaps the user resized the window while we slept.  Handle it,
1020
     * and update the screen in the process. */
1021
    handle_sigwinch(0);
1022
#else
1023
    /* Reenter curses mode, and update the screen in the process. */
1024
    doupdate();
1025
1026
1027
#endif
}

1028
#ifndef NANO_TINY
1029
/* Handler for SIGWINCH (window size change). */
1030
RETSIGTYPE handle_sigwinch(int signal)
1031
1032
{
    const char *tty = ttyname(0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1033
    int fd, result = 0;
1034
1035
    struct winsize win;

1036
    if (tty == NULL)
1037
1038
1039
1040
1041
1042
1043
1044
1045
	return;
    fd = open(tty, O_RDWR);
    if (fd == -1)
	return;
    result = ioctl(fd, TIOCGWINSZ, &win);
    close(fd);
    if (result == -1)
	return;

1046
1047
1048
1049
    /* 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. */
1050
1051
1052
    COLS = win.ws_col;
    LINES = win.ws_row;

1053
1054
    /* If we've partitioned the filestruct, unpartition it now. */
    if (filepart != NULL)
1055
	unpartition_filestruct(&filepart);
1056

1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 1: If we just do what
     * curses does here, it'll only work properly if the resize made the
     * window smaller.  Do what mutt does: Leave and immediately reenter
     * Slang screen management mode. */
    SLsmg_reset_smg();
    SLsmg_init_smg();
#else
    /* Do the equivalent of what Minimum Profit does: Leave and
     * immediately reenter curses mode. */
    endwin();
1068
    doupdate();
1069
#endif
1070

1071
1072
1073
    /* Restore the terminal to its previous state. */
    terminal_init();

1074
1075
1076
    /* Turn the cursor back on for sure. */
    curs_set(1);

1077
1078
1079
1080
1081
    /* Do the equivalent of what both mutt and Minimum Profit do:
     * Reinitialize all the windows based on the new screen
     * dimensions. */
    window_init();

1082
    /* Redraw the contents of the windows that need it. */
1083
    blank_statusbar();
1084
    currshortcut = main_list;
1085
1086
    total_refresh();

1087
1088
1089
    /* Jump back to either main() or the unjustify routine in
     * do_justify(). */
    siglongjmp(jump_buf, 1);
1090
}
1091

1092
1093
1094
/* If allow is TRUE, block any SIGWINCH signals that we get, so that we
 * can deal with them later.  If allow is FALSE, unblock any SIGWINCH
 * signals that we have, so that we can deal with them now. */
1095
void allow_pending_sigwinch(bool allow)
1096
1097
1098
1099
{
    sigset_t winch;
    sigemptyset(&winch);
    sigaddset(&winch, SIGWINCH);
1100
    sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
1101
}
1102
#endif /* !NANO_TINY */
1103

1104
#ifndef NANO_TINY
1105
/* Handle the global toggle specified in which. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1106
void do_toggle(const toggle *which)
1107
{
1108
    bool enabled;
1109

1110
    TOGGLE(which->flag);
Chris Allegretta's avatar
Chris Allegretta committed
1111

1112
    switch (which->val) {
1113
#ifndef DISABLE_MOUSE
1114
1115
1116
	case TOGGLE_MOUSE_KEY:
	    mouse_init();
	    break;
1117
#endif
1118
	case TOGGLE_MORESPACE_KEY:
1119
1120
	case TOGGLE_NOHELP_KEY:
	    window_init();
1121
	    total_refresh();
1122
	    break;
1123
1124
	case TOGGLE_SUSPEND_KEY:
	    signal_init();
1125
	    break;
1126
#ifdef ENABLE_NANORC
1127
	case TOGGLE_WHITESPACE_KEY:
1128
	    titlebar(NULL);
1129
1130
	    edit_refresh();
	    break;
1131
1132
1133
1134
1135
#endif
#ifdef ENABLE_COLOR
	case TOGGLE_SYNTAX_KEY:
	    edit_refresh();
	    break;
1136
#endif
1137
    }
Chris Allegretta's avatar
Chris Allegretta committed
1138

Chris Allegretta's avatar
Chris Allegretta committed
1139
    enabled = ISSET(which->flag);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1140

1141
1142
1143
1144
1145
1146
1147
1148
    if (which->val == TOGGLE_NOHELP_KEY
#ifndef DISABLE_WRAPPING
	|| which->val == TOGGLE_WRAP_KEY
#endif
#ifdef ENABLE_COLOR
	|| which->val == TOGGLE_SYNTAX_KEY
#endif
	)
Chris Allegretta's avatar
Chris Allegretta committed
1149
	enabled = !enabled;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1150
1151
1152

    statusbar("%s %s", which->desc, enabled ? _("enabled") :
	_("disabled"));
Chris Allegretta's avatar
Chris Allegretta committed
1153
}
1154
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
1155

1156
1157
/* Disable extended input and output processing in our terminal
 * settings. */
1158
void disable_extended_io(void)
1159
1160
1161
1162
1163
{
    struct termios term;

    tcgetattr(0, &term);
    term.c_lflag &= ~IEXTEN;
1164
    term.c_oflag &= ~OPOST;
1165
1166
1167
    tcsetattr(0, TCSANOW, &term);
}

1168
1169
/* Disable interpretation of the special control keys in our terminal
 * settings. */
1170
1171
1172
1173
1174
1175
1176
1177
1178
void disable_signals(void)
{
    struct termios term;

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

1179
#ifndef NANO_TINY
1180
1181
/* Enable interpretation of the special control keys in our terminal
 * settings. */
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
void enable_signals(void)
{
    struct termios term;

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

1192
1193
/* Disable interpretation of the flow control characters in our terminal
 * settings. */
1194
1195
1196
1197
1198
void disable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1199
    term.c_iflag &= ~IXON;
1200
1201
1202
    tcsetattr(0, TCSANOW, &term);
}

1203
1204
/* Enable interpretation of the flow control characters in our terminal
 * settings. */
1205
1206
1207
1208
1209
void enable_flow_control(void)
{
    struct termios term;

    tcgetattr(0, &term);
1210
    term.c_iflag |= IXON;
1211
1212
1213
    tcsetattr(0, TCSANOW, &term);
}

1214
1215
1216
1217
/* Set up the terminal state.  Put the terminal in cbreak mode (read one
 * character at a time and interpret the special control keys), disable
 * translation of carriage return (^M) into newline (^J) so that we can
 * tell the difference between the Enter key and Ctrl-J, and disable
1218
 * echoing of characters as they're typed.  Finally, disable extended
1219
1220
1221
 * input and output processing, disable interpretation of the special
 * control keys, and if we're not in preserve mode, disable
 * interpretation of the flow control characters too. */
1222
1223
1224
1225
1226
void terminal_init(void)
{
    cbreak();
    nonl();
    noecho();
1227
    disable_extended_io();
1228
1229
1230
1231
1232
    disable_signals();
    if (!ISSET(PRESERVE))
	disable_flow_control();
}

1233
1234
1235
1236
1237
1238
1239
1240
1241
/* Read in a character, interpret it as a shortcut or toggle if
 * necessary, and return it.  Set meta_key to TRUE if the character is a
 * meta sequence, set func_key to TRUE if the character is a function
 * key, set s_or_t to TRUE if the character is a shortcut or toggle
 * key, set ran_func to TRUE if we ran a function associated with a
 * shortcut key, and set finished to TRUE if we're done after running
 * or trying to run a function associated with a shortcut key.  If
 * allow_funcs is FALSE, don't actually run any functions associated
 * with shortcut keys. */
1242
int do_input(bool *meta_key, bool *func_key, bool *s_or_t, bool
1243
	*ran_func, bool *finished, bool allow_funcs)
1244
1245
1246
1247
1248
1249
1250
{
    int input;
	/* The character we read in. */
    static int *kbinput = NULL;
	/* The input buffer. */
    static size_t kbinput_len = 0;
	/* The length of the input buffer. */
1251
1252
    bool cut_copy = FALSE;
	/* Are we cutting or copying text? */
1253
1254
    const shortcut *s;
    bool have_shortcut;
1255
#ifndef NANO_TINY
1256
1257
1258
1259
1260
    const toggle *t;
    bool have_toggle;
#endif

    *s_or_t = FALSE;
1261
    *ran_func = FALSE;
1262
    *finished = FALSE;
1263
1264
1265
1266
1267

    /* Read in a character. */
    input = get_kbinput(edit, meta_key, func_key);

#ifndef DISABLE_MOUSE
1268
    if (allow_funcs) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1269
1270
	/* If we got a mouse click and it was on a shortcut, read in the
	 * shortcut character. */
1271
1272
1273
1274
1275
1276
1277
1278
1279
	if (*func_key == TRUE && input == KEY_MOUSE) {
	    if (do_mouse())
		input = get_kbinput(edit, meta_key, func_key);
	    else {
		*meta_key = FALSE;
		*func_key = FALSE;
		input = ERR;
	    }
	}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1280
    }
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
#endif

    /* Check for a shortcut in the main list. */
    s = get_shortcut(main_list, &input, meta_key, func_key);

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

1291
#ifndef NANO_TINY
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
    /* Check for a toggle in the main list. */
    t = get_toggle(input, *meta_key);

    /* If we got a toggle from the main list, set have_toggle to
     * TRUE. */
    have_toggle = (t != NULL);
#endif

    /* Set s_or_t to TRUE if we got a shortcut or toggle. */
    *s_or_t = (have_shortcut
1302
#ifndef NANO_TINY
1303
1304
1305
1306
	|| have_toggle
#endif
	);

1307
1308
    /* 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. */
1309
    if (*s_or_t == FALSE) {
1310
1311
	if (is_ascii_cntrl_char(input) || *meta_key == TRUE ||
		*func_key == TRUE) {
1312
	    statusbar(_("Unknown Command"));
1313
	    beep();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1314
1315
	    *meta_key = FALSE;
	    *func_key = FALSE;
1316
	    input = ERR;
1317
1318
1319
	}
    }

1320
    if (allow_funcs) {
1321
1322
1323
1324
1325
	/* If we got a character, and it isn't a shortcut or toggle,
	 * it's a normal text character.  Display the warning if we're
	 * in view mode, or add the character to the input buffer if
	 * we're not. */
	if (input != ERR && *s_or_t == FALSE) {
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
	    if (ISSET(VIEW_MODE))
		print_view_warning();
	    else {
		kbinput_len++;
		kbinput = (int *)nrealloc(kbinput, kbinput_len *
			sizeof(int));
		kbinput[kbinput_len - 1] = input;
	    }
	}

	/* If we got a shortcut or toggle, or if there aren't any other
	 * characters waiting after the one we read in, we need to
1338
	 * output all the characters in the input buffer if it isn't
1339
1340
	 * empty.  Note that it should be empty if we're in view
	 * mode. */
1341
	 if (*s_or_t == TRUE || get_key_buffer_len() == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1342
#ifndef DISABLE_WRAPPING
1343
1344
1345
1346
1347
	    /* If we got a shortcut or toggle, and it's not the shortcut
	     * for verbatim input, turn off prepending of wrapped
	     * text. */
	    if (*s_or_t == TRUE && (!have_shortcut || s == NULL ||
		s->func != do_verbatim_input))
1348
		wrap_reset();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1349
#endif
1350

1351
1352
	    if (kbinput != NULL) {
		/* Display all the characters in the input buffer at
1353
		 * once, filtering out control characters. */
1354
1355
1356
1357
1358
1359
1360
		char *output = charalloc(kbinput_len + 1);
		size_t i;

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

1361
		do_output(output, kbinput_len, FALSE);
1362
1363

		free(output);
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373

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

	if (have_shortcut) {
	    switch (input) {
1374
		/* Handle the "universal" edit window shortcuts. */
1375
		case NANO_XON_KEY:
1376
		    statusbar(_("XON ignored, mumble mumble"));
1377
1378
		    break;
		case NANO_XOFF_KEY:
1379
		    statusbar(_("XOFF ignored, mumble mumble"));
1380
		    break;
1381
#ifndef NANO_TINY
1382
1383
1384
1385
1386
		case NANO_SUSPEND_KEY:
		    if (ISSET(SUSPEND))
			do_suspend(0);
		    break;
#endif
1387
		/* Handle the normal edit window shortcuts, setting
1388
1389
		 * ran_func to TRUE if we try to run their associated
		 * functions and setting finished to TRUE to indicate
1390
1391
		 * that we're done after running or trying to run their
		 * associated functions. */
1392
		default:
1393
1394
1395
		    /* If the function associated with this shortcut is
		     * cutting or copying text, indicate this. */
		    if (s->func == do_cut_text_void
1396
#ifndef NANO_TINY
1397
			|| s->func == do_copy_text || s->func ==
1398
			do_cut_till_end
1399
1400
#endif
			)
1401
			cut_copy = TRUE;
1402

1403
		    if (s->func != NULL) {
1404
			*ran_func = TRUE;
1405
1406
1407
1408
			if (ISSET(VIEW_MODE) && !s->viewok)
			    print_view_warning();
			else
			    s->func();
1409
		    }
1410
		    *finished = TRUE;
1411
1412
1413
		    break;
	    }
	}
1414
#ifndef NANO_TINY
1415
1416
1417
1418
1419
1420
1421
1422
	else if (have_toggle) {
	    /* Toggle the flag associated with this shortcut. */
	    if (allow_funcs)
		do_toggle(t);
	}
#endif
    }

1423
1424
1425
1426
1427
    /* If we aren't cutting or copying text, blow away the text in the
     * cutbuffer. */
    if (!cut_copy)
	cutbuffer_reset();

1428
1429
1430
1431
    return input;
}

#ifndef DISABLE_MOUSE
1432
/* Handle a mouse click on the edit window or the shortcut list. */
1433
1434
1435
bool do_mouse(void)
{
    int mouse_x, mouse_y;
1436
    bool retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
1437
1438
1439
1440
1441
1442
1443

    if (!retval) {
	/* We can click in the edit window to move the cursor. */
	if (wenclose(edit, mouse_y, mouse_x)) {
	    bool sameline;
		/* Did they click on the line with the cursor?  If they
		 * clicked on the cursor, we set the mark. */
1444
	    const filestruct *current_save = openfile->current;
1445
1446
	    size_t current_x_save = openfile->current_x;
	    size_t pww_save = openfile->placewewant;
1447

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1448
	    /* Subtract out the size of topwin. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1449
	    mouse_y -= 2 - no_more_space();
1450

1451
	    sameline = (mouse_y == openfile->current_y);
1452
1453

	    /* Move to where the click occurred. */
1454
	    for (; openfile->current_y < mouse_y &&
1455
1456
		openfile->current != openfile->filebot;
		openfile->current_y++)
1457
1458
		openfile->current = openfile->current->next;
	    for (; openfile->current_y > mouse_y &&
1459
1460
		openfile->current != openfile->fileage;
		openfile->current_y--)
1461
1462
		openfile->current = openfile->current->prev;

1463
1464
1465
	    openfile->current_x = actual_x(openfile->current->data,
		get_page_start(xplustabs()) + mouse_x);
	    openfile->placewewant = xplustabs();
1466

1467
#ifndef NANO_TINY
1468
1469
1470
	    /* Clicking where the cursor is toggles the mark, as does
	     * clicking beyond the line length with the cursor at the
	     * end of the line. */
1471
	    if (sameline && openfile->current_x == current_x_save)
1472
1473
1474
		do_mark();
#endif

1475
	    edit_redraw(current_save, pww_save);
1476
1477
1478
1479
1480
1481
1482
	}
    }

    return retval;
}
#endif /* !DISABLE_MOUSE */

1483
/* The user typed output_len multibyte characters.  Add them to the edit
1484
 * buffer, filtering out all ASCII control characters if allow_cntrls is
1485
1486
 * TRUE. */
void do_output(char *output, size_t output_len, bool allow_cntrls)
1487
{
1488
    size_t current_len, i = 0;
1489
1490
    bool do_refresh = FALSE;
	/* Do we have to call edit_refresh(), or can we get away with
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1491
	 * just update_line()? */
1492

1493
1494
    char *char_buf = charalloc(mb_cur_max());
    int char_buf_len;
1495

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

1498
    current_len = strlen(openfile->current->data);
1499

1500
    while (i < output_len) {
1501
	/* If allow_cntrls is FALSE, filter out nulls and newlines,
1502
	 * since they're ASCII control characters. */
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
	if (allow_cntrls) {
	    /* Null to newline, if needed. */
	    if (output[i] == '\0')
		output[i] = '\n';
	    /* Newline to Enter, if needed. */
	    else if (output[i] == '\n') {
		do_enter();
		i++;
		continue;
	    }
1513
1514
	}

1515
1516
	/* Interpret the next multibyte character. */
	char_buf_len = parse_mbchar(output + i, char_buf, NULL);
1517
1518

	i += char_buf_len;
1519

1520
1521
1522
1523
	/* If allow_cntrls is FALSE, filter out an ASCII control
	 * character. */
	if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -
		char_buf_len)))
1524
1525
	    continue;

1526
1527
1528
1529
	/* If the NO_NEWLINES flag isn't set, when a character is
	 * added to the magicline, it means we need a new magicline! */
	if (!ISSET(NO_NEWLINES) && openfile->filebot ==
		openfile->current)
1530
1531
1532
	    new_magicline();

	/* More dangerousness fun =) */
1533
	openfile->current->data = charealloc(openfile->current->data,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1534
		current_len + (char_buf_len * 2));
1535

1536
	assert(openfile->current_x <= current_len);
1537

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1538
1539
1540
1541
1542
	charmove(openfile->current->data + openfile->current_x +
		char_buf_len, openfile->current->data +
		openfile->current_x, current_len - openfile->current_x +
		char_buf_len);
	strncpy(openfile->current->data + openfile->current_x, char_buf,
1543
		char_buf_len);
1544
	current_len += char_buf_len;
1545
	openfile->totsize++;
1546
1547
	set_modified();

1548
#ifndef NANO_TINY
1549
	/* Note that current_x has not yet been incremented. */
1550
	if (openfile->mark_set && openfile->current ==
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1551
1552
1553
		openfile->mark_begin && openfile->current_x <
		openfile->mark_begin_x)
	    openfile->mark_begin_x += char_buf_len;
1554
1555
#endif

1556
	openfile->current_x += char_buf_len;
1557
1558

#ifndef DISABLE_WRAPPING
1559
1560
	/* If we're wrapping text, we need to call edit_refresh(). */
	if (!ISSET(NO_WRAP)) {
1561
1562
	    bool do_refresh_save = do_refresh;

1563
	    do_refresh = do_wrap(openfile->current);
1564
1565
1566
1567
1568
1569
1570
1571
1572

	    /* If we needed to call edit_refresh() before this, we'll
	     * still need to after this. */
	    if (do_refresh_save)
		do_refresh = TRUE;
	}
#endif

#ifdef ENABLE_COLOR
1573
1574
1575
	/* If color syntaxes are available and turned on, we need to
	 * call edit_refresh(). */
	if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX))
1576
1577
1578
1579
	    do_refresh = TRUE;
#endif
    }

1580
    free(char_buf);
1581

1582
1583
    openfile->placewewant = xplustabs();

1584
1585
1586
    if (do_refresh)
	edit_refresh();
    else
1587
	update_line(openfile->current, openfile->current_x);
1588
1589
}

1590
int main(int argc, char **argv)
Chris Allegretta's avatar
Chris Allegretta committed
1591
1592
{
    int optchr;
1593
    ssize_t startline = 1;
1594
	/* Line to try and start at. */
1595
1596
    ssize_t startcol = 1;
	/* Column to try and start at. */
1597
#ifndef DISABLE_WRAPJUSTIFY
1598
    bool fill_used = FALSE;
1599
	/* Was the fill option used? */
1600
#endif
1601
1602
1603
1604
1605
#ifdef ENABLE_MULTIBUFFER
    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
1606
#ifdef HAVE_GETOPT_LONG
1607
    const struct option long_options[] = {
1608
	{"help", 0, NULL, 'h'},
1609
	{"boldtext", 0, NULL, 'D'},
1610
#ifdef ENABLE_MULTIBUFFER
1611
	{"multibuffer", 0, NULL, 'F'},
Chris Allegretta's avatar
Chris Allegretta committed
1612
1613
#endif
#ifdef ENABLE_NANORC
1614
	{"ignorercfiles", 0, NULL, 'I'},
1615
#endif
1616
	{"rebindkeypad", 0, NULL, 'K'},
1617
	{"nonewlines", 0, NULL, 'L'},
1618
	{"morespace", 0, NULL, 'O'},
1619
#ifndef DISABLE_JUSTIFY
1620
	{"quotestr", 1, NULL, 'Q'},
1621
#endif
1622
	{"restricted", 0, NULL, 'R'},
1623
1624
	{"tabsize", 1, NULL, 'T'},
	{"version", 0, NULL, 'V'},
1625
#ifdef ENABLE_COLOR
1626
	{"syntax", 1, NULL, 'Y'},
1627
#endif
1628
1629
1630
	{"const", 0, NULL, 'c'},
	{"rebinddelete", 0, NULL, 'd'},
	{"nofollow", 0, NULL, 'l'},
1631
#ifndef DISABLE_MOUSE
1632
	{"mouse", 0, NULL, 'm'},
1633
#endif
1634
#ifndef DISABLE_OPERATINGDIR
1635
	{"operatingdir", 1, NULL, 'o'},
1636
#endif
1637
	{"preserve", 0, NULL, 'p'},
1638
#ifndef DISABLE_WRAPJUSTIFY
1639
	{"fill", 1, NULL, 'r'},
1640
1641
#endif
#ifndef DISABLE_SPELLER
1642
	{"speller", 1, NULL, 's'},
1643
#endif
1644
1645
	{"tempfile", 0, NULL, 't'},
	{"view", 0, NULL, 'v'},
1646
#ifndef DISABLE_WRAPPING
1647
	{"nowrap", 0, NULL, 'w'},
1648
#endif
1649
1650
	{"nohelp", 0, NULL, 'x'},
	{"suspend", 0, NULL, 'z'},
1651
#ifndef NANO_TINY
1652
1653
	{"smarthome", 0, NULL, 'A'},
	{"backup", 0, NULL, 'B'},
1654
1655
	{"backupdir", 1, NULL, 'C'},
	{"tabstospaces", 0, NULL, 'E'},
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1656
	{"historylog", 0, NULL, 'H'},
1657
1658
	{"noconvert", 0, NULL, 'N'},
	{"smooth", 0, NULL, 'S'},
1659
	{"quickblank", 0, NULL, 'U'},
1660
	{"wordbounds", 0, NULL, 'W'},
1661
1662
	{"autoindent", 0, NULL, 'i'},
	{"cut", 0, NULL, 'k'},
1663
#endif
1664
	{NULL, 0, NULL, 0}
Chris Allegretta's avatar
Chris Allegretta committed
1665
1666
1667
    };
#endif

1668
#ifdef ENABLE_UTF8
1669
    {
1670
1671
	/* If the locale set exists and includes the case-insensitive
	 * string "UTF8" or "UTF-8", we should use UTF-8. */
1672
1673
	char *locale = setlocale(LC_ALL, "");

1674
1675
	if (locale != NULL && (strcasestr(locale, "UTF8") != NULL ||
		strcasestr(locale, "UTF-8") != NULL)) {
1676
#ifdef USE_SLANG
1677
	    SLutf8_enable(TRUE);
1678
#endif
1679
	    utf8_init();
1680
	}
1681
1682
    }
#else
Chris Allegretta's avatar
Chris Allegretta committed
1683
    setlocale(LC_ALL, "");
1684
1685
#endif

1686
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
1687
1688
1689
1690
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1691
#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
1692
1693
    /* If we don't have rcfile support, we're root, and
     * --disable-wrapping-as-root is used, turn wrapping off. */
1694
    if (geteuid() == NANO_ROOT_UID)
1695
1696
	SET(NO_WRAP);
#endif
1697

1698
    while ((optchr =
Chris Allegretta's avatar
Chris Allegretta committed
1699
#ifdef HAVE_GETOPT_LONG
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1700
	getopt_long(argc, argv,
1701
		"h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pr:s:tvwxz",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1702
		long_options, NULL)
Chris Allegretta's avatar
Chris Allegretta committed
1703
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1704
	getopt(argc, argv,
1705
		"h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pr:s:tvwxz")
Chris Allegretta's avatar
Chris Allegretta committed
1706
#endif
1707
		) != -1) {
Chris Allegretta's avatar
Chris Allegretta committed
1708
	switch (optchr) {
1709
1710
1711
1712
1713
1714
1715
1716
	    case 'a':
	    case 'b':
	    case 'e':
	    case 'f':
	    case 'g':
	    case 'j':
		/* Pico compatibility flags. */
		break;
1717
#ifndef NANO_TINY
1718
1719
1720
1721
1722
1723
	    case 'A':
		SET(SMART_HOME);
		break;
	    case 'B':
		SET(BACKUP_FILE);
		break;
1724
	    case 'C':
1725
1726
		backup_dir = mallocstrcpy(backup_dir, optarg);
		break;
1727
1728
1729
1730
1731
#endif
	    case 'D':
		SET(BOLD_TEXT);
		break;
#ifndef NANO_TINY
1732
1733
1734
	    case 'E':
		SET(TABS_TO_SPACES);
		break;
1735
#endif
1736
#ifdef ENABLE_MULTIBUFFER
1737
1738
1739
	    case 'F':
		SET(MULTIBUFFER);
		break;
Chris Allegretta's avatar
Chris Allegretta committed
1740
1741
#endif
#ifdef ENABLE_NANORC
1742
#ifndef NANO_TINY
1743
1744
1745
	    case 'H':
		SET(HISTORYLOG);
		break;
1746
#endif
1747
	    case 'I':
1748
		no_rcfiles = TRUE;
1749
		break;
1750
#endif
1751
1752
1753
	    case 'K':
		SET(REBIND_KEYPAD);
		break;
1754
1755
1756
	    case 'L':
		SET(NO_NEWLINES);
		break;
1757
#ifndef NANO_TINY
1758
1759
1760
	    case 'N':
		SET(NO_CONVERT);
		break;
1761
#endif
1762
1763
1764
	    case 'O':
		SET(MORE_SPACE);
		break;
1765
#ifndef DISABLE_JUSTIFY
1766
1767
1768
	    case 'Q':
		quotestr = mallocstrcpy(quotestr, optarg);
		break;
1769
#endif
1770
1771
1772
	    case 'R':
		SET(RESTRICTED);
		break;
1773
#ifndef NANO_TINY
1774
	    case 'S':
1775
		SET(SMOOTH_SCROLL);
1776
		break;
1777
#endif
1778
1779
	    case 'T':
		if (!parse_num(optarg, &tabsize) || tabsize <= 0) {
1780
		    fprintf(stderr, _("Requested tab size \"%s\" is invalid"), optarg);
1781
		    fprintf(stderr, "\n");
1782
1783
1784
		    exit(1);
		}
		break;
1785
#ifndef NANO_TINY
1786
1787
1788
1789
	    case 'U':
		SET(QUICK_BLANK);
		break;
#endif
1790
1791
1792
	    case 'V':
		version();
		exit(0);
1793
#ifndef NANO_TINY
1794
1795
1796
1797
	    case 'W':
		SET(WORD_BOUNDS);
		break;
#endif
1798
#ifdef ENABLE_COLOR
1799
1800
1801
	    case 'Y':
		syntaxstr = mallocstrcpy(syntaxstr, optarg);
		break;
1802
#endif
1803
	    case 'c':
1804
		SET(CONST_UPDATE);
1805
1806
1807
1808
		break;
	    case 'd':
		SET(REBIND_DELETE);
		break;
1809
#ifndef NANO_TINY
1810
1811
1812
1813
1814
1815
	    case 'i':
		SET(AUTOINDENT);
		break;
	    case 'k':
		SET(CUT_TO_END);
		break;
1816
#endif
1817
1818
1819
	    case 'l':
		SET(NOFOLLOW_SYMLINKS);
		break;
1820
#ifndef DISABLE_MOUSE
1821
1822
1823
	    case 'm':
		SET(USE_MOUSE);
		break;
1824
#endif
1825
#ifndef DISABLE_OPERATINGDIR
1826
1827
1828
	    case 'o':
		operating_dir = mallocstrcpy(operating_dir, optarg);
		break;
1829
#endif
1830
1831
1832
	    case 'p':
		SET(PRESERVE);
		break;
1833
#ifndef DISABLE_WRAPJUSTIFY
1834
1835
	    case 'r':
		if (!parse_num(optarg, &wrap_at)) {
1836
		    fprintf(stderr, _("Requested fill size \"%s\" is invalid"), optarg);
1837
		    fprintf(stderr, "\n");
1838
1839
		    exit(1);
		}
1840
		fill_used = TRUE;
1841
		break;
1842
#endif
1843
#ifndef DISABLE_SPELLER
1844
1845
1846
	    case 's':
		alt_speller = mallocstrcpy(alt_speller, optarg);
		break;
1847
#endif
1848
1849
1850
1851
1852
1853
	    case 't':
		SET(TEMP_FILE);
		break;
	    case 'v':
		SET(VIEW_MODE);
		break;
1854
#ifndef DISABLE_WRAPPING
1855
1856
1857
	    case 'w':
		SET(NO_WRAP);
		break;
1858
#endif
1859
1860
1861
1862
1863
1864
1865
1866
	    case 'x':
		SET(NO_HELP);
		break;
	    case 'z':
		SET(SUSPEND);
		break;
	    default:
		usage();
Chris Allegretta's avatar
Chris Allegretta committed
1867
1868
1869
	}
    }

1870
1871
    /* If the executable filename starts with 'r', we use restricted
     * mode. */
1872
1873
1874
    if (*(tail(argv[0])) == 'r')
	SET(RESTRICTED);

1875
1876
1877
    /* If we're using restricted mode, disable suspending, backups, and
     * reading rcfiles, since they all would allow reading from or
     * writing to files not specified on the command line. */
1878
1879
1880
    if (ISSET(RESTRICTED)) {
	UNSET(SUSPEND);
	UNSET(BACKUP_FILE);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1881
#ifdef ENABLE_NANORC
1882
	no_rcfiles = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1883
#endif
1884
1885
    }

Chris Allegretta's avatar
Chris Allegretta committed
1886
/* We've read through the command line options.  Now back up the flags
1887
1888
 * and values that are set, and read the rcfile(s).  If the values
 * haven't changed afterward, restore the backed-up values. */
Chris Allegretta's avatar
Chris Allegretta committed
1889
#ifdef ENABLE_NANORC
1890
    if (!no_rcfiles) {
Chris Allegretta's avatar
Chris Allegretta committed
1891
1892
1893
#ifndef DISABLE_OPERATINGDIR
	char *operating_dir_cpy = operating_dir;
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1894
#ifndef DISABLE_WRAPJUSTIFY
1895
	ssize_t wrap_at_cpy = wrap_at;
Chris Allegretta's avatar
Chris Allegretta committed
1896
#endif
1897
#ifndef NANO_TINY
1898
1899
	char *backup_dir_cpy = backup_dir;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1900
1901
1902
1903
1904
1905
#ifndef DISABLE_JUSTIFY
	char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
	char *alt_speller_cpy = alt_speller;
#endif
1906
	ssize_t tabsize_cpy = tabsize;
1907
	long flags_cpy = flags;
Chris Allegretta's avatar
Chris Allegretta committed
1908

1909
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
1910
	operating_dir = NULL;
1911
#endif
1912
#ifndef NANO_TINY
1913
1914
	backup_dir = NULL;
#endif
1915
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
1916
	quotestr = NULL;
1917
1918
#endif
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
1919
	alt_speller = NULL;
1920
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1921
1922
1923
1924
1925
1926
1927
1928
1929

	do_rcfile();

#ifndef DISABLE_OPERATINGDIR
	if (operating_dir_cpy != NULL) {
	    free(operating_dir);
	    operating_dir = operating_dir_cpy;
	}
#endif
1930
#ifndef DISABLE_WRAPJUSTIFY
1931
	if (fill_used)
Chris Allegretta's avatar
Chris Allegretta committed
1932
1933
	    wrap_at = wrap_at_cpy;
#endif
1934
#ifndef NANO_TINY
1935
1936
1937
1938
1939
	if (backup_dir_cpy != NULL) {
	    free(backup_dir);
	    backup_dir = backup_dir_cpy;
	}
#endif	
Chris Allegretta's avatar
Chris Allegretta committed
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
#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
1952
	if (tabsize_cpy != -1)
Chris Allegretta's avatar
Chris Allegretta committed
1953
1954
1955
1956
	    tabsize = tabsize_cpy;
	flags |= flags_cpy;
    }
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
1957
    else if (geteuid() == NANO_ROOT_UID)
Chris Allegretta's avatar
Chris Allegretta committed
1958
1959
1960
1961
	SET(NO_WRAP);
#endif
#endif /* ENABLE_NANORC */

1962
1963
1964
1965
1966
    /* If we're using bold text instead of reverse video text, set it up
     * now. */
    if (ISSET(BOLD_TEXT))
	reverse_attr = A_BOLD;

1967
#ifndef NANO_TINY
1968
    /* Set up the search/replace history. */
1969
1970
    history_init();
#ifdef ENABLE_NANORC
1971
    if (!no_rcfiles && ISSET(HISTORYLOG))
1972
1973
1974
1975
	load_history();
#endif
#endif

1976
#ifndef NANO_TINY
1977
    /* Set up the backup directory (unless we're using restricted mode,
1978
1979
1980
1981
     * 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. */
1982
1983
    if (!ISSET(RESTRICTED))
	init_backup_dir();
1984
1985
#endif

1986
#ifndef DISABLE_OPERATINGDIR
Chris Allegretta's avatar
Chris Allegretta committed
1987
    /* Set up the operating directory.  This entails chdir()ing there,
1988
     * so that file reads and writes will be based there. */
1989
1990
1991
    init_operating_dir();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1992
#ifndef DISABLE_JUSTIFY
1993
    /* If punct wasn't specified, set its default value. */
1994
    if (punct == NULL)
1995
	punct = mallocstrcpy(NULL, "!.?");
1996

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

2001
    /* If quotestr wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2002
    if (quotestr == NULL)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2003
	quotestr = mallocstrcpy(NULL,
Chris Allegretta's avatar
Chris Allegretta committed
2004
#ifdef HAVE_REGEX_H
2005
		"^([ \t]*[#:>|}])+"
Chris Allegretta's avatar
Chris Allegretta committed
2006
#else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2007
		"> "
Chris Allegretta's avatar
Chris Allegretta committed
2008
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2009
		);
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
#ifdef HAVE_REGEX_H
    quoterc = regcomp(&quotereg, quotestr, REG_EXTENDED);

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

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

2028
2029
#ifndef DISABLE_SPELLER
    /* If we don't have an alternative spell checker after reading the
2030
     * command line and/or rcfile(s), check $SPELL for one, as Pico
2031
     * does (unless we're using restricted mode, in which case spell
2032
2033
     * checking is disabled, since it would allow reading from or
     * writing to files not specified on the command line). */
2034
    if (!ISSET(RESTRICTED) && alt_speller == NULL) {
2035
2036
2037
2038
2039
2040
	char *spellenv = getenv("SPELL");
	if (spellenv != NULL)
	    alt_speller = mallocstrcpy(NULL, spellenv);
    }
#endif

2041
2042
2043
2044
2045
2046
#ifndef NANO_TINY
    /* If matchbrackets wasn't specified, set its default value. */
    if (matchbrackets == NULL)
	matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");
#endif

2047
#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2048
    /* If whitespace wasn't specified, set its default value. */
2049
    if (whitespace == NULL) {
2050
	whitespace = mallocstrcpy(NULL, "  ");
2051
2052
2053
	whitespace_len[0] = 1;
	whitespace_len[1] = 1;
    }
2054
2055
#endif

2056
    /* If tabsize wasn't specified, set its default value. */
Chris Allegretta's avatar
Chris Allegretta committed
2057
    if (tabsize == -1)
2058
	tabsize = WIDTH_OF_TAB;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2059

2060
    /* Back up the old terminal settings so that they can be restored. */
2061
    tcgetattr(0, &oldterm);
2062

2063
    /* Initialize curses mode. */
Chris Allegretta's avatar
Chris Allegretta committed
2064
    initscr();
2065
2066

    /* Set up the terminal state. */
2067
    terminal_init();
2068

2069
2070
2071
    /* Turn the cursor on for sure. */
    curs_set(1);

2072
2073
2074
#ifdef DEBUG
    fprintf(stderr, "Main: set up windows\n");
#endif
2075

2076
2077
2078
    /* Initialize all the windows based on the current screen
     * dimensions. */
    window_init();
2079
2080

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

2083
2084
    /* Set up the shortcut lists. */
    shortcut_init(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2085

2086
#ifndef DISABLE_MOUSE
2087
    mouse_init();
2088
#endif
2089

Chris Allegretta's avatar
Chris Allegretta committed
2090
#ifdef DEBUG
2091
    fprintf(stderr, "Main: open file\n");
Chris Allegretta's avatar
Chris Allegretta committed
2092
#endif
2093

2094
2095
2096
    /* 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. */
2097
    if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
2098
	parse_line_column(&argv[optind][1], &startline, &startcol);
2099
2100
2101
	optind++;
    }

Chris Allegretta's avatar
Chris Allegretta committed
2102
#ifdef ENABLE_MULTIBUFFER
2103
2104
2105
2106
2107
2108
    old_multibuffer = ISSET(MULTIBUFFER);
    SET(MULTIBUFFER);

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

2112
	for (; i < argc; i++) {
2113
2114
2115
2116
	    /* If there's a +LINE or +LINE,COLUMN flag here, it is
	     * followed by at least one other argument, the filename it
	     * applies to. */
	    if (i < argc - 1 && argv[i][0] == '+' && iline == 1 &&
2117
		icol == 1)
2118
		parse_line_column(&argv[i][1], &iline, &icol);
2119
	    else {
2120
		open_buffer(argv[i]);
2121

2122
		if (iline > 1 || icol > 1) {
2123
2124
		    do_gotolinecolumn(iline, icol, FALSE, FALSE, FALSE,
			FALSE);
2125
2126
		    iline = 1;
		    icol = 1;
2127
2128
2129
		}
	    }
	}
2130
2131
2132
2133
2134
2135
2136
    }
#endif

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

    /* We didn't open any files if all the command line arguments were
     * invalid files like directories or if there were no command line
     * arguments given.  In this case, we have to load a blank buffer.
     * Also, we unset view mode to allow editing. */
2143
2144
    if (openfile == NULL) {
	open_buffer("");
2145
	UNSET(VIEW_MODE);
Chris Allegretta's avatar
Chris Allegretta committed
2146
    }
2147
2148
2149
2150

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

2153
2154
2155
2156
#ifdef DEBUG
    fprintf(stderr, "Main: top and bottom win\n");
#endif

2157
    if (startline > 1 || startcol > 1)
2158
2159
	do_gotolinecolumn(startline, startcol, FALSE, FALSE, FALSE,
		FALSE);
2160

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2161
2162
    display_main_list();

2163
    display_buffer();    
Robert Siemborski's avatar
Robert Siemborski committed
2164

2165
    while (TRUE) {
2166
	bool meta_key, func_key, s_or_t, ran_func, finished;
2167
2168

	/* Make sure the cursor is in the edit window. */
2169
	reset_cursor();
2170

2171
#ifndef NANO_TINY
2172
2173
2174
2175
2176
2177
2178
2179
	if (!jump_buf_main) {
	    /* If we haven't already, we're going to set jump_buf so
	     * that we return here after a SIGWINCH.  Indicate this. */
	    jump_buf_main = TRUE;

	    /* Return here after a SIGWINCH. */
	    sigsetjmp(jump_buf, 1);
	}
2180
2181
#endif

2182
2183
2184
2185
	/* If constant cursor position display is on, and there are no
	 * keys waiting in the input buffer, display the current cursor
	 * position on the statusbar. */
	if (ISSET(CONST_UPDATE) && get_key_buffer_len() == 0)
2186
	    do_cursorpos(TRUE);
2187

2188
2189
	currshortcut = main_list;

2190
	/* Read in and interpret characters. */
2191
2192
	do_input(&meta_key, &func_key, &s_or_t, &ran_func, &finished,
		TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
2193
    }
2194

2195
    /* We should never get here. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2196
    assert(FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
2197
}