nano.c 77.9 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-2002 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
28
#include <setjmp.h>
Chris Allegretta's avatar
Chris Allegretta committed
29
30
31
32
33
34
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
Chris Allegretta's avatar
Chris Allegretta committed
35
36
#include <sys/types.h>
#include <sys/wait.h>
Chris Allegretta's avatar
Chris Allegretta committed
37
38
39
40
#include <errno.h>
#include <ctype.h>
#include <locale.h>
#include <limits.h>
41
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

#include "proto.h"
#include "nano.h"

#ifndef NANO_SMALL
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#ifdef HAVE_TERMIO_H
#include <termio.h>
#endif

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

65
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
66
/* Former globals, now static */
67
int fill = 0;/* Fill - where to wrap lines, basically */
68
int wrap_at = 0;/* Right justified fill value, allows resize */
69
#endif
70

Chris Allegretta's avatar
Chris Allegretta committed
71
struct termios oldterm;		/* The user's original term settings */
72
static struct sigaction act;	/* For all our fun signal handlers */
Chris Allegretta's avatar
Chris Allegretta committed
73

74
#ifndef DISABLE_HELP
75
static char *help_text_init = "";	/* Initial message, not including shortcuts */
76
77
#endif

Chris Allegretta's avatar
Chris Allegretta committed
78
79
80
81
char *last_search = NULL;	/* Last string we searched for */
char *last_replace = NULL;	/* Last replacement string */
int search_last_line;		/* Is this the last search line? */

82
83
static sigjmp_buf jmpbuf;	/* Used to return to mainloop after SIGWINCH */

Chris Allegretta's avatar
Chris Allegretta committed
84
85
86
/* What we do when we're all set to exit */
RETSIGTYPE finish(int sigage)
{
87
88
89
90

    keypad(edit, TRUE);
    keypad(bottomwin, TRUE);

Chris Allegretta's avatar
Chris Allegretta committed
91
92
93
    if (!ISSET(NO_HELP)) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
94
    } else
Chris Allegretta's avatar
Chris Allegretta committed
95
	mvwaddstr(bottomwin, 0, 0, hblank);
96

Chris Allegretta's avatar
Chris Allegretta committed
97
98
99
100
    wrefresh(bottomwin);
    endwin();

    /* Restore the old term settings */
Chris Allegretta's avatar
Chris Allegretta committed
101
    tcsetattr(0, TCSANOW, &oldterm);
Chris Allegretta's avatar
Chris Allegretta committed
102

103
104
    thanks_for_all_the_fish();

Chris Allegretta's avatar
Chris Allegretta committed
105
106
107
108
109
110
111
112
113
114
115
116
    exit(sigage);
}

/* Die (gracefully?) */
void die(char *msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);

117
118
119
120
121
122
123
124
125
126
    /* Restore the old term settings */
    tcsetattr(0, TCSANOW, &oldterm);

    clear();
    refresh();
    resetty();
    endwin();

    fprintf(stderr, msg);

127
128
129
    /* save the currently loaded file if it's been modified */
    if (ISSET(MODIFIED))
	die_save_file(filename);
130

131
#ifdef ENABLE_MULTIBUFFER
132
    /* then save all of the other modified loaded files, if any */
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    if (open_files) {
        filestruct *tmp;

	tmp = open_files;

	while (open_files->prev)
	    open_files = open_files->prev;

	while (open_files->next) {

	    /* if we already saved the file above (i. e. if it was the
	       currently loaded file), don't save it again */
	    if (tmp != open_files) {
		fileage = open_files->file;
147
148
149
		/* save the file if it's been modified */
		if (open_files->file_modified)
		    die_save_file(open_files->data);
150
151
152
153
154
155
156
157
158
159
160
161
162
	    }

	    open_files = open_files->next;
	}

    }
#endif

    exit(1);			/* We have a problem: exit w/ errorlevel(1) */
}

void die_save_file(char *die_filename)
{
163
164
    char *name, *ret;
    int i = -1;
165

Chris Allegretta's avatar
Chris Allegretta committed
166
    /* if we can't save we have REAL bad problems,
167
     * but we might as well TRY. */
168
    if (die_filename[0] == '\0') {
169
	name = "nano.save";
170
171
172
173
174
175
	ret = get_next_filename(name);
	if (strcmp(ret, ""))
	    i = write_file(ret, 1, 0, 0);
	name = ret;
    }
    else {
176
177
	char *buf = charalloc(strlen(die_filename) + 6);
	strcpy(buf, die_filename);
178
	strcat(buf, ".save");
179
180
181
182
	ret = get_next_filename(buf);
	if (strcmp(ret, ""))
	    i = write_file(ret, 1, 0, 0);
	name = ret;
183
    }
Chris Allegretta's avatar
Chris Allegretta committed
184

185
186
187
    if (i != -1)
	fprintf(stderr, _("\nBuffer written to %s\n"), name);
    else
188
189
190
	fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), name);

    free(ret);
Chris Allegretta's avatar
Chris Allegretta committed
191
192
}

193
194
195
196
197
198
199
200
201
202
/* Die with an error message that the screen was too small if, well, the
   screen is too small */
void die_too_small(void)
{
    char *too_small_msg = _("Window size is too small for Nano...");

    die(too_small_msg);

}

Chris Allegretta's avatar
Chris Allegretta committed
203
204
205
206
207
void print_view_warning(void)
{
    statusbar(_("Key illegal in VIEW mode"));
}

Chris Allegretta's avatar
Chris Allegretta committed
208
209
210
211
void clear_filename(void)
{
    if (filename != NULL)
	free(filename);
212
    filename = charalloc(1);
Chris Allegretta's avatar
Chris Allegretta committed
213
214
    filename[0] = 0;
}
Chris Allegretta's avatar
Chris Allegretta committed
215

216
217
218
/* Initialize global variables - no better way for now.  If
   save_cutbuffer is nonzero, don't set cutbuffer to NULL. */
void global_init(int save_cutbuffer)
Chris Allegretta's avatar
Chris Allegretta committed
219
220
221
{
    current_x = 0;
    current_y = 0;
222
223
224
225

    if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
	die_too_small();

Chris Allegretta's avatar
Chris Allegretta committed
226
    fileage = NULL;
227
228
    if (!save_cutbuffer)
	cutbuffer = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
229
230
231
232
    current = NULL;
    edittop = NULL;
    editbot = NULL;
    totlines = 0;
233
    totsize = 0;
Chris Allegretta's avatar
Chris Allegretta committed
234
    placewewant = 0;
235

236
237
238
239
#ifndef DISABLE_WRAPJUSTIFY
    if (wrap_at)
	fill = COLS + wrap_at;
    else if (!fill)
240
241
242
243
	fill = COLS - CHARS_FROM_EOL;

    if (fill < MIN_FILL_LENGTH)
	die_too_small();
244
#endif
245

246
    hblank = charalloc(COLS + 1);
247
248
    memset(hblank, ' ', COLS);
    hblank[COLS] = 0;
249

Chris Allegretta's avatar
Chris Allegretta committed
250
251
}

252
#ifndef DISABLE_HELP
Chris Allegretta's avatar
Chris Allegretta committed
253
254
255
256
void init_help_msg(void)
{

    help_text_init =
Chris Allegretta's avatar
Chris Allegretta committed
257
258
259
260
261
262
263
264
265
266
267
	_(" nano help text\n\n "
	  "The nano editor is designed to emulate the functionality and "
	  "ease-of-use of the UW Pico text editor.  There are four main "
	  "sections of the editor: The top line shows the program "
	  "version, the current filename being edited, and whether "
	  "or not the file has been modified.  Next is the main editor "
	  "window showing the file being edited.  The status line is "
	  "the third line from the bottom and shows important messages. "
	  "The bottom two lines show the most commonly used shortcuts "
	  "in the editor.\n\n "
	  "The notation for shortcuts is as follows: Control-key "
268
	  "sequences are notated with a caret (^) symbol and are entered "
269
270
271
272
	  "with the Control (Ctrl) key.  Escape-key sequences are notated "
	  "with the Meta (M) symbol and can be entered using either the "
	  "Esc, Alt or Meta key depending on your keyboard setup.  The "
	  "following keystrokes are available in the main editor window. "
Chris Allegretta's avatar
Chris Allegretta committed
273
	  "Optional keys are shown in parentheses:\n\n");
Chris Allegretta's avatar
Chris Allegretta committed
274
}
275
#endif
Chris Allegretta's avatar
Chris Allegretta committed
276

277
278
/* Make a copy of a node to a pointer (space will be malloc()ed).  This
   does NOT copy the data members used only by open_files. */
Chris Allegretta's avatar
Chris Allegretta committed
279
280
281
282
283
filestruct *copy_node(filestruct * src)
{
    filestruct *dst;

    dst = nmalloc(sizeof(filestruct));
284
    dst->data = charalloc(strlen(src->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

    dst->next = src->next;
    dst->prev = src->prev;

    strcpy(dst->data, src->data);
    dst->lineno = src->lineno;

    return dst;
}

/* Unlink a node from the rest of the struct */
void unlink_node(filestruct * fileptr)
{
    if (fileptr->prev != NULL)
	fileptr->prev->next = fileptr->next;

    if (fileptr->next != NULL)
	fileptr->next->prev = fileptr->prev;
}

305
306
/* Delete a node from the struct.  This does NOT delete the data members
   used only by open_files. */
Chris Allegretta's avatar
Chris Allegretta committed
307
308
void delete_node(filestruct * fileptr)
{
309
310
311
    if (fileptr == NULL)
	return;

Chris Allegretta's avatar
Chris Allegretta committed
312
    if (fileptr->data != NULL)
313
	free(fileptr->data);
Chris Allegretta's avatar
Chris Allegretta committed
314
315
316
    free(fileptr);
}

317
318
/* Okay, now let's duplicate a whole struct!  This does NOT duplicate the
   data members used only by open_files. */
Chris Allegretta's avatar
Chris Allegretta committed
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
filestruct *copy_filestruct(filestruct * src)
{
    filestruct *dst, *tmp, *head, *prev;

    head = copy_node(src);
    dst = head;			/* Else we barf on copying just one line */
    head->prev = NULL;
    tmp = src->next;
    prev = head;

    while (tmp != NULL) {
	dst = copy_node(tmp);
	dst->prev = prev;
	prev->next = dst;

	prev = dst;
	tmp = tmp->next;
    }

    dst->next = NULL;
    return head;
}

342
343
/* Frees a struct.  This does NOT free the data members used only by
   open_files. */
Chris Allegretta's avatar
Chris Allegretta committed
344
345
346
347
348
349
350
351
352
int free_filestruct(filestruct * src)
{
    filestruct *fileptr = src;

    if (src == NULL)
	return 0;

    while (fileptr->next != NULL) {
	fileptr = fileptr->next;
353
	delete_node(fileptr->prev);
Chris Allegretta's avatar
Chris Allegretta committed
354
355

#ifdef DEBUG
356
	fprintf(stderr, _("delete_node(): free'd a node, YAY!\n"));
Chris Allegretta's avatar
Chris Allegretta committed
357
358
#endif
    }
359
    delete_node(fileptr);
Chris Allegretta's avatar
Chris Allegretta committed
360
#ifdef DEBUG
361
    fprintf(stderr, _("delete_node(): free'd last node.\n"));
Chris Allegretta's avatar
Chris Allegretta committed
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
#endif

    return 1;
}

int renumber_all(void)
{
    filestruct *temp;
    long i = 1;

    for (temp = fileage; temp != NULL; temp = temp->next) {
	temp->lineno = i++;
    }

    return 0;
}

int renumber(filestruct * fileptr)
{
    filestruct *temp;

    if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage) {
	renumber_all();
	return 0;
    }
    for (temp = fileptr; temp != NULL; temp = temp->next) {
Chris Allegretta's avatar
Chris Allegretta committed
388
389
390
391
	if (temp->prev != NULL)
	    temp->lineno = temp->prev->lineno + 1;
	else
	    temp->lineno = 1;
Chris Allegretta's avatar
Chris Allegretta committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    }

    return 0;
}

/* Fix the memory allocation for a string */
void align(char **strp)
{
    /* There was a serious bug here:  the new address was never
       stored anywhere... */

    *strp = nrealloc(*strp, strlen(*strp) + 1);
}

Chris Allegretta's avatar
Chris Allegretta committed
406
/* Null a string at a certain index and align it */
407
void null_at(char **data, int index)
Chris Allegretta's avatar
Chris Allegretta committed
408
{
409
410
411
412

    /* Ahh!  Damn dereferencing */
    (*data)[index] = 0;
    align(data);
Chris Allegretta's avatar
Chris Allegretta committed
413
414
}

Chris Allegretta's avatar
Chris Allegretta committed
415
416
417
418
419
void usage(void)
{
#ifdef HAVE_GETOPT_LONG
    printf(_("Usage: nano [GNU long option] [option] +LINE <file>\n\n"));
    printf(_("Option		Long option		Meaning\n"));
420

421
422
423
424
425
#ifndef NANO_SMALL
    printf
	(_
	 (" -D 		--dos			Write file in DOS format\n"));
#endif
426
#ifdef ENABLE_MULTIBUFFER
427
428
    printf
	(_
429
	 (" -F 		--multibuffer		Enable multiple file buffers\n"));
430
#endif
431
432
    printf(_
	 (" -K 		--keypad		Use alternate keypad routines\n"));
Chris Allegretta's avatar
Chris Allegretta committed
433
434
435
436
#ifndef NANO_SMALL
    printf
	(_
	 (" -M 		--mac			Write file in Mac format\n"));
437
438
439
    printf
	(_
	 (" -N 		--noconvert		Don't convert files from DOS/Mac format\n"));
440
441
442
443
#endif
#ifndef NANO_SMALL
    printf(_
	   (" -S		--smooth		Smooth scrolling\n"));
444
#endif
445
446
    printf(_
	   (" -T [num]	--tabsize=[num]		Set width of a tab to num\n"));
Chris Allegretta's avatar
Chris Allegretta committed
447
448
449
450
451
452
453
    printf
	(_
	 (" -V 		--version		Print version information and exit\n"));
    printf(_
	   (" -c 		--const			Constantly show cursor position\n"));
    printf(_
	   (" -h 		--help			Show this message\n"));
454
#ifndef NANO_SMALL
455
456
    printf(_
	   (" -i 		--autoindent		Automatically indent new lines\n"));
457
458
    printf(_
	   (" -k 		--cut			Let ^K cut from cursor to end of line\n"));
459
#endif
Chris Allegretta's avatar
Chris Allegretta committed
460
    printf(_
461
	   (" -l 		--nofollow		Don't follow symbolic links, overwrite\n"));
462
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
463
464
465
466
#ifdef NCURSES_MOUSE_VERSION
    printf(_(" -m 		--mouse			Enable mouse\n"));
#endif
#endif
467
#ifndef DISABLE_OPERATINGDIR
468
    printf(_
469
	   (" -o [dir] 	--operatingdir=[dir]	Set operating directory\n"));
470
471
472
#endif
    printf(_
	   (" -p 		--pico			Emulate Pico as closely as possible\n"));
473
474

#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
475
476
477
    printf
	(_
	 (" -r [#cols] 	--fill=[#cols]		Set fill cols to (wrap lines at) #cols\n"));
478
#endif
479
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
480
481
    printf(_
	   (" -s [prog] 	--speller=[prog]	Enable alternate speller\n"));
482
#endif
Chris Allegretta's avatar
Chris Allegretta committed
483
484
485
486
    printf(_
	   (" -t 		--tempfile		Auto save on exit, don't prompt\n"));
    printf(_
	   (" -v 		--view			View (read only) mode\n"));
487
#ifndef DISABLE_WRAPPING
Chris Allegretta's avatar
Chris Allegretta committed
488
489
    printf(_
	   (" -w 		--nowrap		Don't wrap long lines\n"));
490
#endif
Chris Allegretta's avatar
Chris Allegretta committed
491
492
493
494
495
496
497
498
499
    printf(_
	   (" -x 		--nohelp		Don't show help window\n"));
    printf(_
	   (" -z 		--suspend		Enable suspend\n"));
    printf(_
	   (" +LINE					Start at line number LINE\n"));
#else
    printf(_("Usage: nano [option] +LINE <file>\n\n"));
    printf(_("Option		Meaning\n"));
500
501
502
#ifndef NANO_SMALL
    printf(_(" -D 		Write file in DOS format\n"));
#endif
503
504
#ifdef ENABLE_MULTIBUFFER
    printf(_(" -F 		Enable multiple file buffers\n"));
Chris Allegretta's avatar
Chris Allegretta committed
505
#endif
Jordi Mallach's avatar
Jordi Mallach committed
506
    printf(_(" -K		Use alternate keypad routines\n"));
Chris Allegretta's avatar
Chris Allegretta committed
507
508
#ifndef NANO_SMALL
    printf(_(" -M 		Write file in Mac format\n"));
509
#endif
510
    printf(_(" -R		Use regular expressions for search\n"));
511
512
513
#ifndef NANO_SMALL
    printf(_(" -S		Smooth scrolling\n"));
#endif
514
    printf(_(" -T [num]	Set width of a tab to num\n"));
Chris Allegretta's avatar
Chris Allegretta committed
515
516
517
    printf(_(" -V 		Print version information and exit\n"));
    printf(_(" -c 		Constantly show cursor position\n"));
    printf(_(" -h 		Show this message\n"));
518
#ifndef NANO_SMALL
519
    printf(_(" -i 		Automatically indent new lines\n"));
520
    printf(_(" -k 		Let ^K cut from cursor to end of line\n"));
521
#endif
Chris Allegretta's avatar
Chris Allegretta committed
522
    printf(_
523
	   (" -l 		Don't follow symbolic links, overwrite\n"));
524
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
525
526
527
#ifdef NCURSES_MOUSE_VERSION
    printf(_(" -m 		Enable mouse\n"));
#endif
528
529
530
#endif
#ifndef DISABLE_OPERATINGDIR
    printf(_(" -o [dir] 	Set operating directory\n"));
Chris Allegretta's avatar
Chris Allegretta committed
531
#endif
532
    printf(_(" -p 		Emulate Pico as closely as possible\n"));
533
534

#ifndef DISABLE_WRAPJUSTIFY
535
536
    printf(_
	   (" -r [#cols] 	Set fill cols to (wrap lines at) #cols\n"));
537
#endif
538
#ifndef DISABLE_SPELLER
539
    printf(_(" -s [prog]  	Enable alternate speller\n"));
540
#endif
Chris Allegretta's avatar
Chris Allegretta committed
541
542
    printf(_(" -t 		Auto save on exit, don't prompt\n"));
    printf(_(" -v 		View (read only) mode\n"));
543
#ifndef DISABLE_WRAPPING
Chris Allegretta's avatar
Chris Allegretta committed
544
    printf(_(" -w 		Don't wrap long lines\n"));
545
#endif
Chris Allegretta's avatar
Chris Allegretta committed
546
547
548
549
550
551
552
553
554
    printf(_(" -x 		Don't show help window\n"));
    printf(_(" -z 		Enable suspend\n"));
    printf(_(" +LINE		Start at line number LINE\n"));
#endif
    exit(0);
}

void version(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
555
    printf(_(" GNU nano version %s (compiled %s, %s)\n"),
Chris Allegretta's avatar
Chris Allegretta committed
556
	   VERSION, __TIME__, __DATE__);
557
    printf(_
558
559
	   (" Email: nano@nano-editor.org	Web: http://www.nano-editor.org"));
    printf(_("\n Compiled options:"));
560

561
562
#ifdef NANO_EXTRA
    printf(" --enable-extra");
563
#endif
564
565
#ifdef ENABLE_MULTIBUFFER
    printf(" --enable-multibuffer");
566
#endif
567
568
569
#ifdef ENABLE_NANORC
    printf(" --enable-nanorc");
#endif
570
571
572
#ifdef ENABLE_COLOR
    printf(" --enable-color");
#endif
573
574
575
576

#ifdef NANO_SMALL
    printf(" --enable-tiny");
#else
577
#ifdef DISABLE_BROWSER
578
    printf(" --disable-browser");
579
580
#endif
#ifdef DISABLE_TABCOMP
581
    printf(" --disable-tabcomp");
582
583
#endif
#ifdef DISABLE_JUSTIFY
584
    printf(" --disable-justify");
585
586
#endif
#ifdef DISABLE_SPELLER
587
    printf(" --disable-speller");
588
589
#endif
#ifdef DISABLE_HELP
590
    printf(" --disable-help");
591
#endif
592
593
#ifdef DISABLE_MOUSE
    printf(" --disable-mouse");
594
#endif
595
596
597
#ifdef DISABLE_OPERATINGDIR
    printf(" --disable-operatingdir");
#endif
598
#endif /* NANO_SMALL */
599

600
601
602
#ifdef DISABLE_WRAPPING
    printf(" --disable-wrapping");
#endif
603
604
605
606
607
#ifdef USE_SLANG
    printf(" --with-slang");
#endif
    printf("\n");

Chris Allegretta's avatar
Chris Allegretta committed
608
609
}

610
611
/* Create a new node.  This does NOT initialize the data members used
   only by open_files. */
Chris Allegretta's avatar
Chris Allegretta committed
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
filestruct *make_new_node(filestruct * prevnode)
{
    filestruct *newnode;

    newnode = nmalloc(sizeof(filestruct));
    newnode->data = NULL;

    newnode->prev = prevnode;
    newnode->next = NULL;

    if (prevnode != NULL)
	newnode->lineno = prevnode->lineno + 1;

    return newnode;
}

628
629
/* Splice a node into an existing filestruct.  This does NOT set the data
   members used only by open_files. */
630
631
void splice_node(filestruct * begin, filestruct * newnode,
		 filestruct * end)
632
{
633
634
635
    newnode->next = end;
    newnode->prev = begin;
    begin->next = newnode;
636
    if (end != NULL)
637
	end->prev = newnode;
638
639
}

640
int do_mark(void)
Chris Allegretta's avatar
Chris Allegretta committed
641
642
{
#ifdef NANO_SMALL
643
    nano_disabled_msg();
Chris Allegretta's avatar
Chris Allegretta committed
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
#else
    if (!ISSET(MARK_ISSET)) {
	statusbar(_("Mark Set"));
	SET(MARK_ISSET);
	mark_beginbuf = current;
	mark_beginx = current_x;
    } else {
	statusbar(_("Mark UNset"));
	UNSET(MARK_ISSET);
	mark_beginbuf = NULL;
	mark_beginx = 0;

	edit_refresh();
    }
#endif
    return 1;
}

int no_help(void)
{
    if ISSET
	(NO_HELP)
	    return 2;
    else
	return 0;
}

671
#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP)
672
673
674
675
void nano_disabled_msg(void)
{
    statusbar("Sorry, support for this function has been disabled");
}
676
#endif
677

Chris Allegretta's avatar
Chris Allegretta committed
678
679
680
/* The user typed a printable character; add it to the edit buffer */
void do_char(char ch)
{
Robert Siemborski's avatar
Robert Siemborski committed
681
682
    /* magic-line: when a character is inserted on the current magic line,
     * it means we need a new one! */
683
    if (filebot == current && current->data[0] == '\0') {
Robert Siemborski's avatar
Robert Siemborski committed
684
	new_magicline();
685
	fix_editbot();
Robert Siemborski's avatar
Robert Siemborski committed
686
687
    }

Chris Allegretta's avatar
Chris Allegretta committed
688
689
690
691
692
693
694
695
    /* More dangerousness fun =) */
    current->data = nrealloc(current->data, strlen(current->data) + 2);
    memmove(&current->data[current_x + 1],
	    &current->data[current_x],
	    strlen(current->data) - current_x + 1);
    current->data[current_x] = ch;
    do_right();

696
697
698
699
#ifdef ENABLE_COLOR
    edit_refresh();
#endif

700
#ifndef DISABLE_WRAPPING
701
    if (!ISSET(NO_WRAP) && (ch != '\t'))
Chris Allegretta's avatar
Chris Allegretta committed
702
	check_wrap(current, ch);
703
704
#endif

Chris Allegretta's avatar
Chris Allegretta committed
705
706
707
708
709
710
711
712
713
714
    set_modified();
    check_statblank();
    UNSET(KEEP_CUTBUFFER);
    totsize++;

}

/* Someone hits return *gasp!* */
int do_enter(filestruct * inptr)
{
715
    filestruct *newnode;
716
717
718
    char *tmp;
#ifndef NANO_SMALL
    char *spc;
Chris Allegretta's avatar
Chris Allegretta committed
719
    int extra = 0;
720
#endif
Chris Allegretta's avatar
Chris Allegretta committed
721

722
    newnode = make_new_node(inptr);
Chris Allegretta's avatar
Chris Allegretta committed
723
724
725
    tmp = &current->data[current_x];
    current_x = 0;

726
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
727
728
729
730
731
732
733
734
    /* Do auto-indenting, like the neolithic Turbo Pascal editor */
    if (ISSET(AUTOINDENT)) {
	spc = current->data;
	if (spc) {
	    while ((*spc == ' ') || (*spc == '\t')) {
		extra++;
		spc++;
		current_x++;
735
		totsize++;
Chris Allegretta's avatar
Chris Allegretta committed
736
	    }
737
	    newnode->data = charalloc(strlen(tmp) + extra + 1);
738
739
	    strncpy(newnode->data, current->data, extra);
	    strcpy(&newnode->data[extra], tmp);
Chris Allegretta's avatar
Chris Allegretta committed
740
	}
741
742
743
    } else 
#endif
    {
744
	newnode->data = charalloc(strlen(tmp) + 1);
745
	strcpy(newnode->data, tmp);
Chris Allegretta's avatar
Chris Allegretta committed
746
747
748
    }
    *tmp = 0;

749
    if (inptr->next == NULL) {
750
751
	filebot = newnode;
	editbot = newnode;
Chris Allegretta's avatar
Chris Allegretta committed
752
    }
753
    splice_node(inptr, newnode, inptr->next);
Chris Allegretta's avatar
Chris Allegretta committed
754
755
756

    totsize++;
    renumber(current);
757
    current = newnode;
Chris Allegretta's avatar
Chris Allegretta committed
758
759
    align(&current->data);

Robert Siemborski's avatar
Robert Siemborski committed
760
761
    /* The logic here is as follows:
     *    -> If we are at the bottom of the buffer, we want to recenter
762
     *       (read: rebuild) the screen and forcibly move the cursor.
Robert Siemborski's avatar
Robert Siemborski committed
763
764
765
     *    -> otherwise, we want simply to redraw the screen and update
     *       where we think the cursor is.
     */
Chris Allegretta's avatar
Chris Allegretta committed
766
    if (current_y == editwinrows - 1) {
767
	edit_update(current, CENTER);
768
	reset_cursor();
Robert Siemborski's avatar
Robert Siemborski committed
769
    } else {
Chris Allegretta's avatar
Chris Allegretta committed
770
	current_y++;
Robert Siemborski's avatar
Robert Siemborski committed
771
772
773
	edit_refresh();
	update_cursor();
    }
Chris Allegretta's avatar
Chris Allegretta committed
774
775
776
777

    totlines++;
    set_modified();

778
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
779
780
781
782
783
784
785
786
    return 1;
}

int do_enter_void(void)
{
    return do_enter(current);
}

787
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
788
789
void do_next_word(void)
{
790
    filestruct *fileptr, *old;
Chris Allegretta's avatar
Chris Allegretta committed
791
792
793
794
795
    int i;

    if (current == NULL)
	return;

796
    old = current;
Chris Allegretta's avatar
Chris Allegretta committed
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
    i = current_x;
    for (fileptr = current; fileptr != NULL; fileptr = fileptr->next) {
	if (fileptr == current) {
	    while (isalnum((int) fileptr->data[i])
		   && fileptr->data[i] != 0)
		i++;

	    if (fileptr->data[i] == 0) {
		i = 0;
		continue;
	    }
	}
	while (!isalnum((int) fileptr->data[i]) && fileptr->data[i] != 0)
	    i++;

	if (fileptr->data[i] != 0)
	    break;

	i = 0;
    }
    if (fileptr == NULL)
	current = filebot;
    else
	current = fileptr;

    current_x = i;
    placewewant = xplustabs();
824

Chris Allegretta's avatar
Chris Allegretta committed
825
    if (current->lineno >= editbot->lineno)
826
	edit_update(current, CENTER);
827
828
829
830
831
832
833
834
835
836
    else {
	/* If we've jumped lines, refresh the old line.  We can't just use
	 * current->prev here, because we may have skipped over some blank
	 * lines, in which case the previous line is the wrong one.
	 */
	if (current != old)
	    update_line(old, 0);

	update_line(current, current_x);
    }
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
}

/* the same thing for backwards */
void do_prev_word(void)
{
    filestruct *fileptr, *old;
    int i;

    if (current == NULL)
	return;

    old = current;
    i = current_x;
    for (fileptr = current; fileptr != NULL; fileptr = fileptr->prev) {
	if (fileptr == current) {
	    while (isalnum((int) fileptr->data[i])
		   && i != 0)
		i--;

	    if (i == 0) {
		if (fileptr->prev != NULL)
858
		    i = strlen(fileptr->prev->data);
859
860
861
862
		else if (fileptr == fileage && filebot != NULL) {
		    current_x = 0;
		    return;
		}
863
864
865
866
867
868
869
870
871
872
873
874
875
		continue;
	    }
	}

	while (!isalnum((int) fileptr->data[i]) && i != 0)
	    i--;

	if (i > 0) {
	    i--;

	    while (isalnum((int) fileptr->data[i]) && i != 0)
		i--;

Chris Allegretta's avatar
Chris Allegretta committed
876
877
878
879
	    if (!isalnum((int) fileptr->data[i]))
		i++;

	    if (i != 0 || i != current_x)
880
881
882
883
		break;

	}
	if (fileptr->prev != NULL)
884
	    i = strlen(fileptr->prev->data);
885
886
887
888
	else if (fileptr == fileage && filebot != NULL) {
	    current_x = 0;
	    return;
	}
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
    }
    if (fileptr == NULL)
	current = fileage;
    else
	current = fileptr;

    current_x = i;
    placewewant = xplustabs();

    if (current->lineno <= edittop->lineno)
	edit_update(current, CENTER);
    else {
	/* If we've jumped lines, refresh the old line.  We can't just use
	 * current->prev here, because we may have skipped over some blank
	 * lines, in which case the previous line is the wrong one.
	 */
	if (current != old)
	    update_line(old, 0);

	update_line(current, current_x);
    }
Chris Allegretta's avatar
Chris Allegretta committed
910
911

}
912
#endif /* NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
913

914
#ifndef DISABLE_WRAPPING
Chris Allegretta's avatar
Chris Allegretta committed
915
void do_wrap(filestruct * inptr, char input_char)
Chris Allegretta's avatar
Chris Allegretta committed
916
{
Chris Allegretta's avatar
Chris Allegretta committed
917
918
919
920
921
922
923
    int i = 0;			/* Index into ->data for line. */
    int i_tabs = 0;		/* Screen position of ->data[i]. */
    int last_word_end = -1;	/* Location of end of last word found. */
    int current_word_start = -1;	/* Location of start of current word. */
    int current_word_start_t = -1;	/* Location of start of current word screen position. */
    int current_word_end = -1;	/* Location of end   of current word */
    int current_word_end_t = -1;	/* Location of end   of current word screen position. */
924
    int len = strlen(inptr->data);
Chris Allegretta's avatar
Chris Allegretta committed
925

Chris Allegretta's avatar
Chris Allegretta committed
926
    int down = 0;
927
928
    int right = 0;
    struct filestruct *temp = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
929

Chris Allegretta's avatar
Chris Allegretta committed
930
    assert(strlenpt(inptr->data) > fill);
Chris Allegretta's avatar
Chris Allegretta committed
931

932
    for (i = 0, i_tabs = 0; i < len; i++, i_tabs++) {
933
	if (!isspace((int) inptr->data[i])) {
Chris Allegretta's avatar
Chris Allegretta committed
934
	    last_word_end = current_word_end;
Chris Allegretta's avatar
Chris Allegretta committed
935

Chris Allegretta's avatar
Chris Allegretta committed
936
	    current_word_start = i;
937
	    current_word_start_t = i_tabs;
Chris Allegretta's avatar
Chris Allegretta committed
938

939
	    while (!isspace((int) inptr->data[i])
940
		   && inptr->data[i]) {
941
942
		i++;
		i_tabs++;
Chris Allegretta's avatar
Chris Allegretta committed
943
944
		if (inptr->data[i] < 32)
		    i_tabs++;
945
	    }
Chris Allegretta's avatar
Chris Allegretta committed
946

947
	    if (inptr->data[i]) {
Chris Allegretta's avatar
Chris Allegretta committed
948
		current_word_end = i;
949
		current_word_end_t = i_tabs;
Chris Allegretta's avatar
Chris Allegretta committed
950
951
	    } else {
		current_word_end = i - 1;
952
953
954
		current_word_end_t = i_tabs - 1;
	    }
	}
Chris Allegretta's avatar
Chris Allegretta committed
955

Chris Allegretta's avatar
Chris Allegretta committed
956
	if (inptr->data[i] == NANO_CONTROL_I) {
957
958
	    if (i_tabs % tabsize != 0);
	    i_tabs += tabsize - (i_tabs % tabsize);
Chris Allegretta's avatar
Chris Allegretta committed
959
	}
Chris Allegretta's avatar
Chris Allegretta committed
960

961
	if (current_word_end_t > fill)
962
963
	    break;
    }
Chris Allegretta's avatar
Chris Allegretta committed
964

965
    /* There are a few (ever changing) cases of what the line could look like.
966
     * 1) only one word on the line before wrap point.
967
968
969
     *    a) one word takes up the whole line with no starting spaces.
     *         - do nothing and return.
     *    b) cursor is on word or before word at wrap point and there are spaces at beginning.
970
971
972
973
974
975
     *         - word starts new line.
     *         - keep white space on original line up to the cursor.
     *    *) cursor is after word at wrap point
     *         - either it's all white space after word, and this routine isn't called.
     *         - or we are actually in case 2 (2 words).
     * 2) Two or more words on the line before wrap point.
976
     *    a) cursor is at a word or space before wrap point
977
     *         - word at wrap point starts a new line.
978
979
     *         - white space at end of original line is cleared, unless
     *           it is all spaces between previous word and next word which appears after fill.
980
981
     *    b) cursor is at the word at the wrap point.
     *         - word at wrap point starts a new line.
982
     *         - white space on original line is kept to where cursor was.
983
984
     *    c) cursor is past the word at the wrap point.
     *         - word at wrap point starts a new line.
985
     *         - white space at end of original line is cleared
986
987
     */

Chris Allegretta's avatar
Chris Allegretta committed
988
    temp = nmalloc(sizeof(filestruct));
989

990
    /* Category 1a: one word taking up the whole line with no beginning spaces. */
991
    if ((last_word_end == -1) && (!isspace((int) inptr->data[0]))) {
992
	for (i = current_word_end; i < len; i++) {
993
	    if (!isspace((int) inptr->data[i]) && i < len) {
Chris Allegretta's avatar
Chris Allegretta committed
994
		current_word_start = i;
995
		while (!isspace((int) inptr->data[i]) && (i < len)) {
996
997
		    i++;
		}
Chris Allegretta's avatar
Chris Allegretta committed
998
		last_word_end = current_word_end;
999
1000
1001
1002
1003
1004
		current_word_end = i;
		break;
	    }
	}

	if (last_word_end == -1) {
Chris Allegretta's avatar
Chris Allegretta committed
1005
	    free(temp);
1006
1007
1008
1009
1010
	    return;
	}
	if (current_x >= last_word_end) {
	    right = (current_x - current_word_start) + 1;
	    current_x = last_word_end;
Chris Allegretta's avatar
Chris Allegretta committed
1011
	    down = 1;
1012
	}
1013

1014
1015
1016
1017
	/* Subtract length of original line, plus one for the newline, from
	   totsize. */
	totsize -= (strlen(inptr->data) + 1);

1018
	temp->data = charalloc(strlen(&inptr->data[current_word_start]) + 1);
1019
1020
1021
	strcpy(temp->data, &inptr->data[current_word_start]);
	inptr->data = nrealloc(inptr->data, last_word_end + 2);
	inptr->data[last_word_end + 1] = 0;
1022
1023
1024
1025

	/* Now add lengths of new lines, plus two for the newlines, to totsize. */
	totsize += (strlen(inptr->data) + strlen(temp->data) + 2);

Chris Allegretta's avatar
Chris Allegretta committed
1026
1027
1028
    } else
	/* Category 1b: one word on the line and word not taking up whole line
	   (i.e. there are spaces at the beginning of the line) */
1029
    if (last_word_end == -1) {
1030
	temp->data = charalloc(strlen(&inptr->data[current_word_start]) + 1);
1031
1032
1033
1034
1035
	strcpy(temp->data, &inptr->data[current_word_start]);

	/* Inside word, remove it from original, and move cursor to right spot. */
	if (current_x >= current_word_start) {
	    right = current_x - current_word_start;
1036

1037
	    current_x = 0;
1038
#ifndef NANO_SMALL
1039
1040
	    if (ISSET(AUTOINDENT)) {
		int i = 0;
1041
1042
		while ((inptr->next->data[i] == ' '
			|| inptr->next->data[i] == '\t')) {
1043
		    i++;
1044
		}
1045
	    }
1046
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1047
	    down = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1048
1049
	}

1050
1051
1052
1053
	/* Subtract length of original line, plus one for the newline, from
	   totsize. */
	totsize -= (strlen(inptr->data) + 1);

1054
	null_at(&inptr->data, current_x);
1055

1056
1057
1058
	/* Now add lengths of new lines, plus two for the newlines, to totsize. */
	totsize += (strlen(inptr->data) + strlen(temp->data) + 2);

1059
1060
1061
1062
	if (ISSET(MARK_ISSET) && (mark_beginbuf == inptr)) {
	    mark_beginbuf = temp;
	    mark_beginx = 0;
	}
1063
1064
1065
1066
1067
    }

    /* Category 2: two or more words on the line. */
    else {
	/* Case 2a: cursor before word at wrap point. */
Chris Allegretta's avatar
Chris Allegretta committed
1068
1069
	if (current_x < current_word_start) {
	    temp->data =
1070
		charalloc(strlen(&inptr->data[current_word_start]) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1071
1072
	    strcpy(temp->data, &inptr->data[current_word_start]);

1073
	    if (!isspace((int) input_char)) {
Chris Allegretta's avatar
Chris Allegretta committed
1074
		i = current_word_start - 1;
1075

1076
		while (isspace((int) inptr->data[i])) {
Chris Allegretta's avatar
Chris Allegretta committed
1077
1078
1079
1080
1081
1082
1083
1084
		    i--;
		    assert(i >= 0);
		}
	    } else if (current_x <= last_word_end)
		i = last_word_end - 1;
	    else
		i = current_x;

1085
1086
1087
1088
	    /* Subtract length of original line, plus one for the newline, from
	       totsize. */
	    totsize -= (strlen(inptr->data) + 1);

Chris Allegretta's avatar
Chris Allegretta committed
1089
1090
	    inptr->data = nrealloc(inptr->data, i + 2);
	    inptr->data[i + 1] = 0;
1091
1092
1093
1094

	    /* Now add lengths of new lines, plus two for the newlines, to totsize. */
	    totsize += (strlen(inptr->data) + strlen(temp->data) + 2);

Chris Allegretta's avatar
Chris Allegretta committed
1095
	}
1096
1097
1098


	/* Case 2b: cursor at word at wrap point. */
Chris Allegretta's avatar
Chris Allegretta committed
1099
1100
1101
	else if ((current_x >= current_word_start)
		 && (current_x <= (current_word_end + 1))) {
	    temp->data =
1102
		charalloc(strlen(&inptr->data[current_word_start]) + 1);
1103
1104
1105
1106
1107
	    strcpy(temp->data, &inptr->data[current_word_start]);

	    down = 1;

	    right = current_x - current_word_start;
1108
#ifndef NANO_SMALL
1109
1110
	    if (ISSET(AUTOINDENT)) {
		int i = 0;
1111
1112
		while ((inptr->next->data[i] == ' '
			|| inptr->next->data[i] == '\t')) {
1113
		    i++;
1114
		}
1115
	    }
1116
#endif
1117
	    i = current_word_start - 1;
1118
	    current_x = current_word_start;
1119

1120
1121
1122
1123
	    /* Subtract length of original line, plus one for the newline, from
	       totsize. */
	    totsize -= (strlen(inptr->data) + 1);

1124
	    null_at(&inptr->data, current_word_start);
1125

1126
1127
	    /* Now add lengths of new lines, plus two for the newlines, to totsize. */
	    totsize += (strlen(inptr->data) + strlen(temp->data) + 2);
Chris Allegretta's avatar
Chris Allegretta committed
1128
	}
1129
1130
1131


	/* Case 2c: cursor past word at wrap point. */
Chris Allegretta's avatar
Chris Allegretta committed
1132
1133
	else {
	    temp->data =
1134
		charalloc(strlen(&inptr->data[current_word_start]) + 1);
1135
1136
1137
1138
1139
1140
1141
1142
	    strcpy(temp->data, &inptr->data[current_word_start]);

	    down = 1;
	    right = current_x - current_word_start;

	    current_x = current_word_start;
	    i = current_word_start - 1;

1143
	    while (isspace((int) inptr->data[i])) {
1144
		i--;
Chris Allegretta's avatar
Chris Allegretta committed
1145
		assert(i >= 0);
1146
	    }
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156

	    /* Subtract length of original line, plus one for the newline, from
	       totsize. */
	    totsize -= (strlen(inptr->data) + 1);

	    inptr->data = nrealloc(inptr->data, i + 2);
	    inptr->data[i + 1] = 0;

	    /* Now add lengths of new lines, plus two for the newlines, to totsize. */
	    totsize += (strlen(inptr->data) + strlen(temp->data) + 2);
Chris Allegretta's avatar
Chris Allegretta committed
1157
	}
Chris Allegretta's avatar
Chris Allegretta committed
1158
1159
    }

Chris Allegretta's avatar
Chris Allegretta committed
1160
    /* We pre-pend wrapped part to next line. */
1161
    if (ISSET(SAMELINEWRAP) && inptr->next) {
1162
	int old_x = current_x, old_y = current_y;
1163

1164
	/* Plus one for the space which concatenates the two lines together plus 1 for \0. */
1165
	char *p =
1166
	    charalloc((strlen(temp->data) + strlen(inptr->next->data) + 2));
1167

1168
1169
1170
1171
1172
	/* We're adding to an existing line instead of creating a new
	   one; decrement totlines here so that when it gets incremented
	   below, it won't end up being high by one. */
	totlines--;

1173
#ifndef NANO_SMALL
1174
1175
1176
	if (ISSET(AUTOINDENT)) {
	    int non = 0;

1177
1178
1179
1180
	    /* Grab the beginning of the next line until it's not a 
	       space or tab, then null terminate it so we can strcat it
	       to hell */
	    while ((inptr->next->data[non] == ' '
1181
1182
1183
1184
		    || inptr->next->data[non] == '\t')) {
		p[non] = inptr->next->data[non];
		non++;
	    }
1185
1186
1187
1188
	    p[non] = 0;
	    strcat(p, temp->data);
	    strcat(p, " ");

1189
1190
	    /* Now tack on the rest of the next line after the spaces and
	       tabs */
1191
	    strcat(p, &inptr->next->data[non]);
1192
1193
1194
	} else 
#endif
	{
1195
1196
1197
1198
	    strcpy(p, temp->data);
	    strcat(p, " ");
	    strcat(p, inptr->next->data);
	}
1199

Chris Allegretta's avatar
Chris Allegretta committed
1200
	free(inptr->next->data);
1201
1202
	inptr->next->data = p;

Chris Allegretta's avatar
Chris Allegretta committed
1203
1204
	free(temp->data);
	free(temp);
1205
1206
1207

	current_x = old_x;
	current_y = old_y;
1208
    }
Chris Allegretta's avatar
Chris Allegretta committed
1209
    /* Else we start a new line. */
1210
    else {
1211

1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
	temp->prev = inptr;
	temp->next = inptr->next;

	if (inptr->next)
	    inptr->next->prev = temp;
	inptr->next = temp;

	if (!temp->next)
	    filebot = temp;

	SET(SAMELINEWRAP);
1223

1224
#ifndef NANO_SMALL
1225
1226
1227
1228
1229
	if (ISSET(AUTOINDENT)) {
	    char *spc = inptr->data;
	    char *t = NULL;
	    int extra = 0;
	    if (spc) {
1230
		while ((*spc == ' ') || (*spc == '\t')) {
1231
1232
1233
		    extra++;
		    spc++;
		    totsize++;
1234
		    right++;
1235
		}
1236
		t = charalloc(strlen(temp->data) + extra + 1);
1237
1238
1239
1240
1241
1242
		strncpy(t, inptr->data, extra);
		strcpy(t + extra, temp->data);
		free(temp->data);
		temp->data = t;
	    }
	}
1243
#endif
1244
1245
1246
1247
    }


    totlines++;
1248
    /* Everything about it makes me want this line here, but it causes
1249
1250
     * totsize to be high by one for some reason.  Sigh. (Rob) */
    /* totsize++; */
1251

Chris Allegretta's avatar
Chris Allegretta committed
1252
    renumber(inptr);
1253
    edit_update(edittop, TOP);
1254

1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265

    /* Move the cursor to the new line if appropriate. */
    if (down) {
	do_right();
    }

    /* Move the cursor to the correct spot in the line if appropriate. */
    while (right--) {
	do_right();
    }

1266
    edit_update(edittop, TOP);
1267
1268
    reset_cursor();
    edit_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
1269
1270
1271
}

/* Check to see if we've just caused the line to wrap to a new line */
1272
void check_wrap(filestruct * inptr, char ch)
Chris Allegretta's avatar
Chris Allegretta committed
1273
{
Chris Allegretta's avatar
Chris Allegretta committed
1274
    int len = strlenpt(inptr->data);
Chris Allegretta's avatar
Chris Allegretta committed
1275
1276
1277
1278
1279
#ifdef DEBUG
    fprintf(stderr, _("check_wrap called with inptr->data=\"%s\"\n"),
	    inptr->data);
#endif

1280
    if (len <= fill)
Chris Allegretta's avatar
Chris Allegretta committed
1281
	return;
1282
    else {
Chris Allegretta's avatar
Chris Allegretta committed
1283
	int i = actual_x(inptr, fill);
1284
1285

	/* Do not wrap if there are no words on or after wrap point. */
1286
	int char_found = 0;
1287

1288
	while (isspace((int) inptr->data[i]) && inptr->data[i])
1289
	    i++;
Chris Allegretta's avatar
Chris Allegretta committed
1290

1291
1292
	if (!inptr->data[i])
	    return;
1293

1294
1295
	/* String must be at least 1 character long. */
	for (i = strlen(inptr->data) - 1; i >= 0; i--) {
1296
	    if (isspace((int) inptr->data[i])) {
1297
1298
		if (!char_found)
		    continue;
Chris Allegretta's avatar
Chris Allegretta committed
1299
		char_found = 2;	/* 2 for yes do wrap. */
1300
		break;
Chris Allegretta's avatar
Chris Allegretta committed
1301
1302
	    } else
		char_found = 1;	/* 1 for yes found a word, but must check further. */
1303
	}
1304
1305
1306

	if (char_found == 2)
	    do_wrap(inptr, ch);
1307
    }
Chris Allegretta's avatar
Chris Allegretta committed
1308
}
1309
#endif				/* DISABLE_WRAPPING */
Chris Allegretta's avatar
Chris Allegretta committed
1310
1311

/* Stuff we do when we abort from programs and want to clean up the
1312
 * screen.  This doesn't do much right now.
Chris Allegretta's avatar
Chris Allegretta committed
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
 */
void do_early_abort(void)
{
    blank_statusbar_refresh();
}

int do_backspace(void)
{
    filestruct *previous, *tmp;

    if (current_x != 0) {
	/* Let's get dangerous */
	memmove(&current->data[current_x - 1], &current->data[current_x],
		strlen(current->data) - current_x + 1);
#ifdef DEBUG
	fprintf(stderr, _("current->data now = \"%s\"\n"), current->data);
#endif
	align(&current->data);
	do_left();
    } else {
	if (current == fileage)
	    return 0;		/* Can't delete past top of file */

	previous = current->prev;
	current_x = strlen(previous->data);
	previous->data = nrealloc(previous->data,
				  strlen(previous->data) +
				  strlen(current->data) + 1);
	strcat(previous->data, current->data);

	tmp = current;
	unlink_node(current);
	delete_node(current);
	if (current == edittop) {
	    if (previous->next)
		current = previous->next;
	    else
		current = previous;
1351
	    page_up();
Chris Allegretta's avatar
Chris Allegretta committed
1352
1353
1354
1355
1356
1357
1358
1359
1360
	} else {
	    if (previous->next)
		current = previous->next;
	    else
		current = previous;
	    update_line(current, current_x);
	}

	/* Ooops, sanity check */
Chris Allegretta's avatar
Chris Allegretta committed
1361
	if (tmp == filebot) {
Chris Allegretta's avatar
Chris Allegretta committed
1362
1363
	    filebot = current;
	    editbot = current;
1364
1365

	    /* Recreate the magic line if we're deleting it AND if the
1366
1367
1368
	       line we're on now is NOT blank.  if it is blank we
	       can just use IT for the magic line.   This is how Pico
	       appears to do it, in any case */
1369
1370
1371
1372
	    if (strcmp(current->data, "")) {
		new_magicline();
		fix_editbot();
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
	}

	current = previous;
	renumber(current);
	previous_line();
	totlines--;
#ifdef DEBUG
	fprintf(stderr, _("After, data = \"%s\"\n"), current->data);
#endif

    }

    totsize--;
    set_modified();
    UNSET(KEEP_CUTBUFFER);
    edit_refresh();
    return 1;
}

int do_delete(void)
{
    filestruct *foo;

1396
1397
1398
1399
1400
1401
    /* blbf -> blank line before filebot (see below) */
    int blbf = 0;

    if (current->next == filebot && !strcmp(current->data, ""))
	blbf = 1;

Chris Allegretta's avatar
Chris Allegretta committed
1402
1403
1404
1405
1406
1407
1408
    if (current_x != strlen(current->data)) {
	/* Let's get dangerous */
	memmove(&current->data[current_x], &current->data[current_x + 1],
		strlen(current->data) - current_x);

	align(&current->data);

1409
1410
1411
1412
1413
	/* Now that we have a magic line again, we can check for both being
	   on the line before filebot as well as at filebot; it's a special
	   case if we're on the line before filebot and it's blank, since we
	   should be able to delete it */
    } else if (current->next != NULL && (current->next != filebot || blbf)) {
Chris Allegretta's avatar
Chris Allegretta committed
1414
1415
1416
1417
1418
1419
	current->data = nrealloc(current->data,
				 strlen(current->data) +
				 strlen(current->next->data) + 1);
	strcat(current->data, current->next->data);

	foo = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
1420
	if (filebot == foo) {
Chris Allegretta's avatar
Chris Allegretta committed
1421
1422
1423
1424
1425
1426
1427
1428
	    filebot = current;
	    editbot = current;
	}

	unlink_node(foo);
	delete_node(foo);
	update_line(current, current_x);

1429
	/* Please see the comment in do_backspace if you don't understand
1430
	   this test */
1431
	if (current == filebot && strcmp(current->data, "")) {
1432
1433
	    new_magicline();
	    fix_editbot();
1434
	    totsize++;
1435
	}
Chris Allegretta's avatar
Chris Allegretta committed
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
	renumber(current);
	totlines--;
    } else
	return 0;

    totsize--;
    set_modified();
    UNSET(KEEP_CUTBUFFER);
    edit_refresh();
    return 1;
}

void wrap_reset(void)
{
    UNSET(SAMELINEWRAP);
}

1453
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
1454
1455

int do_int_spell_fix(char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1456
{
Chris Allegretta's avatar
Chris Allegretta committed
1457
    char *prevanswer = NULL, *save_search = NULL, *save_replace = NULL;
1458
    filestruct *begin;
1459
    int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
Chris Allegretta's avatar
Chris Allegretta committed
1460

Chris Allegretta's avatar
Chris Allegretta committed
1461
1462
1463
1464
    /* save where we are */
    begin = current;
    beginx = current_x + 1;

1465
1466
1467
1468
    /* Make sure Spell Check goes forward only */
    reverse_search_set = ISSET(REVERSE_SEARCH);
    UNSET(REVERSE_SEARCH);

Chris Allegretta's avatar
Chris Allegretta committed
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
    /* save the current search/replace strings */
    search_init_globals();
    save_search = mallocstrcpy(save_search, last_search);
    save_replace = mallocstrcpy(save_replace, last_replace);

    /* set search/replace strings to mis-spelt word */
    prevanswer = mallocstrcpy(prevanswer, word);
    last_search = mallocstrcpy(last_search, word);
    last_replace = mallocstrcpy(last_replace, word);

    /* start from the top of file */
1480
1481
    current = fileage;
    current_x = beginx_top = -1;
Chris Allegretta's avatar
Chris Allegretta committed
1482
1483
1484

    search_last_line = FALSE;

1485
1486
    edit_update(fileage, TOP);

1487
    while (1) {
1488

1489
1490
	/* make sure word is still mis-spelt (i.e. when multi-errors) */
	if (findnextstr(TRUE, FALSE, fileage, beginx_top, prevanswer) != NULL) {
1491

1492
1493
1494
	    /* find wholewords only */
	    if (!is_whole_word(current_x, current, prevanswer))
		continue;
1495

1496
1497
1498
	    do_replace_highlight(TRUE, prevanswer);

	    /* allow replace word to be corrected */
1499
	    i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
1500
1501

	    do_replace_highlight(FALSE, prevanswer);
Chris Allegretta's avatar
Chris Allegretta committed
1502

1503
1504
1505
	    /* start from the start of this line again */
	    current = fileage;
	    current_x = beginx_top;
Chris Allegretta's avatar
Chris Allegretta committed
1506

1507
1508
1509
1510
1511
1512
	    search_last_line = FALSE;

	    if (strcmp(prevanswer,answer) != 0) {
		j = i;
		do_replace_loop(prevanswer, fileage, &beginx_top, TRUE, &j);
	    }
1513
	}
1514
1515

	break;
Rocco Corsi's avatar
Rocco Corsi committed
1516
    }
Chris Allegretta's avatar
Chris Allegretta committed
1517

Chris Allegretta's avatar
Chris Allegretta committed
1518
1519
1520
    /* restore the search/replace strings */
    last_search = mallocstrcpy(last_search, save_search);
    last_replace = mallocstrcpy(last_replace, save_replace);
Chris Allegretta's avatar
Chris Allegretta committed
1521

Chris Allegretta's avatar
Chris Allegretta committed
1522
1523
1524
1525
    /* restore where we were */
    current = begin;
    current_x = beginx - 1;

1526
1527
1528
1529
    /* restore Search/Replace direction */
    if (reverse_search_set)
	SET(REVERSE_SEARCH);

Chris Allegretta's avatar
Chris Allegretta committed
1530
1531
1532
1533
1534
1535
1536
1537
1538
    edit_update(current, CENTER);

    if (i == -1)
	return FALSE;

    return TRUE;
}

/* Integrated spell checking using 'spell' program */
Chris Allegretta's avatar
Chris Allegretta committed
1539
int do_int_speller(char *tempfile_name)
Chris Allegretta's avatar
Chris Allegretta committed
1540
{
Chris Allegretta's avatar
Chris Allegretta committed
1541
1542
1543
    char *read_buff, *read_buff_ptr, *read_buff_word;
    long pipe_buff_size;
    int in_fd[2], tempfile_fd;
Chris Allegretta's avatar
Chris Allegretta committed
1544
1545
1546
1547
    int spell_status;
    pid_t pid_spell;
    ssize_t bytesread;

Chris Allegretta's avatar
Chris Allegretta committed
1548
1549
    /* Create a pipe to spell program */

Chris Allegretta's avatar
Chris Allegretta committed
1550
1551
1552
    if (pipe(in_fd) == -1)
	return FALSE;

Chris Allegretta's avatar
Chris Allegretta committed
1553
1554
    /* A new process to run spell in */

1555
    if ((pid_spell = fork()) == 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1556
1557

	/* Child continues, (i.e. future spell process) */
Chris Allegretta's avatar
Chris Allegretta committed
1558
1559
1560

	close(in_fd[0]);

Chris Allegretta's avatar
Chris Allegretta committed
1561
	/* replace the standard in with the tempfile */
Chris Allegretta's avatar
Chris Allegretta committed
1562

1563
	if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
Chris Allegretta's avatar
Chris Allegretta committed
1564

Chris Allegretta's avatar
Chris Allegretta committed
1565
1566
1567
	    close(in_fd[1]);
	    exit(1);
	}
Chris Allegretta's avatar
Chris Allegretta committed
1568

Chris Allegretta's avatar
Chris Allegretta committed
1569
	if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
Chris Allegretta's avatar
Chris Allegretta committed
1570

Chris Allegretta's avatar
Chris Allegretta committed
1571
1572
1573
	    close(tempfile_fd);
	    close(in_fd[1]);
	    exit(1);
Chris Allegretta's avatar
Chris Allegretta committed
1574
	}
Chris Allegretta's avatar
Chris Allegretta committed
1575
	close(tempfile_fd);
Chris Allegretta's avatar
Chris Allegretta committed
1576

1577

Chris Allegretta's avatar
Chris Allegretta committed
1578
1579
1580
1581
1582
1583
	/* send spell's standard out to the pipe */

	if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {

	    close(in_fd[1]);
	    exit(1);
Chris Allegretta's avatar
Chris Allegretta committed
1584
	}
Chris Allegretta's avatar
Chris Allegretta committed
1585
	close(in_fd[1]);
Chris Allegretta's avatar
Chris Allegretta committed
1586

Chris Allegretta's avatar
Chris Allegretta committed
1587
	/* Start spell program, we are using the PATH here!?!? */
Chris Allegretta's avatar
Chris Allegretta committed
1588
1589
	execlp("spell", "spell", NULL);

Chris Allegretta's avatar
Chris Allegretta committed
1590
	/* Should not be reached, if spell is found!!! */
Chris Allegretta's avatar
Chris Allegretta committed
1591

Chris Allegretta's avatar
Chris Allegretta committed
1592
	exit(1);
Chris Allegretta's avatar
Chris Allegretta committed
1593
    }
Chris Allegretta's avatar
Chris Allegretta committed
1594
1595
1596

    /* Parent continues here */

Chris Allegretta's avatar
Chris Allegretta committed
1597
    close(in_fd[1]);
Chris Allegretta's avatar
Chris Allegretta committed
1598

Chris Allegretta's avatar
Chris Allegretta committed
1599
    /* Child process was not forked successfully */
Chris Allegretta's avatar
Chris Allegretta committed
1600

Chris Allegretta's avatar
Chris Allegretta committed
1601
    if (pid_spell < 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1602

Chris Allegretta's avatar
Chris Allegretta committed
1603
	close(in_fd[0]);
Chris Allegretta's avatar
Chris Allegretta committed
1604
1605
1606
	return FALSE;
    }

Chris Allegretta's avatar
Chris Allegretta committed
1607
    /* Get system pipe buffer size */
Chris Allegretta's avatar
Chris Allegretta committed
1608

1609
    if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
Chris Allegretta's avatar
Chris Allegretta committed
1610

Chris Allegretta's avatar
Chris Allegretta committed
1611
1612
	close(in_fd[0]);
	return FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1613
1614
    }

1615
    read_buff = charalloc(pipe_buff_size + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1616

Chris Allegretta's avatar
Chris Allegretta committed
1617
    /* Process the returned spelling errors */
Chris Allegretta's avatar
Chris Allegretta committed
1618

1619
    while ((bytesread = read(in_fd[0], read_buff, pipe_buff_size)) > 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1620

Chris Allegretta's avatar
Chris Allegretta committed
1621
1622
	read_buff[bytesread] = (char) NULL;
	read_buff_word = read_buff_ptr = read_buff;
Chris Allegretta's avatar
Chris Allegretta committed
1623

Chris Allegretta's avatar
Chris Allegretta committed
1624
1625
1626
1627
1628
	while (*read_buff_ptr != (char) NULL) {

	    /* Windows version may need to process additional char '\r' */

	    /* Possible problem here if last word not followed by '\n' */
Chris Allegretta's avatar
Chris Allegretta committed
1629
1630

	    if (*read_buff_ptr == '\n') {
Chris Allegretta's avatar
Chris Allegretta committed
1631
		*read_buff_ptr = (char) NULL;
1632
		if (!do_int_spell_fix(read_buff_word)) {
Chris Allegretta's avatar
Chris Allegretta committed
1633
1634
1635
1636
1637
1638

		    close(in_fd[0]);
		    free(read_buff);
		    replace_abort();

		    return TRUE;
1639
		}
Chris Allegretta's avatar
Chris Allegretta committed
1640
1641
		read_buff_word = read_buff_ptr;
		read_buff_word++;
Chris Allegretta's avatar
Chris Allegretta committed
1642
1643
1644
1645
1646
	    }

	    read_buff_ptr++;
	}
    }
Chris Allegretta's avatar
Chris Allegretta committed
1647
1648
1649

    close(in_fd[0]);
    free(read_buff);
Chris Allegretta's avatar
Chris Allegretta committed
1650
1651
    replace_abort();

Chris Allegretta's avatar
Chris Allegretta committed
1652
1653
1654
1655
1656
1657
    /* Process end of spell process */

    wait(&spell_status);
    if (WIFEXITED(spell_status)) {
	if (WEXITSTATUS(spell_status) != 0)
	    return FALSE;
1658
    } else
Chris Allegretta's avatar
Chris Allegretta committed
1659
1660
	return FALSE;

Chris Allegretta's avatar
Chris Allegretta committed
1661
1662
1663
1664
    return TRUE;
}

/* External spell checking */
Chris Allegretta's avatar
Chris Allegretta committed
1665
int do_alt_speller(char *file_name)
Chris Allegretta's avatar
Chris Allegretta committed
1666
{
1667
1668
1669
    int alt_spell_status, lineno_cur = current->lineno;
    int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;

Chris Allegretta's avatar
Chris Allegretta committed
1670
    pid_t pid_spell;
Chris Allegretta's avatar
Chris Allegretta committed
1671
1672
1673
    char *ptr;
    static int arglen = 3;
    static char **spellargs = (char **) NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1674
1675
1676

    endwin();

1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
    /* Set up an argument list to pass the execvp function */
    if (spellargs == NULL) {
	spellargs = nmalloc(arglen * sizeof(char *));

	spellargs[0] = strtok(alt_speller, " ");
	while ((ptr = strtok(NULL, " ")) != NULL) {
	    arglen++;
	    spellargs = nrealloc(spellargs, arglen * sizeof(char *));
	    spellargs[arglen - 3] = ptr;
	}
	spellargs[arglen - 1] = NULL;
    }
    spellargs[arglen - 2] = file_name;
Chris Allegretta's avatar
Chris Allegretta committed
1690

1691
    /* Start a new process for the alternate speller */
1692
    if ((pid_spell = fork()) == 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1693

1694
	/* Start alternate spell program; we are using the PATH here!?!? */
Chris Allegretta's avatar
Chris Allegretta committed
1695
	execvp(spellargs[0], spellargs);
Chris Allegretta's avatar
Chris Allegretta committed
1696
1697
1698
1699
1700
1701
1702
1703
1704

	/* Should not be reached, if alternate speller is found!!! */

	exit(1);
    }

    /* Could not fork?? */

    if (pid_spell < 0)
Chris Allegretta's avatar
Chris Allegretta committed
1705
1706
	return FALSE;

Chris Allegretta's avatar
Chris Allegretta committed
1707
1708
1709
1710
1711
1712
    /* Wait for alternate speller to complete */

    wait(&alt_spell_status);
    if (WIFEXITED(alt_spell_status)) {
	if (WEXITSTATUS(alt_spell_status) != 0)
	    return FALSE;
1713
    } else
Chris Allegretta's avatar
Chris Allegretta committed
1714
	return FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1715

Chris Allegretta's avatar
Chris Allegretta committed
1716
    refresh();
Chris Allegretta's avatar
Chris Allegretta committed
1717
    free_filestruct(fileage);
1718
    global_init(1);
Chris Allegretta's avatar
Chris Allegretta committed
1719
    open_file(file_name, 0, 1);
1720

1721
1722
1723
    /* go back to the old position, mark the file as modified, and make
       sure that the titlebar is refreshed */
    do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
Chris Allegretta's avatar
Chris Allegretta committed
1724
    set_modified();
1725
1726
    clearok(topwin, FALSE);
    titlebar(NULL);
1727

Chris Allegretta's avatar
Chris Allegretta committed
1728
1729
1730
1731
1732
1733
1734
    return TRUE;
}
#endif

int do_spell(void)
{

1735
#ifdef DISABLE_SPELLER
1736
1737
    nano_disabled_msg();
    return (TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
1738
#else
Chris Allegretta's avatar
Chris Allegretta committed
1739
1740
    char *temp;
    int spell_res;
Chris Allegretta's avatar
Chris Allegretta committed
1741

1742
    if ((temp = safe_tempnam(0, "nano.")) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
1743
	statusbar(_("Could not create a temporary filename: %s"),
1744
		  strerror(errno));
Chris Allegretta's avatar
Chris Allegretta committed
1745
1746
	return 0;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1747

1748
    if (write_file(temp, 1, 0, 0) == -1) {
1749
	statusbar(_("Spell checking failed: unable to write temp file!"));
Chris Allegretta's avatar
Chris Allegretta committed
1750
	return 0;
1751
    }
Chris Allegretta's avatar
Chris Allegretta committed
1752

1753
1754
1755
#ifdef ENABLE_MULTIBUFFER
    /* update the current open_files entry before spell-checking, in case
       any problems occur; the case of there being no open_files entries
1756
1757
       is handled elsewhere (before we reach this point) */
    add_open_file(1);
1758
1759
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1760
1761
1762
1763
    if (alt_speller)
	spell_res = do_alt_speller(temp);
    else
	spell_res = do_int_speller(temp);
Chris Allegretta's avatar
Chris Allegretta committed
1764

Chris Allegretta's avatar
Chris Allegretta committed
1765
    remove(temp);
Chris Allegretta's avatar
Chris Allegretta committed
1766
1767
1768
1769
1770
1771
1772
1773

    if (spell_res)
	statusbar(_("Finished checking spelling"));
    else
	statusbar(_("Spell checking failed"));

    return spell_res;

1774
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1775
}
Chris Allegretta's avatar
Chris Allegretta committed
1776
1777
1778
1779
1780

int do_exit(void)
{
    int i;

1781
1782
    if (!ISSET(MODIFIED)) {

1783
#ifdef ENABLE_MULTIBUFFER
1784
1785
1786
1787
1788
1789
1790
1791
1792
	if (!close_open_file()) {
	    display_main_list();
	    return 1;
	}
	else
#endif

	    finish(0);
    }
Chris Allegretta's avatar
Chris Allegretta committed
1793

1794
    if (ISSET(TEMP_OPT)) {
Chris Allegretta's avatar
Chris Allegretta committed
1795
1796
	i = 1;
    } else {
1797
	i = do_yesno(0, 0,
Chris Allegretta's avatar
Chris Allegretta committed
1798
1799
1800
1801
1802
1803
1804
1805
1806
		     _
		     ("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
    }

#ifdef DEBUG
    dump_buffer(fileage);
#endif

    if (i == 1) {
1807
1808
	if (do_writeout(filename, 1, 0) > 0) {

1809
#ifdef ENABLE_MULTIBUFFER
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
	    if (!close_open_file()) {
		display_main_list();
		return 1;
	    }
	    else
#endif

		finish(0);
	}
    } else if (i == 0) {

1821
#ifdef ENABLE_MULTIBUFFER
1822
1823
1824
1825
1826
1827
1828
	if (!close_open_file()) {
	    display_main_list();
	    return 1;
	}
	else
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1829
	    finish(0);
1830
    } else
Chris Allegretta's avatar
Chris Allegretta committed
1831
1832
1833
1834
1835
1836
	statusbar(_("Cancelled"));

    display_main_list();
    return 1;
}

1837
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
1838
1839
1840
1841
#ifdef NCURSES_MOUSE_VERSION
void do_mouse(void)
{
    MEVENT mevent;
Chris Allegretta's avatar
Chris Allegretta committed
1842
    int foo = 0, tab_found = 0;
1843
    int currslen;
Chris Allegretta's avatar
Chris Allegretta committed
1844
1845
1846
1847

    if (getmouse(&mevent) == ERR)
	return;

1848
1849
    /* If mouse not in edit or bottom window, return */
    if (wenclose(edit, mevent.y, mevent.x)) {
Chris Allegretta's avatar
Chris Allegretta committed
1850

1851
1852
1853
	/* Don't let people screw with the marker when they're in a
	   subfunction */
	if (currshortcut != main_list)
Chris Allegretta's avatar
Chris Allegretta committed
1854
	    return;
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887

	/* Subtract out size of topwin.  Perhaps we need a constant somewhere? */
	mevent.y -= 2;

	/* Selecting where the cursor is sets the mark.
	 * Selecting beyond the line length with the cursor at the end of the
	 * line sets the mark as well. 
	 */
	if ((mevent.y == current_y) &&
	    ((mevent.x == current_x) || (current_x == strlen(current->data)
					 && (mevent.x >
					     strlen(current->data))))) {
	    if (ISSET(VIEW_MODE)) {
		print_view_warning();
		return;
	    }
	    do_mark();
	} else if (mevent.y > current_y) {
	    while (mevent.y > current_y) {
		if (current->next != NULL)
		    current = current->next;
		else
		    break;
		current_y++;
	    }
	} else if (mevent.y < current_y) {
	    while (mevent.y < current_y) {
		if (current->prev != NULL)
		    current = current->prev;
		else
		    break;
		current_y--;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1888
	}
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
	current_x = mevent.x;
	placewewant = current_x;
	while (foo < current_x) {
	    if (current->data[foo] == NANO_CONTROL_I) {
		current_x -= tabsize - (foo % tabsize);
		tab_found = 1;
	    } else if (current->data[foo] & 0x80);
	    else if (current->data[foo] < 32)
		current_x--;
	    foo++;
Chris Allegretta's avatar
Chris Allegretta committed
1899
	}
1900
1901
1902
1903
1904
	/* This is where tab_found comes in.  I can't figure out why,
	 * but without it any line with a tab will place the cursor
	 * one character behind.  Whatever, this fixes it. */
	if (tab_found == 1)
	    current_x++;
Chris Allegretta's avatar
Chris Allegretta committed
1905

1906
1907
	if (current_x > strlen(current->data))
	    current_x = strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
1908

1909
1910
1911
	update_cursor();
	edit_refresh();
    } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
1912
1913
1914

	int k, val = 0;

1915
1916
1917
1918
1919
	if (currshortcut == main_list)
	    currslen = MAIN_VISIBLE;
	else
	    currslen = length_of_list(currshortcut);

1920
1921
1922
1923
	if (currslen < 2)
	    k = COLS / 6;
	else 
	    k = COLS / ((currslen + (currslen %2)) / 2);
1924
1925
1926
1927
1928
1929

	/* Determine what shortcut list was clicked */
	mevent.y -= (editwinrows + 3);

	if (mevent.y < 0) /* They clicked on the statusbar */
	    return;
Chris Allegretta's avatar
Chris Allegretta committed
1930

1931
1932
1933
1934
1935
1936
1937
1938
	/* Don't select stuff beyond list length */
	if (mevent.x / k >= currslen)	
	    return;

	val = currshortcut[(mevent.x / k) * 2 + mevent.y].val;

	/* And ungetch that value */
	ungetch(val);
1939
1940
1941
1942
1943

	/* And if it's an alt-key sequence, we should probably send alt
	   too ;-) */
	if (val >= 'a' && val <= 'z')
	   ungetch(27);
1944
    }
Chris Allegretta's avatar
Chris Allegretta committed
1945
1946
1947
1948
1949
1950
1951
}
#endif
#endif

/* Handler for SIGHUP */
RETSIGTYPE handle_hup(int signal)
{
1952
    die(_("Received SIGHUP"));
Chris Allegretta's avatar
Chris Allegretta committed
1953
1954
}

1955
1956
1957
1958
/* What do we do when we catch the suspend signal */
RETSIGTYPE do_suspend(int signal)
{
    endwin();
1959
1960
1961
    printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
    fflush(stdout);

1962
1963
1964
    /* Restore the terminal settings for the disabled keys */
    tcsetattr(0, TCSANOW, &oldterm);

1965
1966
1967
1968
    /* We used to re-enable the default SIG_DFL and raise SIGTSTP, but 
	then we could be (and were) interrupted in the middle of the call.
	So we do it the mutt way instead */
    kill(0, SIGSTOP);
1969
1970
1971
1972
1973
1974
}

/* Restore the suspend handler when we come back into the prog */
RETSIGTYPE do_cont(int signal)
{

1975
1976
1977
1978
    /* Now we just update the screen instead of having to reenable the
	SIGTSTP handler */

    doupdate();
Chris Allegretta's avatar
Chris Allegretta committed
1979
1980
1981
    /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
	start suspending again */
   signal_init();
1982
}
Chris Allegretta's avatar
Chris Allegretta committed
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005

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

    tty = ttyname(0);
    if (!tty)
	return;
    fd = open(tty, O_RDWR);
    if (fd == -1)
	return;
    result = ioctl(fd, TIOCGWINSZ, &win);
    if (result == -1)
	return;


    COLS = win.ws_col;
    LINES = win.ws_row;

2006
2007
2008
    if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
	die_too_small();

2009
#ifndef DISABLE_WRAPJUSTIFY
2010
2011
    if ((fill = COLS - CHARS_FROM_EOL) < MIN_FILL_LENGTH)
	die_too_small();
2012
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2013

2014
2015
2016
    hblank = nrealloc(hblank, COLS + 1);
    memset(hblank, ' ', COLS);
    hblank[COLS] = 0;
Chris Allegretta's avatar
Chris Allegretta committed
2017

2018
#ifdef HAVE_RESIZETERM
Chris Allegretta's avatar
Chris Allegretta committed
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
    resizeterm(LINES, COLS);
#ifdef HAVE_WRESIZE
    if (wresize(topwin, 2, COLS) == ERR)
	die(_("Cannot resize top win"));
    if (mvwin(topwin, 0, 0) == ERR)
	die(_("Cannot move top win"));
    if (wresize(edit, editwinrows, COLS) == ERR)
	die(_("Cannot resize edit win"));
    if (mvwin(edit, 2, 0) == ERR)
	die(_("Cannot move edit win"));
    if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
	die(_("Cannot resize bottom win"));
    if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
	die(_("Cannot move bottom win"));
#endif				/* HAVE_WRESIZE */
2034
#endif				/* HAVE_RESIZETERM */
Chris Allegretta's avatar
Chris Allegretta committed
2035

Robert Siemborski's avatar
Robert Siemborski committed
2036
    fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
2037

Chris Allegretta's avatar
Chris Allegretta committed
2038
    if (current_y > editwinrows - 1) {
2039
	edit_update(editbot, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
2040
2041
    }
    erase();
2042
2043

    /* Do these b/c width may have changed... */
Chris Allegretta's avatar
Chris Allegretta committed
2044
    refresh();
Chris Allegretta's avatar
Chris Allegretta committed
2045
    titlebar(NULL);
2046
2047
    edit_refresh();
    display_main_list();
2048
    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
2049
    total_refresh();
2050

2051
2052
2053
    /* Turn cursor back on for sure */
    curs_set(1);

2054
2055
2056
    /* Jump back to mainloop */
    siglongjmp(jmpbuf, 1);

Chris Allegretta's avatar
Chris Allegretta committed
2057
2058
2059
#endif
}

2060
2061
void signal_init(void)
{
2062
2063
2064
#ifdef _POSIX_VDISABLE
    struct termios term;
#endif
2065

2066
    /* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
2067
2068
2069
2070
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGINT, &act, NULL);

2071
2072
2073
2074
2075
2076
2077
    /* Trap SIGHUP cuz we want to write the file out. */
    act.sa_handler = handle_hup;
    sigaction(SIGHUP, &act, NULL);

    act.sa_handler = handle_sigwinch;
    sigaction(SIGWINCH, &act, NULL);

Chris Allegretta's avatar
Chris Allegretta committed
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087

#ifdef _POSIX_VDISABLE
    tcgetattr(0, &term);

#ifdef VDSUSP
    term.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif /* VDSUSP */

#endif /* _POSIX_VDISABLE */

2088
    if (!ISSET(SUSPEND)) {
2089
2090
2091
2092

/* Insane! */
#ifdef _POSIX_VDISABLE
	term.c_cc[VSUSP] = _POSIX_VDISABLE;
Chris Allegretta's avatar
Chris Allegretta committed
2093
#else
2094
	act.sa_handler = SIG_IGN;
2095
	sigaction(SIGTSTP, &act, NULL);
Chris Allegretta's avatar
Chris Allegretta committed
2096
#endif
2097

2098
    } else {
2099
2100
2101
2102
	/* if we don't do this, it seems other stuff interrupts the
	   suspend handler!  Try using nano with mutt without this line */
	sigfillset(&act.sa_mask);

2103
	act.sa_handler = do_suspend;
2104
2105
	sigaction(SIGTSTP, &act, NULL);

2106
	act.sa_handler = do_cont;
2107
	sigaction(SIGCONT, &act, NULL);
2108
2109
    }

Chris Allegretta's avatar
Chris Allegretta committed
2110
2111
2112
2113
2114
2115

#ifdef _POSIX_VDISABLE
    tcsetattr(0, TCSANOW, &term);
#endif


2116
2117
}

Chris Allegretta's avatar
Chris Allegretta committed
2118
2119
void window_init(void)
{
2120
2121
    if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
	die_too_small();
2122

2123
    /* Set up the main text window */
Chris Allegretta's avatar
Chris Allegretta committed
2124
2125
2126
2127
    edit = newwin(editwinrows, COLS, 2, 0);

    /* And the other windows */
    topwin = newwin(2, COLS, 0, 0);
2128
    bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
2129

Chris Allegretta's avatar
Chris Allegretta committed
2130
2131
2132
2133
2134
2135
2136
#ifdef PDCURSES
    /* Oops, I guess we need this again.
       Moved here so the keypad still works after a Meta-X, for example */
    keypad(edit, TRUE);
    keypad(bottomwin, TRUE);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
2137
2138
}

2139
2140
void mouse_init(void)
{
2141
#ifndef DISABLE_MOUSE
2142
2143
#ifdef NCURSES_MOUSE_VERSION
    if (ISSET(USE_MOUSE)) {
2144
	keypad_on(edit, 1);
2145
	keypad_on(bottomwin, 1);
2146

2147
2148
	mousemask(BUTTON1_RELEASED, NULL);
	mouseinterval(50);
2149

2150
    } else
2151
	mousemask(0, NULL);
2152

2153
2154
2155
2156
2157
#endif
#endif

}

Chris Allegretta's avatar
Chris Allegretta committed
2158
2159
2160
2161
2162
2163
int do_tab(void)
{
    do_char('\t');
    return 1;
}

2164
#ifndef DISABLE_JUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
2165
2166
2167
int empty_line(const char *data)
{
    while (*data) {
2168
	if (!isspace((int) *data))
Chris Allegretta's avatar
Chris Allegretta committed
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
	    return 0;

	data++;
    }

    return 1;
}

int no_spaces(const char *data)
{
    while (*data) {
2180
	if (isspace((int) *data))
Chris Allegretta's avatar
Chris Allegretta committed
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
	    return 0;

	data++;
    }

    return 1;
}

void justify_format(char *data)
{
    int i = 0;
    int len = strlen(data);

    /* Skip first character regardless and leading whitespace. */
    for (i = 1; i < len; i++) {
2196
	if (!isspace((int) data[i]))
Chris Allegretta's avatar
Chris Allegretta committed
2197
2198
2199
2200
2201
2202
2203
	    break;
    }

    i++;			/* (i) is now at least 2. */

    /* No double spaces allowed unless following a period.  Tabs -> space.  No double tabs. */
    for (; i < len; i++) {
2204
	if (isspace((int) data[i]) && isspace((int) data[i - 1])
Chris Allegretta's avatar
Chris Allegretta committed
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
	    && (data[i - 2] != '.')) {
	    memmove(data + i, data + i + 1, len - i);
	    len--;
	    i--;
	}
    }
}
#endif

int do_justify(void)
{
2216
#ifdef DISABLE_JUSTIFY
2217
2218
2219
    nano_disabled_msg();
    return 1;
#else
Chris Allegretta's avatar
Chris Allegretta committed
2220
    int slen = 0;		/* length of combined lines on one line. */
2221
    int initial_y, kbinput = 0, totbak;
2222
    filestruct *initial = NULL, *tmpjust = NULL, *cutbak, *tmptop, *tmpbot;
Chris Allegretta's avatar
Chris Allegretta committed
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239

    if (empty_line(current->data)) {
	/* Justify starting at first non-empty line. */
	do {
	    if (!current->next)
		return 1;

	    current = current->next;
	    current_y++;
	}
	while (empty_line(current->data));
    } else {
	/* Search back for the beginning of the paragraph, where
	 *   Paragraph is  1)  A line with leading whitespace
	 *             or  2)  A line following an empty line.
	 */
	while (current->prev != NULL) {
2240
	    if (isspace((int) current->data[0]) || !current->data[0])
Chris Allegretta's avatar
Chris Allegretta committed
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
		break;

	    current = current->prev;
	    current_y--;
	}

	/* First line with leading whitespace may be empty. */
	if (empty_line(current->data)) {
	    if (current->next) {
		current = current->next;
		current_y++;
	    } else
		return 1;
	}
    }
    initial = current;
    initial_y = current_y;

    set_modified();
2260
    cutbak = cutbuffer;		/* Got to like cutbak ;) */
2261
    totbak = totsize;
2262
2263
2264
2265
    cutbuffer = NULL;

    tmptop = current;
    tmpjust = copy_node(current);
Robert Siemborski's avatar
Robert Siemborski committed
2266
2267

    /* This is annoying because it mucks with totsize */
2268
    add_to_cutbuffer(tmpjust);
Robert Siemborski's avatar
Robert Siemborski committed
2269

Chris Allegretta's avatar
Chris Allegretta committed
2270
    /* Put the whole paragraph into one big line. */
2271
    while (current->next && !isspace((int) current->next->data[0])
Chris Allegretta's avatar
Chris Allegretta committed
2272
2273
2274
2275
2276
	   && current->next->data[0]) {
	filestruct *tmpnode = current->next;
	int len = strlen(current->data);
	int len2 = strlen(current->next->data);

2277
2278
2279
2280
	tmpjust = NULL;
	tmpjust = copy_node(current->next);
	add_to_cutbuffer(tmpjust);

Robert Siemborski's avatar
Robert Siemborski committed
2281
	/* Wiping out a newline */
2282
	totsize--;
Robert Siemborski's avatar
Robert Siemborski committed
2283

Chris Allegretta's avatar
Chris Allegretta committed
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
	/* length of both strings plus space between strings and ending \0. */
	current->data = nrealloc(current->data, len + len2 + 2);
	current->data[len++] = ' ';
	current->data[len] = '\0';

	strncat(current->data, current->next->data, len2);

	unlink_node(tmpnode);
	delete_node(tmpnode);
    }

    justify_format(current->data);

    slen = strlen(current->data);
2298
    totsize += slen;
Chris Allegretta's avatar
Chris Allegretta committed
2299

2300
2301
    if ((strlenpt(current->data) > (fill))
	&& !no_spaces(current->data)) {
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
	do {
	    int i = 0;
	    int len2 = 0;
	    filestruct *tmpline = nmalloc(sizeof(filestruct));

	    /* Start at fill , unless line isn't that long (but it 
	     * appears at least fill long with tabs.
	     */
	    if (slen > fill)
		i = fill;
	    else
		i = slen;
Robert Siemborski's avatar
Robert Siemborski committed
2314

2315
	    for (; i > 0; i--) {
2316
		if (isspace((int) current->data[i]) &&
2317
2318
2319
		    ((strlenpt(current->data) - strlen(current->data + i))
		     <= fill))
		    break;
2320
	    }
Robert Siemborski's avatar
Robert Siemborski committed
2321

2322
	    if (!i)
2323
		break;
Chris Allegretta's avatar
Chris Allegretta committed
2324

2325
	    current->data[i] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
2326

2327
	    len2 = strlen(current->data + i + 1);
2328
	    tmpline->data = charalloc(len2 + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2329

2330
2331
2332
	    /* Skip the white space in current. */
	    memcpy(tmpline->data, current->data + i + 1, len2);
	    tmpline->data[len2] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
2333

2334
	    current->data = nrealloc(current->data, i + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2335

2336
2337
2338
2339
	    tmpline->prev = current;
	    tmpline->next = current->next;
	    if (current->next != NULL)
		current->next->prev = tmpline;
Chris Allegretta's avatar
Chris Allegretta committed
2340

2341
2342
2343
2344
	    current->next = tmpline;
	    current = tmpline;
	    slen -= i + 1;
	    current_y++;
2345
2346
	} while ((strlenpt(current->data) > (fill))
		 && !no_spaces(current->data));
2347
    }
2348
    tmpbot = current;
Chris Allegretta's avatar
Chris Allegretta committed
2349
2350
2351

    if (current->next)
	current = current->next;
2352
2353
    else
	filebot = current;
Chris Allegretta's avatar
Chris Allegretta committed
2354
2355
2356
    current_x = 0;
    placewewant = 0;

2357
2358
2359
2360
2361
    renumber(initial);
    totlines = filebot->lineno;

    werase(edit);

Chris Allegretta's avatar
Chris Allegretta committed
2362
2363
    if ((current_y < 0) || (current_y >= editwinrows - 1)
	|| (initial_y <= 0)) {
2364
	edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
2365
2366
	center_cursor();
    } else {
Robert Siemborski's avatar
Robert Siemborski committed
2367
	fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
2368
2369
    }

2370
    edit_refresh();
2371
    statusbar(_("Can now UnJustify!"));
2372
2373
2374
    /* Change the shortcut list to display the unjustify code */
    shortcut_init(1);
    display_main_list();
2375
2376
    reset_cursor();

2377
    /* Now get a keystroke and see if it's unjustify; if not, unget the keystroke 
2378
       and return */
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391

#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION

    /* If it was a mouse click, parse it with do_mouse and it might become
	the unjustify key.  Else give it back to the input stream.  */
    if ((kbinput = wgetch(edit)) == KEY_MOUSE)
	do_mouse();
    else
	ungetch(kbinput);
#endif
#endif

2392
    if ((kbinput = wgetch(edit)) != NANO_UNJUSTIFY_KEY) {
2393
	ungetch(kbinput);
2394
2395
	blank_statusbar_refresh();
    } else {
2396
2397
2398
	/* Else restore the justify we just did (ungrateful user!) */
	if (tmptop->prev != NULL)
	    tmptop->prev->next = tmpbot->next;
2399
2400
	else
	    fileage = current;
2401
	tmpbot->next->prev = tmptop->prev;
2402
	current = tmpbot->next;
2403
2404
	tmpbot->next = NULL;
	do_uncut_text();
2405
2406
2407
	if (tmptop->prev == NULL)
	    edit_refresh();

2408
	/* Restore totsize from before justify */
2409
	totsize = totbak;
2410
2411
2412
	free_filestruct(tmptop);
	blank_statusbar_refresh();
    }
2413
2414
    shortcut_init(0);
    display_main_list();
2415
2416
    free_filestruct(cutbuffer);
    cutbuffer = cutbak;
2417

Chris Allegretta's avatar
Chris Allegretta committed
2418
2419
2420
2421
    return 1;
#endif
}

2422
#ifndef DISABLE_HELP
Chris Allegretta's avatar
Chris Allegretta committed
2423
2424
void help_init(void)
{
2425
    int i, sofar = 0, meta_shortcut = 0, helplen;
Chris Allegretta's avatar
Chris Allegretta committed
2426
    long allocsize = 1;		/* How much space we're gonna need for the help text */
2427
    char buf[BUFSIZ] = "", *ptr = NULL;
2428
2429
    toggle *t;
    shortcut *s;
2430

2431
    helplen = length_of_list(currshortcut);
2432

2433
2434
2435
2436
2437
2438
2439
    /* First set up the initial help text for the current function */
    if (currshortcut == whereis_list || currshortcut == replace_list
	     || currshortcut == replace_list_2)
	ptr = _("Search Command Help Text\n\n "
		"Enter the words or characters you would like to search "
		"for, then hit enter.  If there is a match for the text you "
		"entered, the screen will be updated to the location of the "
Jordi Mallach's avatar
Jordi Mallach committed
2440
2441
2442
2443
		"nearest match for the search string.\n\n "
		"If using Pico Mode via the -p or --pico flags, using the "
		"Meta-P toggle or using a nanorc file, the previous search "
		"string will be shown in brackets after the Search: prompt.  "
Jordi Mallach's avatar
Jordi Mallach committed
2444
		"Hitting enter without entering any text will perform the "
Jordi Mallach's avatar
Jordi Mallach committed
2445
2446
2447
2448
		"previous search. Otherwise, the previous string will be "
		"placed in front of the cursor, and can be edited or deleted "
		"before hitting enter.\n\n The following functions keys are "
		"available in Search mode:\n\n");
2449
2450
2451
    else if (currshortcut == goto_list)
	ptr = _("Goto Line Help Text\n\n "
		"Enter the line number that you wish to go to and hit "
Jordi Mallach's avatar
Jordi Mallach committed
2452
		"Enter.  If there are fewer lines of text than the "
2453
2454
2455
2456
2457
2458
2459
2460
2461
		"number you entered, you will be brought to the last line "
		"of the file.\n\n The following functions keys are "
		"available in Goto Line mode:\n\n");
    else if (currshortcut == insertfile_list)
	ptr = _("Insert File Help Text\n\n "
		"Type in the name of a file to be inserted into the current "
		"file buffer at the current cursor location.\n\n "
		"If you have compiled nano with multiple file buffer "
		"support, and enable multiple buffers with the -F "
Jordi Mallach's avatar
Jordi Mallach committed
2462
2463
		"or --multibuffer command line flags, the Meta-F toggle or "
		"using a nanorc file, inserting a file will cause it to be "
2464
		"loaded into a separate buffer (use Meta-< and > to switch "
2465
2466
2467
2468
2469
		"between file buffers).\n\n If you need another blank "
		"buffer, just press Enter at the prompt without typing in a "
		"filename, or type in a nonexistent filename at the prompt "
		"and press Enter.\n\n The following function keys are "
		"available in Insert File mode:\n\n");
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
    else if (currshortcut == writefile_list)
	ptr = _("Write File Help Text\n\n "
		"Type the name that you wish to save the current file "
		"as and hit enter to save the file.\n\n "
		"If you are using the marker code with Ctrl-^ and have "
		"selected text, you will be prompted to save only the "
		"selected portion to a separate file.  To reduce the "
		"chance of overwriting the current file with just a portion "
		"of it, the current filename is not the default in this "
		"mode.\n\n The following function keys are available in "
		"Write File mode:\n\n");
#ifndef DISABLE_BROWSER
    else if (currshortcut == browser_list)
	ptr = _("File Browser Help Text\n\n "
		"The file browser is used to visually browse the "
		"directory structure to select a file for reading "
		"or writing.  You may use the arrow keys or Page Up/"
		"Down to browse through the files, and S or Enter to "
		"choose the selected file or enter the selected "
		"directory. To move up one level, select the directory "
		"called \"..\" at the top of the file list.\n\n The "
		"following functions keys are available in the file "
		"browser:\n\n");
    else if (currshortcut == gotodir_list)
	ptr = _("Browser Goto Directory Help Text\n\n "
		"Enter the name of the directory you would like to "
		"browse to.\n\n If tab completion has not been disabled, "
		"you can use the TAB key to (attempt to) automatically "
		"complete the directory name.  The following function "
		"keys are available in Browser GotoDir mode:\n\n");
#endif
    else if (currshortcut == spell_list)
	ptr = _("Spell Check Help Text\n\n "
		"The spell checker checks the spelling of all text "
		"in the current file.  When an unknown word is "
		"encountered, it is highlighted and a replacement can "
		"be edited.  It will then prompt to replace every "
		"instance of the given misspelled word in the "
		"current file.\n\n The following other functions are "
		"available in Spell Check mode:\n\n");
    else /* Default to the main help list */
	ptr = help_text_init;
Chris Allegretta's avatar
Chris Allegretta committed
2512
2513
2514

    /* Compute the space needed for the shortcut lists - we add 15 to
       have room for the shortcut abbrev and its possible alternate keys */
2515
2516
2517
2518
2519
2520
    s = currshortcut;
    for (i = 0; i <= helplen - 1; i++) {
	if (s->help != NULL)
	    allocsize += strlen(s->help) + 15;
	s = s->next;
    }
2521
2522
2523

    /* If we're on the main list, we also allocate space for toggle help text. */
    if (currshortcut == main_list) {
2524
2525
2526
	for (t = toggles; t != NULL; t = t->next)
	    if (t->desc != NULL)
		allocsize += strlen(t->desc) + 30;
2527
    }
Chris Allegretta's avatar
Chris Allegretta committed
2528

2529
    allocsize += strlen(ptr);
2530

Chris Allegretta's avatar
Chris Allegretta committed
2531
2532
2533
2534
    if (help_text != NULL)
	free(help_text);

    /* Allocate space for the help text */
2535
    help_text = charalloc(allocsize);
Chris Allegretta's avatar
Chris Allegretta committed
2536
2537

    /* Now add the text we want */
2538
    strcpy(help_text, ptr);
Chris Allegretta's avatar
Chris Allegretta committed
2539
2540

    /* Now add our shortcut info */
2541
    s = currshortcut;
2542
    for (i = 0; i <= helplen - 1; i++) {
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
	if (s->val > 0 && s->val < 'a')
	    sofar = snprintf(buf, BUFSIZ, "^%c	", s->val + 64);
	else {
	    if (s->altval > 0) {
		sofar = 0;
		meta_shortcut = 1;
	    }
	    else
		sofar = snprintf(buf, BUFSIZ, "	");
	}
Chris Allegretta's avatar
Chris Allegretta committed
2553

2554
2555
2556
2557
2558
2559
2560
	if (!meta_shortcut) {
	    if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
		sofar += snprintf(&buf[sofar], BUFSIZ - sofar, "(F%d)	",
				  s->misc1 - KEY_F0);
	    else
		sofar += snprintf(&buf[sofar], BUFSIZ - sofar, "	");
	}
Chris Allegretta's avatar
Chris Allegretta committed
2561

2562
2563
2564
2565
2566
2567
2568
2569
2570
	if (s->altval > 0 && s->altval < 91 
		&& (s->altval - 32) > 32)
	    sofar += snprintf(&buf[sofar], BUFSIZ - sofar,
	    (meta_shortcut ? "M-%c	" : "(M-%c)	"),
	    s->altval - 32);
	else if (s->altval > 0)
	    sofar += snprintf(&buf[sofar], BUFSIZ - sofar,
	    (meta_shortcut ? "M-%c	" : "(M-%c)	"),
	    s->altval);
2571
	/* Hack */
2572
2573
2574
2575
	else if (s->val >= 'a')
	    sofar += snprintf(&buf[sofar], BUFSIZ - sofar,
	    (meta_shortcut ? "(M-%c)	" : "M-%c	"),
	    s->val - 32);
Chris Allegretta's avatar
Chris Allegretta committed
2576
	else
2577
	    sofar += snprintf(&buf[sofar], BUFSIZ - sofar, "	");
Chris Allegretta's avatar
Chris Allegretta committed
2578

2579
2580
2581
2582
2583
2584
2585
2586
	if (meta_shortcut) {
	    if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
		sofar += snprintf(&buf[sofar], BUFSIZ - sofar,
			"(F%d)		", s->misc1 - KEY_F0);
	    else
		sofar += snprintf(&buf[sofar], BUFSIZ - sofar,
			"		");
	}
2587

2588
2589
	if (s->help != NULL)
	    snprintf(&buf[sofar], BUFSIZ - sofar, "%s", s->help);
2590

Chris Allegretta's avatar
Chris Allegretta committed
2591
	strcat(help_text, buf);
2592
	strcat(help_text, "\n");
2593
2594

	s = s->next;
Chris Allegretta's avatar
Chris Allegretta committed
2595
2596
    }

2597
    /* And the toggles... */
2598
    if (currshortcut == main_list)
2599
	for (t = toggles; t != NULL; t = t->next) {
2600
		sofar = snprintf(buf, BUFSIZ,
2601
2602
			     "M-%c			", t->val - 32);
	    if (t->desc != NULL) {
2603
		    snprintf(&buf[sofar], BUFSIZ - sofar, _("%s enable/disable"),
2604
			 t->desc);
2605
	}
2606
2607
2608
2609
2610
	strcat(help_text, buf);
	strcat(help_text, "\n");
    }

}
2611
#endif
2612

2613
void do_toggle(toggle *which)
2614
{
2615
2616
2617
#ifdef NANO_SMALL
    nano_disabled_msg();
#else
Jordi Mallach's avatar
   
Jordi Mallach committed
2618
2619
    char *enabled = _("enabled");
    char *disabled = _("disabled");
2620

2621
    switch (which->val) {
2622
2623
2624
2625
2626
2627
    case TOGGLE_BACKWARDS_KEY:
    case TOGGLE_CASE_KEY:
    case TOGGLE_REGEXP_KEY:
	return;
    }

2628
    /* Even easier! */
2629
    TOGGLE(which->flag);
Chris Allegretta's avatar
Chris Allegretta committed
2630

2631
    switch (which->val) {
2632
    case TOGGLE_PICOMODE_KEY:
2633
	shortcut_init(0);
Chris Allegretta's avatar
Chris Allegretta committed
2634
	SET(CLEAR_BACKUPSTRING);
2635
2636
2637
2638
2639
2640
2641
2642
2643
	display_main_list();
	break;
    case TOGGLE_SUSPEND_KEY:
	signal_init();
	break;
    case TOGGLE_MOUSE_KEY:
	mouse_init();
	break;
    case TOGGLE_NOHELP_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
2644
2645
2646
	wclear(bottomwin);
	wrefresh(bottomwin);
	window_init();
2647
	fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
2648
2649
	edit_refresh();
	display_main_list();
2650
	break;
2651
2652
2653
2654
2655
2656
    case TOGGLE_DOS_KEY:
	UNSET(MAC_FILE);
	break;
    case TOGGLE_MAC_KEY:
	UNSET(DOS_FILE);
	break;
2657
    }
Chris Allegretta's avatar
Chris Allegretta committed
2658

2659
2660
2661
2662
    if (!ISSET(which->flag)) {
	if (which->val == TOGGLE_NOHELP_KEY ||
	    which->val == TOGGLE_WRAP_KEY)
	    statusbar("%s %s", which->desc, enabled);
2663
	else
2664
	    statusbar("%s %s", which->desc, disabled);
2665
    } else {
2666
2667
2668
	if (which->val == TOGGLE_NOHELP_KEY ||
	    which->val == TOGGLE_WRAP_KEY)
	    statusbar("%s %s", which->desc, disabled);
2669
	else
2670
	    statusbar("%s %s", which->desc, enabled);
Chris Allegretta's avatar
Chris Allegretta committed
2671
    }
2672

2673
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2674
2675
}

2676
2677
/* If the NumLock key has made the keypad go awry, print an error
   message; hopefully we can address it later. */
2678
2679
2680
2681
void print_numlock_warning(void)
{
    static int didmsg = 0;
    if (!didmsg) {
2682
2683
	statusbar(_
		  ("NumLock glitch detected.  Keypad will malfunction with NumLock off"));
2684
2685
2686
2687
	didmsg = 1;
    }
}

2688
2689
2690
2691
2692
/* This function returns the correct keystroke, given the A,B,C or D
   input key.  This is a common sequence of many terms which send
   Esc-O-[A-D] or Esc-[-[A-D]. */
int ABCD(int input)
{
2693
2694
    switch (input) {
    case 'A':
2695
    case 'a':
2696
2697
	return (KEY_UP);
    case 'B':
2698
    case 'b':
2699
2700
	return (KEY_DOWN);
    case 'C':
2701
    case 'c':
2702
2703
	return (KEY_RIGHT);
    case 'D':
2704
    case 'd':
2705
2706
2707
	return (KEY_LEFT);
    default:
	return 0;
2708
2709
2710
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
2711
2712
2713
2714
2715
int main(int argc, char *argv[])
{
    int optchr;
    int kbinput;		/* Input from keyboard */
    long startline = 0;		/* Line to try and start at */
2716
    int keyhandled;		/* Have we handled the keystroke yet? */
2717
    int modify_control_seq;
Chris Allegretta's avatar
Chris Allegretta committed
2718
    char *argv0;
2719
2720
    shortcut *s;
    toggle *t;
2721

Chris Allegretta's avatar
Chris Allegretta committed
2722
#ifdef _POSIX_VDISABLE
Chris Allegretta's avatar
Chris Allegretta committed
2723
    struct termios term;
Chris Allegretta's avatar
Chris Allegretta committed
2724
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2725
2726
2727
2728

#ifdef HAVE_GETOPT_LONG
    int option_index = 0;
    struct option long_options[] = {
2729
#ifdef HAVE_REGEX_H
2730
	{"regexp", 0, 0, 'R'},
2731
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2732
2733
2734
2735
2736
2737
	{"version", 0, 0, 'V'},
	{"const", 0, 0, 'c'},
	{"suspend", 0, 0, 'z'},
	{"nowrap", 0, 0, 'w'},
	{"nohelp", 0, 0, 'x'},
	{"help", 0, 0, 'h'},
2738
	{"view", 0, 0, 'v'},
2739
#ifndef NANO_SMALL
2740
	{"cut", 0, 0, 'k'},
2741
	{"dos", 0, 0, 'D'},
Chris Allegretta's avatar
Chris Allegretta committed
2742
	{"mac", 0, 0, 'M'},
2743
	{"noconvert", 0, 0, 'N'},
Chris Allegretta's avatar
Chris Allegretta committed
2744
	{"autoindent", 0, 0, 'i'},
2745
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2746
	{"tempfile", 0, 0, 't'},
2747
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
2748
	{"speller", 1, 0, 's'},
2749
#endif
2750
2751

#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
2752
	{"fill", 1, 0, 'r'},
2753
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2754
	{"mouse", 0, 0, 'm'},
2755
2756
2757
#ifndef DISABLE_OPERATINGDIR
	{"operatingdir", 1, 0, 'o'},
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2758
2759
	{"pico", 0, 0, 'p'},
	{"nofollow", 0, 0, 'l'},
2760
	{"tabsize", 1, 0, 'T'},
2761

2762
#ifdef ENABLE_MULTIBUFFER
2763
	{"multibuffer", 0, 0, 'F'},
2764
#endif
2765
2766
2767
#ifndef NANO_SMALL
	{"smooth", 0, 0, 'S'},
#endif
2768
	{"keypad", 0, 0, 'K'},
Chris Allegretta's avatar
Chris Allegretta committed
2769
2770
2771
2772
2773
2774
2775
2776
	{0, 0, 0, 0}
    };
#endif

    /* Flag inits... */
    SET(FOLLOW_SYMLINKS);

#ifndef NANO_SMALL
2777
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
2778
2779
2780
2781
    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif
2782
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2783

2784
2785
2786
2787
#ifdef ENABLE_NANORC
    do_rcfile();
#endif /* ENABLE_NANORC */

Chris Allegretta's avatar
Chris Allegretta committed
2788
#ifdef HAVE_GETOPT_LONG
2789
    while ((optchr = getopt_long(argc, argv, "h?DFKMNRST:Vabcefgijklmo:pr:s:tvwxz",
Chris Allegretta's avatar
Chris Allegretta committed
2790
2791
				 long_options, &option_index)) != EOF) {
#else
2792
    while ((optchr =
2793
	    getopt(argc, argv, "h?DFKMNRST:Vabcefgijklmo:pr:s:tvwxz")) != EOF) {
Chris Allegretta's avatar
Chris Allegretta committed
2794
2795
2796
#endif

	switch (optchr) {
2797

2798
2799
2800
2801
2802
#ifndef NANO_SMALL
	case 'D':
	    SET(DOS_FILE);
	    break;
#endif
2803
#ifdef ENABLE_MULTIBUFFER
2804
	case 'F':
2805
	    SET(MULTIBUFFER);
2806
2807
	    break;
#endif
2808
2809
2810
	case 'K':
	    SET(ALT_KEYPAD);
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
2811
2812
2813
2814
#ifndef NANO_SMALL
	case 'M':
	    SET(MAC_FILE);
	    break;
2815
2816
	case 'N':
	    SET(NO_CONVERT);
Chris Allegretta's avatar
Chris Allegretta committed
2817
	    break;
2818
#endif
2819
#ifdef HAVE_REGEX_H
2820
2821
2822
	case 'R':
	    SET(USE_REGEXP);
	    break;
2823
2824
2825
2826
2827
#endif
#ifndef NANO_SMALL
	case 'S':
	    SET(SMOOTHSCROLL);
	    break;
2828
#endif
2829
2830
2831
2832
2833
2834
2835
	case 'T':
	    tabsize = atoi(optarg);
	    if (tabsize <= 0) {
		usage();	/* To stop bogus data for tab width */
		finish(1);
	    }
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
2836
2837
2838
	case 'V':
	    version();
	    exit(0);
2839
	case 'a':
2840
2841
2842
	case 'b':
	case 'e':
	case 'f':
Rocco Corsi's avatar
   
Rocco Corsi committed
2843
2844
	case 'g':
	case 'j':
2845
2846
	    /* Pico compatibility flags */
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
2847
2848
2849
2850
2851
2852
2853
	case 'c':
	    SET(CONSTUPDATE);
	    break;
	case 'h':
	case '?':
	    usage();
	    exit(0);
2854
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2855
2856
2857
	case 'i':
	    SET(AUTOINDENT);
	    break;
2858
2859
2860
	case 'k':
	    SET(CUT_TO_END);
	    break;
2861
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2862
2863
2864
2865
2866
2867
	case 'l':
	    UNSET(FOLLOW_SYMLINKS);
	    break;
	case 'm':
	    SET(USE_MOUSE);
	    break;
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
#ifndef DISABLE_OPERATINGDIR
	case 'o':
	    operating_dir = charalloc(strlen(optarg) + 1);
	    strcpy(operating_dir, optarg);

	    /* make sure we're inside the operating directory */
	    if (check_operating_dir(".", 0)) {
		if (chdir(operating_dir) == -1) {
		    free(operating_dir);
		    operating_dir = NULL;
		}
	    }
	    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2882
	case 'p':
2883
	    SET(PICO_MODE);
Chris Allegretta's avatar
Chris Allegretta committed
2884
2885
	    break;
	case 'r':
2886
#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta's avatar
Chris Allegretta committed
2887
	    fill = atoi(optarg);
2888
2889
2890
	    if (fill < 0)
		wrap_at = fill;
	    else if (fill == 0) {
Chris Allegretta's avatar
Chris Allegretta committed
2891
2892
2893
2894
		usage();	/* To stop bogus data (like a string) */
		finish(1);
	    }
	    break;
2895
2896
2897
2898
2899
#else
	    usage();
	    exit(0);

#endif
2900
#ifndef DISABLE_SPELLER
Chris Allegretta's avatar
Chris Allegretta committed
2901
	case 's':
2902
	    alt_speller = charalloc(strlen(optarg) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2903
2904
	    strcpy(alt_speller, optarg);
	    break;
2905
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2906
	case 't':
2907
	    SET(TEMP_OPT);
Chris Allegretta's avatar
Chris Allegretta committed
2908
2909
2910
2911
2912
	    break;
	case 'v':
	    SET(VIEW_MODE);
	    break;
	case 'w':
2913
2914
2915
2916
#ifdef DISABLE_WRAPPING
	    usage();
	    exit(0);
#else
Chris Allegretta's avatar
Chris Allegretta committed
2917
2918
	    SET(NO_WRAP);
	    break;
2919
#endif				/* DISABLE_WRAPPING */
Chris Allegretta's avatar
Chris Allegretta committed
2920
2921
2922
2923
2924
2925
2926
2927
	case 'x':
	    SET(NO_HELP);
	    break;
	case 'z':
	    SET(SUSPEND);
	    break;
	default:
	    usage();
Chris Allegretta's avatar
Chris Allegretta committed
2928
	    exit(0);
Chris Allegretta's avatar
Chris Allegretta committed
2929
2930
2931
2932
2933
2934
2935
	}

    }

    argv0 = strrchr(argv[0], '/');
    if ((argv0 && strstr(argv0, "pico"))
	|| (!argv0 && strstr(argv[0], "pico")))
2936
	SET(PICO_MODE);
Chris Allegretta's avatar
Chris Allegretta committed
2937
2938
2939
2940

    /* See if there's a non-option in argv (first non-option is the
       filename, if +LINE is not given) */
    if (argc == 1 || argc <= optind)
Chris Allegretta's avatar
Chris Allegretta committed
2941
	clear_filename();
Chris Allegretta's avatar
Chris Allegretta committed
2942
2943
2944
2945
2946
2947
    else {
	/* Look for the +line flag... */
	if (argv[optind][0] == '+') {
	    startline = atoi(&argv[optind][1]);
	    optind++;
	    if (argc == 1 || argc <= optind)
Chris Allegretta's avatar
Chris Allegretta committed
2948
		clear_filename();
Chris Allegretta's avatar
Chris Allegretta committed
2949
	    else
Chris Allegretta's avatar
Chris Allegretta committed
2950
		filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta's avatar
Chris Allegretta committed
2951

Chris Allegretta's avatar
Chris Allegretta committed
2952
2953
	} else
	    filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta's avatar
Chris Allegretta committed
2954
2955
2956
2957
    }


    /* First back up the old settings so they can be restored, duh */
Chris Allegretta's avatar
Chris Allegretta committed
2958
    tcgetattr(0, &oldterm);
Chris Allegretta's avatar
Chris Allegretta committed
2959

2960
#ifdef _POSIX_VDISABLE
2961
2962
2963
2964
    term = oldterm;
    term.c_cc[VINTR] = _POSIX_VDISABLE;
    term.c_cc[VQUIT] = _POSIX_VDISABLE;
    term.c_lflag &= ~IEXTEN;
Chris Allegretta's avatar
Chris Allegretta committed
2965
    tcsetattr(0, TCSANOW, &term);
2966
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2967
2968
2969
2970
2971
2972
2973
2974
2975

    /* now ncurses init stuff... */
    initscr();
    savetty();
    nonl();
    cbreak();
    noecho();

    /* Set up some global variables */
2976
    global_init(0);
2977
    shortcut_init(0);
2978
#ifndef DISABLE_HELP
Chris Allegretta's avatar
Chris Allegretta committed
2979
2980
    init_help_msg();
    help_init();
2981
#endif
2982
    signal_init();
Chris Allegretta's avatar
Chris Allegretta committed
2983
2984
2985
2986
2987

#ifdef DEBUG
    fprintf(stderr, _("Main: set up windows\n"));
#endif

2988
2989
2990
    window_init();
    mouse_init();

2991
2992
2993
2994
2995
    if (!ISSET(ALT_KEYPAD)) {
	keypad(edit, TRUE);
	keypad(bottomwin, TRUE);
    }

2996
2997
2998
#ifdef ENABLE_COLOR
    do_colorinit();

2999
#endif /* ENABLE_COLOR */
Chris Allegretta's avatar
Chris Allegretta committed
3000
3001
3002
3003

#ifdef DEBUG
    fprintf(stderr, _("Main: bottom win\n"));
#endif
3004
    /* Set up bottom of window */
Chris Allegretta's avatar
Chris Allegretta committed
3005
3006
3007
3008
3009
3010
    display_main_list();

#ifdef DEBUG
    fprintf(stderr, _("Main: open file\n"));
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3011
    titlebar(NULL);
3012
3013
3014
3015

    /* Now we check to see if argv[optind] is non-null to determine if
       we're dealing with a new file or not, not argc == 1... */
    if (argv[optind] == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
3016
3017
3018
3019
3020
	new_file();
    else
	open_file(filename, 0, 0);

    if (startline > 0)
3021
	do_gotoline(startline, 0);
Chris Allegretta's avatar
Chris Allegretta committed
3022
    else
3023
	edit_update(fileage, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
3024

3025
    /* return here after a sigwinch */
3026
    sigsetjmp(jmpbuf, 1);
3027
3028
3029
3030
3031
3032

    /* Fix clobber-age */
    kbinput = 0;
    keyhandled = 0;
    modify_control_seq = 0;

Robert Siemborski's avatar
Robert Siemborski committed
3033
3034
3035
    edit_refresh();
    reset_cursor();

Chris Allegretta's avatar
Chris Allegretta committed
3036
    while (1) {
3037

3038
#ifndef DISABLE_MOUSE
3039
	currshortcut = main_list;
3040
#endif
3041

3042
3043
3044
3045
3046
#ifndef _POSIX_VDISABLE
	/* We're going to have to do it the old way, i.e. on cygwin */
	raw();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3047
	kbinput = wgetch(edit);
3048
3049
3050
#ifdef DEBUG
	fprintf(stderr, "AHA!  %c (%d)\n", kbinput, kbinput);
#endif
3051

Chris Allegretta's avatar
Chris Allegretta committed
3052
3053
	if (kbinput == 27) {	/* Grab Alt-key stuff first */
	    switch (kbinput = wgetch(edit)) {
3054
		/* Alt-O, suddenly very important ;) */
3055
	    case 'O':
3056
		kbinput = wgetch(edit);
3057
3058
		if ((kbinput <= 'D' && kbinput >= 'A') ||
			(kbinput <= 'd' && kbinput >= 'a'))
3059
		    kbinput = ABCD(kbinput);
3060
3061
3062
		else if (kbinput <= 'z' && kbinput >= 'j')
		    print_numlock_warning();
		else if (kbinput <= 'S' && kbinput >= 'P')
3063
		    kbinput = KEY_F(kbinput - 79);
3064
3065
#ifdef DEBUG
		else {
3066
3067
3068
		    fprintf(stderr, _("I got Alt-O-%c! (%d)\n"),
			    kbinput, kbinput);
		    break;
3069
3070
3071
		}
#endif
		break;
3072
3073
3074
3075
3076
3077
	    case 27:
		/* If we get Alt-Alt, the next keystroke should be the same as a
		   control sequence */
		modify_control_seq = 1;
		keyhandled = 1;
		break;
3078
#ifndef NANO_SMALL
3079
3080
3081
3082
3083
	    case ' ':
		/* If control-space is next word, Alt-space should be previous word */
		do_prev_word();
		keyhandled = 1;
		break;
3084
#endif
3085
	    case '[':
Chris Allegretta's avatar
Chris Allegretta committed
3086
		switch (kbinput = wgetch(edit)) {
3087
		case '1':	/* Alt-[-1-[0-5,7-9] = F1-F8 in X at least */
3088
3089
		    kbinput = wgetch(edit);
		    if (kbinput >= '1' && kbinput <= '5') {
3090
3091
3092
3093
3094
			kbinput = KEY_F(kbinput - 48);
			wgetch(edit);
		    } else if (kbinput >= '7' && kbinput <= '9') {
			kbinput = KEY_F(kbinput - 49);
			wgetch(edit);
3095
		    } else if (kbinput == '~')
3096
			kbinput = KEY_HOME;
3097
3098
3099

#ifdef DEBUG
		    else {
3100
3101
3102
			fprintf(stderr, _("I got Alt-[-1-%c! (%d)\n"),
				kbinput, kbinput);
			break;
3103
3104
3105
3106
		    }
#endif

		    break;
3107
		case '2':	/* Alt-[-2-[0,1,3,4] = F9-F12 in many terms */
3108
3109
		    kbinput = wgetch(edit);
		    switch (kbinput) {
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
		    case '0':
			kbinput = KEY_F(9);
			wgetch(edit);
			break;
		    case '1':
			kbinput = KEY_F(10);
			wgetch(edit);
			break;
		    case '3':
			kbinput = KEY_F(11);
			wgetch(edit);
			break;
		    case '4':
			kbinput = KEY_F(12);
			wgetch(edit);
			break;
3126
3127
		    case '~':
			goto do_insertkey;
3128
#ifdef DEBUG
3129
3130
3131
3132
		    default:
			fprintf(stderr, _("I got Alt-[-2-%c! (%d)\n"),
				kbinput, kbinput);
			break;
3133
3134
3135
3136
#endif

		    }
		    break;
3137
		case '3':	/* Alt-[-3 = Delete? */
3138
3139
3140
		    kbinput = NANO_DELETE_KEY;
		    wgetch(edit);
		    break;
3141
		case '4':	/* Alt-[-4 = End? */
3142
3143
3144
		    kbinput = NANO_END_KEY;
		    wgetch(edit);
		    break;
3145
		case '5':	/* Alt-[-5 = Page Up */
3146
3147
3148
		    kbinput = KEY_PPAGE;
		    wgetch(edit);
		    break;
3149
		case 'V':	/* Alt-[-V = Page Up in Hurd Console */
3150
3151
3152
		case 'I':	/* Alt-[-I = Page Up - FreeBSD Console */
		    kbinput = KEY_PPAGE;
		    break;
3153
		case '6':	/* Alt-[-6 = Page Down */
3154
3155
3156
		    kbinput = KEY_NPAGE;
		    wgetch(edit);
		    break;
3157
		case 'U':	/* Alt-[-U = Page Down in Hurd Console */
3158
3159
3160
		case 'G':	/* Alt-[-G = Page Down - FreeBSD Console */
		    kbinput = KEY_NPAGE;
		    break;
Chris Allegretta's avatar
Chris Allegretta committed
3161
3162
3163
3164
3165
3166
3167
3168
		case '7':
		    kbinput = KEY_HOME;
		    wgetch(edit);
		    break;
		case '8':
		    kbinput = KEY_END;
		    wgetch(edit);
		    break;
3169
3170
3171
		case '9':	/* Alt-[-9 = Delete in Hurd Console */
		    kbinput = KEY_DC;
		    break;
3172
3173
3174
3175
		case '@':	/* Alt-[-@ = Insert in Hurd Console */
		case 'L':	/* Alt-[-L = Insert - FreeBSD Console */
		    goto do_insertkey;
		case '[':	/* Alt-[-[-[A-E], F1-F5 in Linux console */
3176
		    kbinput = wgetch(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3177
		    if (kbinput >= 'A' && kbinput <= 'E')
3178
			kbinput = KEY_F(kbinput - 64);
3179
		    break;
Chris Allegretta's avatar
Chris Allegretta committed
3180
3181
3182
3183
		case 'A':
		case 'B':
		case 'C':
		case 'D':
3184
3185
3186
3187
		case 'a':
		case 'b':
		case 'c':
		case 'd':
3188
		    kbinput = ABCD(kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
3189
3190
3191
3192
3193
		    break;
		case 'H':
		    kbinput = KEY_HOME;
		    break;
		case 'F':
3194
		case 'Y':		/* End Key in Hurd Console */
Chris Allegretta's avatar
Chris Allegretta committed
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
		    kbinput = KEY_END;
		    break;
		default:
#ifdef DEBUG
		    fprintf(stderr, _("I got Alt-[-%c! (%d)\n"),
			    kbinput, kbinput);
#endif
		    break;
		}
		break;
3205

3206
#ifdef ENABLE_MULTIBUFFER
3207
	    case NANO_OPENPREV_KEY:
3208
	    case NANO_OPENPREV_ALTKEY:
3209
3210
3211
3212
		open_prevfile(0);
		keyhandled = 1;
		break;
	    case NANO_OPENNEXT_KEY:
3213
	    case NANO_OPENNEXT_ALTKEY:
3214
3215
3216
		open_nextfile(0);
		keyhandled = 1;
		break;
Chris Allegretta's avatar
Chris Allegretta committed
3217
#endif
3218
3219
3220
3221
3222
3223
3224
3225

#if !defined (NANO_SMALL) && defined (HAVE_REGEX_H)
	    case NANO_BRACKET_KEY:
		do_find_bracket();
		keyhandled = 1;
		break;
#endif

Chris Allegretta's avatar
Chris Allegretta committed
3226
3227
	    default:
		/* Check for the altkey defs.... */
3228
3229
3230
3231
		for (s = main_list; s != NULL; s = s->next)
		    if (kbinput == s->altval ||
			kbinput == s->altval - 32) {
			kbinput = s->val;
3232
			break;
Chris Allegretta's avatar
Chris Allegretta committed
3233
		    }
3234
3235
#ifndef NANO_SMALL
		/* And for toggle switches */
3236
3237
3238
3239
3240
		for (t = toggles; t != NULL && !keyhandled; t = t->next)
		    if (kbinput == t->val ||
			(t->val > 'a' && 
				kbinput == t->val - 32)) {
			do_toggle(t);
3241
3242
			keyhandled = 1;
			break;
3243
3244
		    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3245
3246
3247
3248
3249
3250
3251
#ifdef DEBUG
		fprintf(stderr, _("I got Alt-%c! (%d)\n"), kbinput,
			kbinput);
#endif
		break;
	    }
	}
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
	/* If the modify_control_seq is set, we received an Alt-Alt 
	   sequence before this, so we make this key a control sequence 
	   by subtracting 64 or 96, depending on its value. */
	if (!keyhandled && modify_control_seq) {
	    if (kbinput >= 'A' && kbinput < 'a')
		kbinput -= 64;
	    else if (kbinput >= 'a' && kbinput <= 'z')
		kbinput -= 96;

	    modify_control_seq = 0;
	}

Chris Allegretta's avatar
Chris Allegretta committed
3264
3265
	/* Look through the main shortcut list to see if we've hit a
	   shortcut key */
3266
        
3267
#if !defined(DISABLE_BROWSER) || !defined(DISABLE_MOUSE) || !defined (DISABLE_HELP)
3268
	for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
3269
3270
3271
#else
	for (s = main_list; s != NULL && !keyhandled; s = s->next) {
#endif
3272
3273
3274
3275
	    if (kbinput == s->val ||
		(s->misc1 && kbinput == s->misc1) ||
		(s->misc2 && kbinput == s->misc2)) {
		if (ISSET(VIEW_MODE) && !s->viewok)
Chris Allegretta's avatar
Chris Allegretta committed
3276
3277
		    print_view_warning();
		else
3278
		    s->func();
Chris Allegretta's avatar
Chris Allegretta committed
3279
3280
3281
		keyhandled = 1;
	    }
	}
3282
3283
	/* If we're in raw mode or using Alt-Alt-x, we have to catch
	   Control-S and Control-Q */
3284
3285
3286
	if (kbinput == 17 || kbinput == 19)
	    keyhandled = 1;

3287
3288
3289
	/* Catch ^Z by hand when triggered also 
	   407 == ^Z in Linux console when keypad() is used? */
	if (kbinput == 26 || kbinput == 407) {
3290
3291
3292
3293
3294
	    if (ISSET(SUSPEND))
		do_suspend(0);
	    keyhandled = 1;
	}

3295

3296
#ifndef USE_SLANG
3297
3298
	/* Hack, make insert key do something useful, like insert file */
	if (kbinput == KEY_IC) {
3299
3300
3301
#else
	if (0) {
#endif
3302
3303
3304
	  do_insertkey:

#ifdef ENABLE_MULTIBUFFER
3305
3306
3307
3308
	    /* do_insertfile_void() contains the logic needed to
	       handle view mode with the view mode/multibuffer
	       exception, so use it here */
	    do_insertfile_void();
3309
#else
3310
	    print_view_warning();
3311
3312
#endif

3313
	    keyhandled = 1;
3314
3315
	}

Chris Allegretta's avatar
Chris Allegretta committed
3316
3317
3318
	/* Last gasp, stuff that's not in the main lists */
	if (!keyhandled)
	    switch (kbinput) {
3319
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
3320
3321
3322
3323
3324
3325
#ifdef NCURSES_MOUSE_VERSION
	    case KEY_MOUSE:
		do_mouse();
		break;
#endif
#endif
3326

Chris Allegretta's avatar
Chris Allegretta committed
3327
	    case 0:		/* Erg */
3328
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
3329
3330
		do_next_word();
		break;
3331
#endif
3332

3333
	    case -1:		/* Stuff that we don't want to do squat */
Chris Allegretta's avatar
Chris Allegretta committed
3334
	    case 410:		/* Must ignore this, it gets sent when we resize */
3335
	    case 29:		/* Ctrl-] */
Chris Allegretta's avatar
Chris Allegretta committed
3336
#ifdef PDCURSES
3337
3338
	    case 541:		/* ???? */
	    case 542:		/* Control and alt in Windows *shrug* */
3339
	    case 543:		/* Right ctrl key */
Chris Allegretta's avatar
Chris Allegretta committed
3340
	    case 544:
3341
	    case 545:		/* Right alt key */
Chris Allegretta's avatar
Chris Allegretta committed
3342
#endif
3343

Chris Allegretta's avatar
Chris Allegretta committed
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
		break;
	    default:
#ifdef DEBUG
		fprintf(stderr, "I got %c (%d)!\n", kbinput, kbinput);
#endif
		/* We no longer stop unhandled sequences so that people with
		   odd character sets can type... */

		if (ISSET(VIEW_MODE)) {
		    print_view_warning();
		    break;
		}
		do_char(kbinput);
	    }
3358
3359
3360
	if (ISSET(DISABLE_CURPOS))
	    UNSET(DISABLE_CURPOS);
	else if (ISSET(CONSTUPDATE))
3361
		do_cursorpos(1);
Chris Allegretta's avatar
Chris Allegretta committed
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371

	reset_cursor();
	wrefresh(edit);
	keyhandled = 0;
    }

    getchar();
    finish(0);

}