winio.c 103 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   winio.c                                                              *
 *                                                                        *
5
 *   Copyright (C) 1999-2004 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
24
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
25

Chris Allegretta's avatar
Chris Allegretta committed
26
27
#include <stdarg.h>
#include <string.h>
28
#include <stdlib.h>
29
#include <unistd.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
30
#include <ctype.h>
31
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
32
33
34
#include "proto.h"
#include "nano.h"

35
36
37
38
39
40
41
42
static buffer *key_buffer = NULL;
				/* The default keystroke buffer,
				 * containing all the keystrokes we have
				 * at a given point. */
static size_t key_buffer_len = 0;
				/* The length of the default keystroke
				 * buffer. */
static int statusblank = 0;	/* The number of keystrokes left after
43
44
				 * we call statusbar(), before we
				 * actually blank the statusbar. */
45
46
47
static bool resetstatuspos = FALSE;
				/* Should we reset the cursor position
				 * at the statusbar prompt? */
48

49
50
51
52
53
54
55
56
57
58
59
60
/* Control character compatibility:
 *
 * - NANO_BACKSPACE_KEY is Ctrl-H, which is Backspace under ASCII, ANSI,
 *   VT100, and VT220.
 * - NANO_TAB_KEY is Ctrl-I, which is Tab under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_ENTER_KEY is Ctrl-M, which is Enter under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_XON_KEY is Ctrl-Q, which is XON under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_XOFF_KEY is Ctrl-S, which is XOFF under ASCII, ANSI, VT100,
 *   VT220, and VT320.
61
 * - NANO_CONTROL_8 is Ctrl-8 (Ctrl-?), which is Delete under ASCII,
62
63
 *   ANSI, VT100, and VT220, and which is Backspace under VT320.
 *
64
 * Note: VT220 and VT320 also generate Esc [ 3 ~ for Delete.  By
65
66
 * default, xterm assumes it's running on a VT320 and generates Ctrl-8
 * (Ctrl-?) for Backspace and Esc [ 3 ~ for Delete.  This causes
67
 * problems for VT100-derived terminals such as the FreeBSD console,
68
 * which expect Ctrl-H for Backspace and Ctrl-8 (Ctrl-?) for Delete, and
69
70
71
72
73
74
75
76
77
 * on which the VT320 sequences are translated by the keypad to KEY_DC
 * and [nothing].  We work around this conflict via the REBIND_DELETE
 * flag: if it's not set, we assume VT320 compatibility, and if it is,
 * we assume VT100 compatibility.  Thanks to Lee Nelson and Wouter van
 * Hemel for helping work this conflict out.
 *
 * Escape sequence compatibility:
 *
 * We support escape sequences for ANSI, VT100, VT220, VT320, the Linux
78
 * console, the FreeBSD console, the Mach console (a.k.a. the Hurd
79
80
81
82
83
84
85
86
 * console), xterm, rxvt, and Eterm.  Among these, there are several
 * conflicts and omissions, outlined as follows:
 *
 * - Tab on ANSI == PageUp on FreeBSD console; the former is omitted.
 *   (Ctrl-I is also Tab on ANSI, which we already support.)
 * - PageDown on FreeBSD console == Center (5) on numeric keypad with
 *   NumLock off on Linux console; the latter is omitted.  (The editing
 *   keypad key is more important to have working than the numeric
87
 *   keypad key, because the latter has no value when NumLock is off.)
88
89
90
91
 * - F1 on FreeBSD console == the mouse key on xterm/rxvt/Eterm; the
 *   latter is omitted.  (Mouse input will only work properly if the
 *   extended keypad value KEY_MOUSE is generated on mouse events
 *   instead of the escape sequence.)
92
 * - F9 on FreeBSD console == PageDown on Mach console; the former is
93
94
95
 *   omitted.  (The editing keypad is more important to have working
 *   than the function keys, because the functions of the former are not
 *   arbitrary and the functions of the latter are.)
96
 * - F10 on FreeBSD console == PageUp on Mach console; the former is
97
 *   omitted.  (Same as above.)
98
 * - F13 on FreeBSD console == End on Mach console; the former is
99
 *   omitted.  (Same as above.)
100
101
102
103
104
105
106
 * - F15 on FreeBSD console == Shift-Up on rxvt/Eterm; the former is
 *   omitted.  (The arrow keys, with or without modifiers, are more
 *   important to have working than the function keys, because the
 *   functions of the former are not arbitrary and the functions of the
 *   latter are.)
 * - F16 on FreeBSD console == Shift-Down on rxvt/Eterm; the former is
 *   omitted.  (Same as above.)
107
108
109
 *
 * Note that Center (5) on the numeric keypad with NumLock off can also
 * be the Begin key. */
110

111
112
113
114
#ifndef NANO_SMALL
/* Reset all the input routines that rely on character sequences. */
void reset_kbinput(void)
{
115
116
117
118
119
120
121
122
123
#ifdef NANO_WIDE
    /* Reset the multibyte and wide character interpreter states. */
    if (!ISSET(NO_UTF8)) {
	mbtowc(NULL, NULL, 0);
	wctomb(NULL, 0);
    }
#endif
    parse_kbinput(NULL, NULL, NULL, TRUE);
    get_word_kbinput(0, TRUE);
124
125
126
}
#endif

127
128
129
130
/* Read in a sequence of keystrokes from win and save them in the
 * default keystroke buffer.  This should only be called when the
 * default keystroke buffer is empty. */
void get_buffer(WINDOW *win)
131
{
132
    int input;
133
    size_t i;
134
135
136
137
138
139
140

    /* If the keystroke buffer isn't empty, get out. */
    if (key_buffer != NULL)
	return;

    /* Read in the first character using blocking input. */
    nodelay(win, FALSE);
141

142
143
144
#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
#endif
145
146
147
148
    input = wgetch(win);
#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif
149

150
151
152
153
154
155
156
157
    /* Increment the length of the keystroke buffer, save the value of
     * the keystroke in key, and set key_code to TRUE if the keystroke
     * is an extended keypad value and hence shouldn't be treated as a
     * multibyte character. */
    key_buffer_len++;
    key_buffer = (buffer *)nmalloc(sizeof(buffer));
    key_buffer[0].key = input;
    key_buffer[0].key_code = !is_byte_char(input);
158

159
160
161
162
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
163
#ifndef NANO_SMALL
164
	allow_pending_sigwinch(TRUE);
165
#endif
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
	input = wgetch(win);
#ifndef NANO_SMALL
	allow_pending_sigwinch(FALSE);
#endif

	/* If there aren't any more characters, stop reading. */
	if (input == ERR)
	    break;

	/* Otherwise, increment the length of the keystroke buffer, save
	 * the value of the keystroke in key, and set key_code to TRUE
	 * if the keystroke is an extended keypad value and hence
	 * shouldn't be treated as a multibyte character. */
	key_buffer_len++;
	key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len *
		sizeof(buffer));
	key_buffer[key_buffer_len - 1].key = input;
	key_buffer[key_buffer_len - 1].key_code = !is_byte_char(input);
    }

    /* Switch back to non-blocking input. */
    nodelay(win, FALSE);
188

189
190
#ifdef NANO_WIDE
    if (!ISSET(NO_UTF8)) {
191
192
193
	buffer *clean_key_buffer = NULL;
	size_t clean_key_buffer_len = 0;

194
195
	/* Change all incomplete or invalid multibyte keystrokes to -1,
	 * and change all complete and valid multibyte keystrokes to
196
	 * their wide character values. */
197
198
199
200
	for (i = 0; i < key_buffer_len; i++) {
	    wchar_t wide_key;

	    if (!key_buffer[i].key_code) {
201
202
203
204
205
		if (mbtowc(&wide_key,
			(const char *)&key_buffer[i].key, 1) == -1)
		    key_buffer[i].key = -1;
		else
		    key_buffer[i].key = wide_key;
206
	    }
207
	}
208

209
210
211
	/* Save all of the non-(-1) keystrokes in another buffer. */
	for (i = 0; i < key_buffer_len; i++) {
	    if (key_buffer[i].key != -1) {
212
213
214
		clean_key_buffer_len++;
		clean_key_buffer = (buffer *)nrealloc(clean_key_buffer,
			clean_key_buffer_len * sizeof(buffer));
215

216
		clean_key_buffer[clean_key_buffer_len - 1].key =
217
			key_buffer[i].key;
218
		clean_key_buffer[clean_key_buffer_len - 1].key_code =
219
			key_buffer[i].key_code;
220
	    }
221
	}
222
223
224

	/* Replace the default keystroke buffer with the non-(-1)
	 * keystroke buffer. */
225
	key_buffer_len = clean_key_buffer_len;
226
	free(key_buffer);
227
	key_buffer = clean_key_buffer;
228
    }
229
230
#endif
}
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* Return the length of the default keystroke buffer. */
size_t get_buffer_len(void)
{
    return key_buffer_len;
}

/* Return the key values stored in the keystroke buffer input,
 * discarding the key_code values in it. */
int *buffer_to_keys(buffer *input, size_t input_len)
{
    int *sequence = (int *)nmalloc(input_len * sizeof(int));
    size_t i;

    for (i = 0; i < input_len; i++)
	sequence[i] = input[i].key;

    return sequence;
}

/* Add the contents of the keystroke buffer input to the default
 * keystroke buffer. */
void unget_input(buffer *input, size_t input_len)
{
255
256
257
258
    size_t i;
    buffer *clean_input = NULL;
    size_t clean_input_len = 0;

259
260
261
#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
    allow_pending_sigwinch(FALSE);
262
#endif
263

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#ifdef NANO_WIDE
    if (!ISSET(NO_UTF8)) {
	/* Change all invalid wide character values to -1. */
	for (i = 0; i < input_len; i++) {
	    char key[MB_LEN_MAX];

	    if (!input[i].key_code) {
		if (wctomb(key, input[i].key) == -1)
		    input[i].key = -1;
	    }
	}

	/* Save all of the non-(-1) wide characters in another
	 * buffer. */
	for (i = 0; i < input_len; i++) {
	    if (input[i].key != -1) {
		clean_input_len++;
		clean_input = (buffer *)nrealloc(clean_input,
			clean_input_len * sizeof(buffer));

		clean_input[clean_input_len - 1].key = input[i].key;
		clean_input[clean_input_len - 1].key_code =
			input[i].key_code;
	    }
	}
    } else {
#endif
	clean_input = input;
	clean_input_len = input_len;
#ifdef NANO_WIDE
    }
#endif

297
    /* If input is empty, get out. */
298
    if (clean_input_len == 0)
299
300
301
302
303
	return;

    /* If adding input would put the default keystroke buffer beyond
     * maximum capacity, only add enough of input to put it at maximum
     * capacity. */
304
305
    if (key_buffer_len + clean_input_len < key_buffer_len)
	clean_input_len = (size_t)-1 - key_buffer_len;
306
307
308
309

    /* Add the length of input to the length of the default keystroke
     * buffer, and reallocate the default keystroke buffer so that it
     * has enough room for input. */
310
    key_buffer_len += clean_input_len;
311
312
313
314
315
316
    key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len *
	sizeof(buffer));

    /* If the default keystroke buffer wasn't empty before, move its
     * beginning forward far enough so that we can add input to its
     * beginning. */
317
318
319
    if (key_buffer_len > clean_input_len)
	memmove(key_buffer + clean_input_len, key_buffer,
		(key_buffer_len - clean_input_len) * sizeof(buffer));
320
321

    /* Copy input to the beginning of the default keystroke buffer. */
322
    memcpy(key_buffer, clean_input, clean_input_len * sizeof(buffer));
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
}

/* Put back the character stored in kbinput.  If func_key is TRUE and
 * the character is out of byte range, interpret it as an extended
 * keypad value.  If meta_key is TRUE, put back the Escape character
 * after putting back kbinput. */	
void unget_kbinput(int kbinput, bool meta_key, bool func_key)
{
    buffer input;

    input.key = kbinput;
    input.key_code = (func_key && !is_byte_char(kbinput));

    unget_input(&input, 1);

    if (meta_key) {
	input.key = NANO_CONTROL_3;
	input.key_code = FALSE;
	unget_input(&input, 1);
    }
}

/* Try to read input_len characters from the default keystroke buffer.
 * If the default keystroke buffer is empty and win isn't NULL, try to
 * read in more characters from win and add them to the default
 * keystroke buffer before doing anything else.  If the default
 * keystroke buffer is empty and win is NULL, return NULL. */
buffer *get_input(WINDOW *win, size_t input_len)
{
    buffer *input;

354
#ifndef NANO_SMALL
355
    allow_pending_sigwinch(TRUE);
356
357
358
    allow_pending_sigwinch(FALSE);
#endif

359
360
361
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
388
389
390
391
392
393
394
395
396
397
    if (key_buffer_len == 0) {
	if (win != NULL)
	    get_buffer(win);

	if (key_buffer_len == 0)
	    return NULL;
    }

    /* If input_len is greater than the length of the default keystroke
     * buffer, only read the number of characters in the default
     * keystroke buffer. */
    if (input_len > key_buffer_len)
	input_len = key_buffer_len;

    /* Subtract input_len from the length of the default keystroke
     * buffer, and allocate the keystroke buffer input so that it
     * has enough room for input_len keystrokes. */
    key_buffer_len -= input_len;
    input = (buffer *)nmalloc(input_len * sizeof(buffer));

    /* Copy input_len characters from the beginning of the default
     * keystroke buffer into input. */
    memcpy(input, key_buffer, input_len * sizeof(buffer));

    /* If the default keystroke buffer is empty, mark it as such. */
    if (key_buffer_len == 0) {
	free(key_buffer);
	key_buffer = NULL;
    /* If the default keystroke buffer isn't empty, move its
     * beginning forward far enough back so that the keystrokes in input
     * are no longer at its beginning. */
    } else {
	memmove(key_buffer, key_buffer + input_len, key_buffer_len *
		sizeof(buffer));
	key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len *
		sizeof(buffer));
    }

    return input;
398
399
}

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/* Read in a single character.  If it's ignored, swallow it and go on.
 * Otherwise, try to translate it from ASCII, meta key sequences, escape
 * sequences, and/or extended keypad values.  Set meta_key to TRUE when
 * we get a meta key sequence, and set func_key to TRUE when we get an
 * extended keypad value.  Supported extended keypad values consist of
 * [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace,
 * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown),
 * the function keypad (F1-F16), and the numeric keypad with NumLock
 * off.  Assume nodelay(win) is FALSE. */
int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
{
    int kbinput;

    /* Read in a character and interpret it.  Continue doing this until
     * we get a recognized value or sequence. */
    while ((kbinput = parse_kbinput(win, meta_key, func_key
#ifndef NANO_SMALL
		, FALSE
#endif
		)) == ERR);

    return kbinput;
}

/* Translate ASCII characters, extended keypad values, and escape
 * sequences into their corresponding key values.  Set meta_key to TRUE
 * when we get a meta key sequence, and set func_key to TRUE when we get
 * a function key.  Assume nodelay(win) is FALSE. */
int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key
429
#ifndef NANO_SMALL
430
	, bool reset
431
432
#endif
	)
433

434
{
435
    static int escapes = 0;
436
437
    static int word_digits = 0;
    buffer *kbinput;
438
    int retval = ERR;
439

440
441
    if (reset) {
	escapes = 0;
442
	word_digits = 0;
443
	return ERR;
444
445
    }

446
447
    *meta_key = FALSE;
    *func_key = FALSE;
448

449
450
451
452
    /* Read in a character. */
    while ((kbinput = get_input(win, 1)) == NULL);

    if (kbinput->key_code || is_byte_char(kbinput->key)) {
453
454
	/* If we got an extended keypad value or an ASCII character,
	 * translate it. */
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
	switch (kbinput->key) {
	    case ERR:
		break;
	    case NANO_CONTROL_3:
		/* Increment the escape counter. */
		escapes++;
		switch (escapes) {
		    case 1:
			/* One escape: wait for more input. */
		    case 2:
			/* Two escapes: wait for more input. */
			break;
		    default:
			/* More than two escapes: reset the escape
			 * counter and wait for more input. */
			escapes = 0;
		}
		break;
473
#if !defined(NANO_SMALL) && defined(KEY_RESIZE)
474
475
476
477
478
479
	    /* Since we don't change the default SIGWINCH handler when
	     * NANO_SMALL is defined, KEY_RESIZE is never generated.
	     * Also, Slang and SunOS 5.7-5.9 don't support
	     * KEY_RESIZE. */
	    case KEY_RESIZE:
		break;
480
481
#endif
#ifdef PDCURSES
482
483
484
485
486
487
488
	    case KEY_SHIFT_L:
	    case KEY_SHIFT_R:
	    case KEY_CONTROL_L:
	    case KEY_CONTROL_R:
	    case KEY_ALT_L:
	    case KEY_ALT_R:
		break;
489
#endif
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
	    default:
		switch (escapes) {
		    case 0:
			switch (kbinput->key) {
			    case NANO_CONTROL_8:
				retval = ISSET(REBIND_DELETE) ?
					NANO_DELETE_KEY :
					NANO_BACKSPACE_KEY;
				break;
			    case KEY_DOWN:
				retval = NANO_NEXTLINE_KEY;
				break;
			    case KEY_UP:
				retval = NANO_PREVLINE_KEY;
				break;
			    case KEY_LEFT:
				retval = NANO_BACK_KEY;
				break;
			    case KEY_RIGHT:
				retval = NANO_FORWARD_KEY;
				break;
511
#ifdef KEY_HOME
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
			    /* HP-UX 10 and 11 don't support
			     * KEY_HOME. */
			    case KEY_HOME:
				retval = NANO_HOME_KEY;
				break;
#endif
			    case KEY_BACKSPACE:
				retval = NANO_BACKSPACE_KEY;
				break;
			    case KEY_DC:
				retval = ISSET(REBIND_DELETE) ?
					NANO_BACKSPACE_KEY :
					NANO_DELETE_KEY;
				break;
			    case KEY_IC:
				retval = NANO_INSERTFILE_KEY;
				break;
			    case KEY_NPAGE:
				retval = NANO_NEXTPAGE_KEY;
				break;
			    case KEY_PPAGE:
				retval = NANO_PREVPAGE_KEY;
				break;
			    case KEY_ENTER:
				retval = NANO_ENTER_KEY;
				break;
			    case KEY_A1:	/* Home (7) on numeric
						 * keypad with NumLock
						 * off. */
				retval = NANO_HOME_KEY;
				break;
			    case KEY_A3:	/* PageUp (9) on numeric
						 * keypad with NumLock
						 * off. */
				retval = NANO_PREVPAGE_KEY;
				break;
			    case KEY_B2:	/* Center (5) on numeric
						 * keypad with NumLock
						 * off. */
				break;
			    case KEY_C1:	/* End (1) on numeric
						 * keypad with NumLock
						 * off. */
				retval = NANO_END_KEY;
				break;
			    case KEY_C3:	/* PageDown (4) on
						 * numeric keypad with
						 * NumLock off. */
				retval = NANO_NEXTPAGE_KEY;
				break;
562
#ifdef KEY_BEG
563
564
565
566
567
			    /* Slang doesn't support KEY_BEG. */
			    case KEY_BEG:	/* Center (5) on numeric
						 * keypad with NumLock
						 * off. */
				break;
568
#endif
569
#ifdef KEY_END
570
571
572
573
			    /* HP-UX 10 and 11 don't support KEY_END. */
			    case KEY_END:
				retval = NANO_END_KEY;
				break;
574
575
#endif
#ifdef KEY_SUSPEND
576
577
578
579
			    /* Slang doesn't support KEY_SUSPEND. */
			    case KEY_SUSPEND:
				retval = NANO_SUSPEND_KEY;
				break;
580
581
#endif
#ifdef KEY_SLEFT
582
583
584
585
			    /* Slang doesn't support KEY_SLEFT. */
			    case KEY_SLEFT:
				retval = NANO_BACK_KEY;
				break;
586
587
#endif
#ifdef KEY_SRIGHT
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
			    /* Slang doesn't support KEY_SRIGHT. */
			    case KEY_SRIGHT:
				retval = NANO_FORWARD_KEY;
				break;
#endif
			    default:
				retval = kbinput->key;
				break;
			}
			break;
		    case 1:
			/* One escape followed by a non-escape: escape
			 * sequence mode.  Reset the escape counter.  If
		 	 * there aren't any other keys waiting, we have
			 * a meta key sequence, so set meta_key to TRUE
			 * and save the lowercase version of the
			 * non-escape character as the result.  If there
			 * are other keys waiting, we have a true escape
			 * sequence, so interpret it. */
			escapes = 0;
			if (get_buffer_len() == 0) {
			    *meta_key = TRUE;
			    retval = tolower(kbinput->key);
			} else {
			    buffer *escape_kbinput;
			    int *sequence;
			    size_t seq_len;
			    bool ignore_seq;

			    /* Put back the non-escape character, get
			     * the complete escape sequence, translate
			     * its key values into the corresponding key
			     * value, and save that as the result. */
			    unget_input(kbinput, 1);
			    seq_len = get_buffer_len();
			    escape_kbinput = get_input(NULL, seq_len);
			    sequence = buffer_to_keys(escape_kbinput,
				seq_len);
			    retval = get_escape_seq_kbinput(sequence,
				seq_len, &ignore_seq);

			    /* If the escape sequence is unrecognized
			     * and not ignored, put back all of its
			     * characters except for the initial
			     * escape. */
			    if (retval == ERR && !ignore_seq)
				unget_input(escape_kbinput, seq_len);

			    free(escape_kbinput);
			}
			break;
		    case 2:
640
			/* Two escapes followed by one or more decimal
641
642
643
644
645
			 * digits: word sequence mode.  If the word
			 * sequence's range is limited to 6XXXX (the
			 * first digit is in the '0' to '6' range and
			 * it's the first digit, or it's in the '0' to
			 * '9' range and it's not the first digit),
646
647
648
649
			 * increment the word sequence counter and
			 * interpret the digit.  If the word sequence's
			 * range is not limited to 6XXXX, fall
			 * through. */
650
651
652
653
			if (('0' <= kbinput->key && kbinput->key <= '6'
				&& word_digits == 0) ||
				('0' <= kbinput->key && kbinput->key <= '9'
				&& word_digits > 0)) {
654
655
			    int word_kbinput;

656
			    word_digits++;
657
			    word_kbinput = get_word_kbinput(kbinput->key
658
#ifndef NANO_SMALL
659
				, FALSE
660
#endif
661
662
				);

663
			    if (word_kbinput != ERR) {
664
665
				/* If we've read in a complete word
				 * sequence, reset the word sequence
666
667
668
				 * counter and the escape counter,
				 * and put back the corresponding word
				 * value. */
669
				word_digits = 0;
670
				escapes = 0;
671
672
				unget_kbinput(word_kbinput, FALSE,
					FALSE);
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
			    }
			} else {
			    /* Reset the escape counter. */
			    escapes = 0;
			    if (word_digits == 0)
				/* Two escapes followed by a non-decimal
				 * digit or a decimal digit that would
				 * create a word sequence greater than
				 * 6XXXX, and we're not in the middle of
				 * a word sequence: control character
				 * sequence mode.  Interpret the control
				 * sequence and save the corresponding
				 * control character as the result. */
				retval = get_control_kbinput(kbinput->key);
			    else {
				/* If we're in the middle of a word
				 * sequence, reset the word sequence
				 * counter and save the character we got
				 * as the result. */
				word_digits = 0;
				retval = kbinput->key;
694
			    }
695
			}
696
697
698
			break;
		}
	}
699

700
701
702
703
704
705
706
707
	/* If we have a result and it's an extended keypad value, set
	 * func_key to TRUE. */
	if (retval != ERR)
	    *func_key = !is_byte_char(retval);
    } else
	/* If we didn't get an extended keypad value or an ASCII
	 * character, leave it as-is. */
	retval = kbinput->key;
708
709

#ifdef DEBUG
710
    fprintf(stderr, "parse_kbinput(): kbinput->key = %d, meta_key = %d, func_key = %d, escapes = %d, word_digits = %d, retval = %d\n", kbinput->key, (int)*meta_key, (int)*func_key, escapes, word_digits, retval);
711
712
#endif

713
    /* Return the result. */
714
715
716
    return retval;
}

717
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
718
 * keypad values, into their corresponding key values.  These sequences
719
720
721
722
723
 * are generated when the keypad doesn't support the needed keys.  If
 * the escape sequence is recognized but we want to ignore it, return
 * ERR and set ignore_seq to TRUE; if it's unrecognized, return ERR and
 * set ignore_seq to FALSE.  Assume that Escape has already been read
 * in. */
724
int get_escape_seq_kbinput(const int *sequence, size_t seq_len, bool
725
	*ignore_seq)
726
{
727
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
728

729
730
    *ignore_seq = FALSE;

731
732
    if (seq_len > 1) {
	switch (sequence[0]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
733
	    case 'O':
734
		switch (sequence[1]) {
735
		    case '2':
736
737
			if (seq_len >= 3) {
			    switch (sequence[2]) {
738
739
				case 'P': /* Esc O 2 P == F13 on
					   * xterm. */
740
				    retval = KEY_F(13);
741
742
743
				    break;
				case 'Q': /* Esc O 2 Q == F14 on
					   * xterm. */
744
				    retval = KEY_F(14);
745
				    break;
746
747
748
749
750
751
752
753
				case 'R': /* Esc O 2 R == F15 on
					   * xterm. */
				    retval = KEY_F(15);
				    break;
				case 'S': /* Esc O 2 S == F16 on
					   * xterm. */
				    retval = KEY_F(16);
				    break;
754
755
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
756
			break;
757
758
759
760
761
762
763
		    case 'A': /* Esc O A == Up on VT100/VT320/xterm. */
		    case 'B': /* Esc O B == Down on
			       * VT100/VT320/xterm. */
		    case 'C': /* Esc O C == Right on
			       * VT100/VT320/xterm. */
		    case 'D': /* Esc O D == Left on
			       * VT100/VT320/xterm. */
764
			retval = get_escape_seq_abcd(sequence[1]);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
765
			break;
766
767
		    case 'E': /* Esc O E == Center (5) on numeric keypad
			       * with NumLock off on xterm. */
768
			*ignore_seq = TRUE;
769
			break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
770
		    case 'F': /* Esc O F == End on xterm. */
771
			retval = NANO_END_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
772
773
			break;
		    case 'H': /* Esc O H == Home on xterm. */
774
			retval = NANO_HOME_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
775
			break;
776
		    case 'M': /* Esc O M == Enter on numeric keypad with
777
778
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * Eterm. */
779
			retval = NANO_ENTER_KEY;
780
			break;
781
		    case 'P': /* Esc O P == F1 on VT100/VT220/VT320/Mach
782
			       * console. */
783
			retval = KEY_F(1);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
784
			break;
785
		    case 'Q': /* Esc O Q == F2 on VT100/VT220/VT320/Mach
786
			       * console. */
787
			retval = KEY_F(2);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
788
			break;
789
		    case 'R': /* Esc O R == F3 on VT100/VT220/VT320/Mach
790
			       * console. */
791
			retval = KEY_F(3);
792
			break;
793
		    case 'S': /* Esc O S == F4 on VT100/VT220/VT320/Mach
794
			       * console. */
795
			retval = KEY_F(4);
796
			break;
797
		    case 'T': /* Esc O T == F5 on Mach console. */
798
			retval = KEY_F(5);
799
			break;
800
		    case 'U': /* Esc O U == F6 on Mach console. */
801
			retval = KEY_F(6);
802
			break;
803
		    case 'V': /* Esc O V == F7 on Mach console. */
804
			retval = KEY_F(7);
805
			break;
806
		    case 'W': /* Esc O W == F8 on Mach console. */
807
			retval = KEY_F(8);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
808
			break;
809
		    case 'X': /* Esc O X == F9 on Mach console. */
810
			retval = KEY_F(9);
811
			break;
812
		    case 'Y': /* Esc O Y == F10 on Mach console. */
813
			retval = KEY_F(10);
814
815
816
817
			break;
		    case 'a': /* Esc O a == Ctrl-Up on rxvt. */
		    case 'b': /* Esc O b == Ctrl-Down on rxvt. */
		    case 'c': /* Esc O c == Ctrl-Right on rxvt. */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
818
		    case 'd': /* Esc O d == Ctrl-Left on rxvt. */
819
			retval = get_escape_seq_abcd(sequence[1]);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
820
			break;
821
		    case 'j': /* Esc O j == '*' on numeric keypad with
822
823
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * rxvt. */
824
			retval = '*';
825
826
			break;
		    case 'k': /* Esc O k == '+' on numeric keypad with
827
828
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * rxvt. */
829
			retval = '+';
830
831
			break;
		    case 'l': /* Esc O l == ',' on numeric keypad with
832
833
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * rxvt. */
834
			retval = '+';
835
836
			break;
		    case 'm': /* Esc O m == '-' on numeric keypad with
837
838
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * rxvt. */
839
			retval = '-';
840
841
			break;
		    case 'n': /* Esc O n == Delete (.) on numeric keypad
842
843
			       * with NumLock off on VT100/VT220/VT320/
			       * xterm/rxvt. */
844
			retval = NANO_DELETE_KEY;
845
846
			break;
		    case 'o': /* Esc O o == '/' on numeric keypad with
847
848
			       * NumLock off on VT100/VT220/VT320/xterm/
			       * rxvt. */
849
			retval = '/';
850
851
			break;
		    case 'p': /* Esc O p == Insert (0) on numeric keypad
852
853
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
854
			retval = NANO_INSERTFILE_KEY;
855
856
			break;
		    case 'q': /* Esc O q == End (1) on numeric keypad
857
858
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
859
			retval = NANO_END_KEY;
860
861
			break;
		    case 'r': /* Esc O r == Down (2) on numeric keypad
862
863
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
864
			retval = NANO_NEXTLINE_KEY;
865
866
			break;
		    case 's': /* Esc O s == PageDown (3) on numeric
867
868
			       * keypad with NumLock off on VT100/VT220/
			       * VT320/rxvt. */
869
			retval = NANO_NEXTPAGE_KEY;
870
871
			break;
		    case 't': /* Esc O t == Left (4) on numeric keypad
872
873
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
874
			retval = NANO_BACK_KEY;
875
876
			break;
		    case 'u': /* Esc O u == Center (5) on numeric keypad
877
878
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt/Eterm. */
879
			*ignore_seq = TRUE;
880
881
			break;
		    case 'v': /* Esc O v == Right (6) on numeric keypad
882
883
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
884
			retval = NANO_FORWARD_KEY;
885
886
			break;
		    case 'w': /* Esc O w == Home (7) on numeric keypad
887
888
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
889
			retval = NANO_HOME_KEY;
890
891
			break;
		    case 'x': /* Esc O x == Up (8) on numeric keypad
892
893
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
894
			retval = NANO_PREVLINE_KEY;
895
896
			break;
		    case 'y': /* Esc O y == PageUp (9) on numeric keypad
897
898
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt. */
899
			retval = NANO_PREVPAGE_KEY;
900
			break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
901
902
903
		}
		break;
	    case 'o':
904
		switch (sequence[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
905
906
907
908
		    case 'a': /* Esc o a == Ctrl-Up on Eterm. */
		    case 'b': /* Esc o b == Ctrl-Down on Eterm. */
		    case 'c': /* Esc o c == Ctrl-Right on Eterm. */
		    case 'd': /* Esc o d == Ctrl-Left on Eterm. */
909
			retval = get_escape_seq_abcd(sequence[1]);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
910
911
912
913
			break;
		}
		break;
	    case '[':
914
		switch (sequence[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
915
		    case '1':
916
917
			if (seq_len >= 3) {
			    switch (sequence[2]) {
918
919
				case '1': /* Esc [ 1 1 ~ == F1 on rxvt/
					   * Eterm. */
920
				    retval = KEY_F(1);
921
				    break;
922
923
				case '2': /* Esc [ 1 2 ~ == F2 on rxvt/
					   * Eterm. */
924
				    retval = KEY_F(2);
925
				    break;
926
927
				case '3': /* Esc [ 1 3 ~ == F3 on rxvt/
					   * Eterm. */
928
				    retval = KEY_F(3);
929
				    break;
930
931
				case '4': /* Esc [ 1 4 ~ == F4 on rxvt/
					   * Eterm. */
932
				    retval = KEY_F(4);
933
				    break;
934
935
				case '5': /* Esc [ 1 5 ~ == F5 on xterm/
					   * rxvt/Eterm. */
936
				    retval = KEY_F(5);
937
				    break;
938
				case '7': /* Esc [ 1 7 ~ == F6 on
939
940
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
941
				    retval = KEY_F(6);
942
				    break;
943
				case '8': /* Esc [ 1 8 ~ == F7 on
944
945
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
946
				    retval = KEY_F(7);
947
				    break;
948
				case '9': /* Esc [ 1 9 ~ == F8 on
949
950
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
951
				    retval = KEY_F(8);
952
				    break;
953
				case ';':
954
955
    if (seq_len >= 4) {
	switch (sequence[3]) {
956
	    case '2':
957
958
		if (seq_len >= 5) {
		    switch (sequence[4]) {
959
960
961
962
963
964
965
966
			case 'A': /* Esc [ 1 ; 2 A == Shift-Up on
				   * xterm. */
			case 'B': /* Esc [ 1 ; 2 B == Shift-Down on
				   * xterm. */
			case 'C': /* Esc [ 1 ; 2 C == Shift-Right on
				   * xterm. */
			case 'D': /* Esc [ 1 ; 2 D == Shift-Left on
				   * xterm. */
967
			    retval = get_escape_seq_abcd(sequence[4]);
968
969
970
971
972
			    break;
		    }
		}
		break;
	    case '5':
973
974
		if (seq_len >= 5) {
		    switch (sequence[4]) {
975
976
977
978
979
980
981
982
			case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on
				   * xterm. */
			case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on
				   * xterm. */
			case 'C': /* Esc [ 1 ; 5 C == Ctrl-Right on
				   * xterm. */
			case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on
				   * xterm. */
983
			    retval = get_escape_seq_abcd(sequence[4]);
984
985
986
987
988
989
990
			    break;
		    }
		}
		break;
	}
    }
				    break;
991
992
				default: /* Esc [ 1 ~ == Home on
					  * VT320/Linux console. */
993
				    retval = NANO_HOME_KEY;
994
995
996
997
998
				    break;
			    }
			}
			break;
		    case '2':
999
1000
			if (seq_len >= 3) {
			    switch (sequence[2]) {
1001
				case '0': /* Esc [ 2 0 ~ == F9 on
1002
1003
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
1004
				    retval = KEY_F(9);
1005
				    break;
1006
				case '1': /* Esc [ 2 1 ~ == F10 on
1007
1008
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
1009
				    retval = KEY_F(10);
1010
				    break;
1011
				case '3': /* Esc [ 2 3 ~ == F11 on
1012
1013
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
1014
				    retval = KEY_F(11);
1015
				    break;
1016
				case '4': /* Esc [ 2 4 ~ == F12 on
1017
1018
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
1019
				    retval = KEY_F(12);
1020
				    break;
1021
				case '5': /* Esc [ 2 5 ~ == F13 on
1022
1023
					   * VT220/VT320/Linux console/
					   * rxvt/Eterm. */
1024
				    retval = KEY_F(13);
1025
				    break;
1026
				case '6': /* Esc [ 2 6 ~ == F14 on
1027
1028
					   * VT220/VT320/Linux console/
					   * rxvt/Eterm. */
1029
				    retval = KEY_F(14);
1030
				    break;
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
				case '8': /* Esc [ 2 8 ~ == F15 on
					   * VT220/VT320/Linux console/
					   * rxvt/Eterm. */
				    retval = KEY_F(15);
				    break;
				case '9': /* Esc [ 2 9 ~ == F16 on
					   * VT220/VT320/Linux console/
					   * rxvt/Eterm. */
				    retval = KEY_F(16);
				    break;
1041
				default: /* Esc [ 2 ~ == Insert on
1042
1043
					  * VT220/VT320/Linux console/
					  * xterm. */
1044
				    retval = NANO_INSERTFILE_KEY;
1045
1046
				    break;
			    }
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1047
1048
			}
			break;
1049
1050
		    case '3': /* Esc [ 3 ~ == Delete on VT220/VT320/
			       * Linux console/xterm. */
1051
			retval = NANO_DELETE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1052
			break;
1053
		    case '4': /* Esc [ 4 ~ == End on VT220/VT320/Linux
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1054
			       * console/xterm. */
1055
			retval = NANO_END_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1056
			break;
1057
1058
1059
		    case '5': /* Esc [ 5 ~ == PageUp on VT220/VT320/
			       * Linux console/xterm; Esc [ 5 ^ ==
			       * PageUp on Eterm. */
1060
			retval = NANO_PREVPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1061
			break;
1062
1063
1064
		    case '6': /* Esc [ 6 ~ == PageDown on VT220/VT320/
			       * Linux console/xterm; Esc [ 6 ^ ==
			       * PageDown on Eterm. */
1065
			retval = NANO_NEXTPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1066
1067
			break;
		    case '7': /* Esc [ 7 ~ == Home on rxvt. */
1068
			retval = NANO_HOME_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1069
1070
			break;
		    case '8': /* Esc [ 8 ~ == End on rxvt. */
1071
			retval = NANO_END_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1072
			break;
1073
		    case '9': /* Esc [ 9 == Delete on Mach console. */
1074
			retval = NANO_DELETE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1075
			break;
1076
		    case '@': /* Esc [ @ == Insert on Mach console. */
1077
			retval = NANO_INSERTFILE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1078
			break;
1079
		    case 'A': /* Esc [ A == Up on ANSI/VT220/Linux
1080
			       * console/FreeBSD console/Mach console/
1081
			       * rxvt/Eterm. */
1082
		    case 'B': /* Esc [ B == Down on ANSI/VT220/Linux
1083
			       * console/FreeBSD console/Mach console/
1084
			       * rxvt/Eterm. */
1085
		    case 'C': /* Esc [ C == Right on ANSI/VT220/Linux
1086
			       * console/FreeBSD console/Mach console/
1087
			       * rxvt/Eterm. */
1088
		    case 'D': /* Esc [ D == Left on ANSI/VT220/Linux
1089
			       * console/FreeBSD console/Mach console/
1090
			       * rxvt/Eterm. */
1091
			retval = get_escape_seq_abcd(sequence[1]);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1092
			break;
1093
1094
		    case 'E': /* Esc [ E == Center (5) on numeric keypad
			       * with NumLock off on FreeBSD console. */
1095
			*ignore_seq = TRUE;
1096
			break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1097
1098
		    case 'F': /* Esc [ F == End on FreeBSD
			       * console/Eterm. */
1099
			retval = NANO_END_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1100
1101
			break;
		    case 'G': /* Esc [ G == PageDown on FreeBSD
1102
			       * console. */
1103
			retval = NANO_NEXTPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1104
			break;
1105
		    case 'H': /* Esc [ H == Home on ANSI/VT220/FreeBSD
1106
			       * console/Mach console/Eterm. */
1107
			retval = NANO_HOME_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1108
1109
1110
			break;
		    case 'I': /* Esc [ I == PageUp on FreeBSD
			       * console. */
1111
			retval = NANO_PREVPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1112
			break;
1113
		    case 'L': /* Esc [ L == Insert on ANSI/FreeBSD
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1114
			       * console. */
1115
			retval = NANO_INSERTFILE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1116
			break;
1117
		    case 'M': /* Esc [ M == F1 on FreeBSD console. */
1118
			retval = KEY_F(1);
1119
1120
			break;
		    case 'N': /* Esc [ N == F2 on FreeBSD console. */
1121
			retval = KEY_F(2);
1122
1123
			break;
		    case 'O':
1124
1125
			if (seq_len >= 3) {
			    switch (sequence[2]) {
1126
1127
				case 'P': /* Esc [ O P == F1 on
					   * xterm. */
1128
				    retval = KEY_F(1);
1129
1130
1131
				    break;
				case 'Q': /* Esc [ O Q == F2 on
					   * xterm. */
1132
				    retval = KEY_F(2);
1133
1134
1135
				    break;
				case 'R': /* Esc [ O R == F3 on
					   * xterm. */
1136
				    retval = KEY_F(3);
1137
1138
1139
				    break;
				case 'S': /* Esc [ O S == F4 on
					   * xterm. */
1140
				    retval = KEY_F(4);
1141
1142
				    break;
			    }
1143
1144
1145
			} else {
			    /* Esc [ O == F3 on FreeBSD console. */
			    retval = KEY_F(3);
1146
1147
1148
			}
			break;
		    case 'P': /* Esc [ P == F4 on FreeBSD console. */
1149
			retval = KEY_F(4);
1150
1151
			break;
		    case 'Q': /* Esc [ Q == F5 on FreeBSD console. */
1152
			retval = KEY_F(5);
1153
1154
			break;
		    case 'R': /* Esc [ R == F6 on FreeBSD console. */
1155
			retval = KEY_F(6);
1156
1157
			break;
		    case 'S': /* Esc [ S == F7 on FreeBSD console. */
1158
			retval = KEY_F(7);
1159
1160
			break;
		    case 'T': /* Esc [ T == F8 on FreeBSD console. */
1161
			retval = KEY_F(8);
1162
			break;
1163
		    case 'U': /* Esc [ U == PageDown on Mach console. */
1164
			retval = NANO_NEXTPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1165
			break;
1166
		    case 'V': /* Esc [ V == PageUp on Mach console. */
1167
			retval = NANO_PREVPAGE_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1168
			break;
1169
		    case 'W': /* Esc [ W == F11 on FreeBSD console. */
1170
			retval = KEY_F(11);
1171
1172
			break;
		    case 'X': /* Esc [ X == F12 on FreeBSD console. */
1173
			retval = KEY_F(12);
1174
			break;
1175
		    case 'Y': /* Esc [ Y == End on Mach console. */
1176
			retval = NANO_END_KEY;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1177
			break;
1178
		    case 'Z': /* Esc [ Z == F14 on FreeBSD console. */
1179
			retval = KEY_F(14);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1180
			break;
1181
		    case 'a': /* Esc [ a == Shift-Up on rxvt/Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1182
		    case 'b': /* Esc [ b == Shift-Down on rxvt/Eterm. */
1183
1184
		    case 'c': /* Esc [ c == Shift-Right on rxvt/
			       * Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1185
		    case 'd': /* Esc [ d == Shift-Left on rxvt/Eterm. */
1186
			retval = get_escape_seq_abcd(sequence[1]);
1187
1188
			break;
		    case '[':
1189
1190
			if (seq_len >= 3) {
			    switch (sequence[2]) {
1191
1192
				case 'A': /* Esc [ [ A == F1 on Linux
					   * console. */
1193
				    retval = KEY_F(1);
1194
1195
1196
				    break;
				case 'B': /* Esc [ [ B == F2 on Linux
					   * console. */
1197
				    retval = KEY_F(2);
1198
1199
1200
				    break;
				case 'C': /* Esc [ [ C == F3 on Linux
					   * console. */
1201
				    retval = KEY_F(3);
1202
1203
1204
				    break;
				case 'D': /* Esc [ [ D == F4 on Linux
					   * console. */
1205
				    retval = KEY_F(4);
1206
1207
1208
				    break;
				case 'E': /* Esc [ [ E == F5 on Linux
					   * console. */
1209
				    retval = KEY_F(5);
1210
1211
1212
				    break;
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1213
1214
1215
1216
			break;
		}
		break;
	}
1217
1218
    }

1219
#ifdef DEBUG
1220
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d, ignore_seq = %d\n", retval, (int)*ignore_seq);
1221
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1222

1223
    return retval;
1224
1225
}

1226
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1227
 * letters A (up), B (down), C (right), and D (left).  These are common
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
 * to many escape sequences. */
int get_escape_seq_abcd(int kbinput)
{
    switch (tolower(kbinput)) {
	case 'a':
	    return NANO_PREVLINE_KEY;
	case 'b':
	    return NANO_NEXTLINE_KEY;
	case 'c':
	    return NANO_FORWARD_KEY;
	case 'd':
	    return NANO_BACK_KEY;
	default:
	    return ERR;
    }
}

1245
1246
1247
1248
1249
1250
1251
/* Translate a word sequence: turn a three-digit decimal number from
 * 000 to 255 into its corresponding word value. */
int get_word_kbinput(int kbinput
#ifndef NANO_SMALL
	, bool reset
#endif
	)
1252
{
1253
1254
1255
    static int word_digits = 0;
    static int word_kbinput = 0;
    int retval = ERR;
1256
1257

#ifndef NANO_SMALL
1258
1259
1260
1261
1262
    if (reset) {
	word_digits = 0;
	word_kbinput = 0;
	return ERR;
    }
1263
1264
#endif

1265
1266
    /* Increment the word digit counter. */
    word_digits++;
1267

1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
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
    switch (word_digits) {
	case 1:
	    /* One digit: reset the word sequence holder and add the
	     * digit we got to the 10000's position of the word sequence
	     * holder. */
	    word_kbinput = 0;
	    if ('0' <= kbinput && kbinput <= '6')
		word_kbinput += (kbinput - '0') * 10000;
	    else
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 2:
	    /* Two digits: add the digit we got to the 1000's position
	     * of the word sequence holder. */
	    if (('0' <= kbinput && kbinput <= '5') ||
		(word_kbinput < 60000 && '6' <= kbinput &&
		kbinput <= '9'))
		word_kbinput += (kbinput - '0') * 1000;
	    else
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 3:
	    /* Three digits: add the digit we got to the 100's position
	     * of the word sequence holder. */
	    if (('0' <= kbinput && kbinput <= '5') ||
		(word_kbinput < 65000 && '6' <= kbinput &&
		kbinput <= '9'))
		word_kbinput += (kbinput - '0') * 100;
	    else
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 4:
	    /* Four digits: add the digit we got to the 10's position of
	     * the word sequence holder. */
	    if (('0' <= kbinput && kbinput <= '3') ||
		(word_kbinput < 65500 && '4' <= kbinput &&
		kbinput <= '9'))
		word_kbinput += (kbinput - '0') * 10;
	    else
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 5:
	    /* Five digits: add the digit we got to the 1's position of
	     * the word sequence holder, and save the corresponding word
	     * value as the result. */
	    if (('0' <= kbinput && kbinput <= '5') ||
		(word_kbinput < 65530 && '6' <= kbinput &&
		kbinput <= '9')) {
		word_kbinput += (kbinput - '0');
		retval = word_kbinput;
	    } else
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	default:
	    /* More than three digits: save the character we got as the
	     * result. */
	    retval = kbinput;
	    break;
    }
1342

1343
1344
1345
1346
1347
1348
    /* If we have a result, reset the word digit counter and the word
     * sequence holder. */
    if (retval != ERR) {
	word_digits = 0;
	word_kbinput = 0;
    }
1349

1350
#ifdef DEBUG
1351
    fprintf(stderr, "get_word_kbinput(): kbinput = %d, word_digits = %d, word_kbinput = %d, retval = %d\n", kbinput, word_digits, word_kbinput, retval);
1352
1353
#endif

1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
    return retval;
}

/* Translate a control character sequence: turn an ASCII non-control
 * character into its corresponding control character. */
int get_control_kbinput(int kbinput)
{
    int retval;

     /* Ctrl-2 (Ctrl-Space, Ctrl-@, Ctrl-`) */
    if (kbinput == '2' || kbinput == ' ' || kbinput == '@' ||
	kbinput == '`')
	retval = NANO_CONTROL_SPACE;
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
    /* Ctrl-A to Ctrl-_ */
    else if ('A' <= kbinput && kbinput <= '_')
	retval = kbinput - 64;
    /* Ctrl-a to Ctrl-~ */
    else if ('a' <= kbinput && kbinput <= '~')
	retval = kbinput - 96;
    else
	retval = kbinput;

1382
#ifdef DEBUG
1383
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1384
1385
#endif

1386
1387
    return retval;
}
1388

1389
1390
1391
1392
1393
/* Read in a string of characters verbatim, and return the length of the
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1394

1395
1396
1397
1398
1399
1400
1401
1402
1403
    /* Turn off flow control characters if necessary so that we can type
     * them in verbatim, and turn the keypad off so that we don't get
     * extended keypad values. */
    if (ISSET(PRESERVE))
	disable_flow_control();
    keypad(win, FALSE);

    /* Read in a stream of characters and interpret it if possible. */
    retval = parse_verbatim_kbinput(win, kbinput_len);
1404
1405
1406
1407
1408
1409
1410

    /* Turn flow control characters back on if necessary and turn the
     * keypad back on now that we're done. */
    if (ISSET(PRESERVE))
	enable_flow_control();
    keypad(win, TRUE);

1411
    return retval;
1412
1413
}

1414
1415
1416
1417
/* Read in a stream of all available characters.  Translate the first
 * few characters of the input into the corresponding word value if
 * possible.  After that, leave the input as-is. */ 
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1418
{
1419
1420
    buffer *kbinput, *sequence;
    int word, *retval;
1421

1422
1423
1424
1425
1426
    /* Read in the first keystroke. */
    while ((kbinput = get_input(win, 1)) == NULL);

    /* Check whether the first keystroke is a decimal digit. */
    word = get_word_kbinput(kbinput->key
1427
#ifndef NANO_SMALL
1428
	, FALSE
1429
#endif
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
	);

    /* If the first keystroke isn't a decimal digit, put back the first
     * keystroke. */
    if (word != ERR)
	unget_input(kbinput, 1);
    /* Otherwise, read in keystrokes until we have a complete word
     * sequence, and put back the corresponding word value. */
    else {
	buffer word_kbinput;
1440

1441
1442
1443
	while (word == ERR) {
	    while ((kbinput = get_input(win, 1)) == NULL);
	    word = get_word_kbinput(kbinput->key
1444
#ifndef NANO_SMALL
1445
		, FALSE
1446
#endif
1447
1448
		);
	}
1449

1450
1451
	word_kbinput.key = word;
	word_kbinput.key_code = FALSE;
1452

1453
1454
1455
1456
1457
1458
1459
1460
1461
	unget_input(&word_kbinput, 1);
    }

    /* Get the complete sequence, and save the key values in it as the
     * result. */
    *kbinput_len = get_buffer_len();
    sequence = get_input(NULL, *kbinput_len);
    retval = buffer_to_keys(sequence, *kbinput_len);
    free(sequence);
1462
1463
1464
1465

    return retval;
}

1466
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1467
1468
/* Check for a mouse event, and if one's taken place, save the
 * coordinates where it took place in mouse_x and mouse_y.  After that,
1469
1470
 * assuming allow_shortcuts is FALSE, if the shortcut list on the
 * bottom two lines of the screen is visible and the mouse event took
1471
1472
1473
 * place on it, figure out which shortcut was clicked and put back the
 * equivalent keystroke(s).  Return FALSE if no keystrokes were
 * put back, or TRUE if at least one was.  Assume that KEY_MOUSE has
1474
 * already been read in. */
1475
bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1476
1477
1478
1479
1480
1481
1482
1483
{
    MEVENT mevent;

    *mouse_x = -1;
    *mouse_y = -1;

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1484
	return FALSE;
1485
1486
1487
1488
1489

    /* Save the screen coordinates where the mouse event took place. */
    *mouse_x = mevent.x;
    *mouse_y = mevent.y;

1490
1491
1492
    /* If we're allowing shortcuts, the current shortcut list is being
     * displayed on the last two lines of the screen, and the mouse
     * event took place inside it, we need to figure out which shortcut
1493
     * was clicked and put back the equivalent keystroke(s) for it. */
1494
1495
    if (allow_shortcuts && !ISSET(NO_HELP) && wenclose(bottomwin,
	*mouse_y, *mouse_x)) {
1496
	int i, j;
1497
	size_t currslen;
1498
1499
1500
1501
1502
1503
1504
1505
	    /* The number of shortcuts in the current shortcut list. */
	const shortcut *s = currshortcut;
	    /* The actual shortcut we clicked on, starting at the first
	     * one in the current shortcut list. */

	/* Get the shortcut lists' length. */
	if (currshortcut == main_list)
	    currslen = MAIN_VISIBLE;
1506
	else {
1507
1508
	    currslen = length_of_list(currshortcut);

1509
1510
1511
1512
1513
1514
	    /* We don't show any more shortcuts than the main list
	     * does. */
	    if (currslen > MAIN_VISIBLE)
		currslen = MAIN_VISIBLE;
	}

1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
	/* Calculate the width of each shortcut in the list (it's the
	 * same for all of them). */
	if (currslen < 2)
	    i = COLS / 6;
	else
	    i = COLS / ((currslen / 2) + (currslen % 2));

	/* Calculate the y-coordinates relative to the beginning of
	 * bottomwin, i.e, the bottom three lines of the screen. */
	j = *mouse_y - (editwinrows + 3);

	/* If we're on the statusbar, beyond the end of the shortcut
	 * list, or beyond the end of a shortcut on the right side of
	 * the screen, don't do anything. */
	if (j < 0 || (*mouse_x / i) >= currslen)
1530
	    return FALSE;
1531
1532
	j = (*mouse_x / i) * 2 + j;
	if (j >= currslen)
1533
	    return FALSE;
1534
1535
1536
1537
1538
1539

	/* Go through the shortcut list to determine which shortcut was
	 * clicked. */
	for (; j > 0; j--)
	    s = s->next;

1540
1541
1542
	/* And put back the equivalent key.  Assume that each shortcut
	 * has, at the very least, an equivalent control key, an
	 * equivalent primary meta key sequence, or both. */
1543
	if (s->ctrlval != NANO_NO_KEY)
1544
	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1545
	else if (s->metaval != NANO_NO_KEY)
1546
	    unget_kbinput(s->metaval, TRUE, FALSE);
1547

1548
	return TRUE;
1549
    }
1550
    return FALSE;
1551
}
1552
1553
#endif /* !DISABLE_MOUSE */

1554
const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool
1555
	*meta_key, bool *func_key)
1556
1557
1558
1559
{
    const shortcut *s = s_list;
    size_t slen = length_of_list(s_list);

1560
#ifdef DEBUG
1561
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %d, func_key = %d\n", *kbinput, (int)*meta_key, (int)*func_key);
1562
1563
#endif

1564
1565
1566
1567
1568
1569
    /* Check for shortcuts. */
    for (; slen > 0; slen--) {
	/* We've found a shortcut if:
	 *
	 * 1. The key exists.
	 * 2. The key is a control key in the shortcut list.
1570
1571
1572
1573
1574
	 * 3. meta_key is TRUE and the key is the primary or
	 *    miscellaneous meta sequence in the shortcut list.
	 * 4. func_key is TRUE and the key is a function key in the
	 *    shortcut list. */

1575
1576
1577
1578
	if (*kbinput != NANO_NO_KEY && (*kbinput == s->ctrlval ||
		(*meta_key == TRUE && (*kbinput == s->metaval ||
		*kbinput == s->miscval)) || (*func_key == TRUE &&
		*kbinput == s->funcval))) {
1579
1580
1581
1582
1583
1584
1585
1586
	    break;
	}

	s = s->next;
    }

    /* Translate the shortcut to either its control key or its meta key
     * equivalent.  Assume that the shortcut has an equivalent control
1587
     * key, an equivalent primary meta key sequence, or both. */
1588
1589
1590
    if (slen > 0) {
	if (s->ctrlval != NANO_NO_KEY) {
	    *meta_key = FALSE;
1591
	    *func_key = FALSE;
1592
	    *kbinput = s->ctrlval;
1593
1594
	} else if (s->metaval != NANO_NO_KEY) {
	    *meta_key = TRUE;
1595
	    *func_key = FALSE;
1596
	    *kbinput = s->metaval;
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
	}
	return s;
    }

    return NULL;
}

#ifndef NANO_SMALL
const toggle *get_toggle(int kbinput, bool meta_key)
{
    const toggle *t = toggles;

1609
1610
1611
1612
#ifdef DEBUG
    fprintf(stderr, "get_toggle(): kbinput = %d, meta_key = %d\n", kbinput, (int)meta_key);
#endif

1613
1614
1615
    /* Check for toggles. */
    for (; t != NULL; t = t->next) {
	/* We've found a toggle if meta_key is TRUE and the key is in
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1616
	 * the meta key toggle list. */
1617
1618
1619
1620
1621
1622
1623
1624
	if (meta_key && kbinput == t->val)
	    break;
    }

    return t;
}
#endif /* !NANO_SMALL */

Chris Allegretta's avatar
Chris Allegretta committed
1625
1626
1627
1628
/* Return the placewewant associated with current_x.  That is, xplustabs
 * is the zero-based column position of the cursor.  Value is no smaller
 * than current_x. */
size_t xplustabs(void)
Chris Allegretta's avatar
Chris Allegretta committed
1629
{
Chris Allegretta's avatar
Chris Allegretta committed
1630
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
1631
1632
}

1633
1634
1635
1636
/* actual_x() gives the index in str of the character displayed at
 * column xplus.  That is, actual_x() is the largest value such that
 * strnlenpt(str, actual_x(str, xplus)) <= xplus. */
size_t actual_x(const char *str, size_t xplus)
Chris Allegretta's avatar
Chris Allegretta committed
1637
{
Chris Allegretta's avatar
Chris Allegretta committed
1638
    size_t i = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1639
	/* The position in str, returned. */
Chris Allegretta's avatar
Chris Allegretta committed
1640
    size_t length = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1641
	/* The screen display width to str[i]. */
Chris Allegretta's avatar
Chris Allegretta committed
1642

1643
    assert(str != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1644

1645
1646
    for (; length < xplus && *str != '\0'; i++, str++) {
	if (*str == '\t')
1647
	    length += tabsize - (length % tabsize);
1648
	else if (is_cntrl_char(*str))
Chris Allegretta's avatar
Chris Allegretta committed
1649
1650
1651
1652
	    length += 2;
	else
	    length++;
    }
1653
1654
    assert(length == strnlenpt(str - i, i));
    assert(i <= strlen(str - i));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1655

Chris Allegretta's avatar
Chris Allegretta committed
1656
1657
    if (length > xplus)
	i--;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1658

Chris Allegretta's avatar
Chris Allegretta committed
1659
    return i;
1660
1661
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1662
/* A strlen() with tabs factored in, similar to xplustabs().  How many
1663
 * columns wide are the first size characters of buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1664
size_t strnlenpt(const char *buf, size_t size)
1665
{
Chris Allegretta's avatar
Chris Allegretta committed
1666
1667
    size_t length = 0;

1668
1669
1670
1671
    assert(buf != NULL);
    for (; *buf != '\0' && size != 0; size--, buf++) {
	if (*buf == '\t')
	    length += tabsize - (length % tabsize);
1672
	else if (is_cntrl_char(*buf))
1673
1674
1675
1676
	    length += 2;
	else
	    length++;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1677
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
1678
1679
}

1680
/* How many columns wide is buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1681
size_t strlenpt(const char *buf)
1682
{
1683
    return strnlenpt(buf, (size_t)-1);
1684
1685
}

1686
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1687
{
1688
    mvwaddstr(topwin, 0, 0, hblank);
1689
1690
}

Chris Allegretta's avatar
Chris Allegretta committed
1691
1692
void blank_edit(void)
{
1693
    int i;
1694
    for (i = 0; i < editwinrows; i++)
Chris Allegretta's avatar
Chris Allegretta committed
1695
1696
1697
1698
1699
1700
1701
1702
	mvwaddstr(edit, i, 0, hblank);
}

void blank_statusbar(void)
{
    mvwaddstr(bottomwin, 0, 0, hblank);
}

1703
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1704
{
1705
1706
1707
1708
    if (statusblank > 1)
	statusblank--;
    else if (statusblank == 1 && !ISSET(CONSTUPDATE)) {
	statusblank = 0;
1709
1710
1711
1712
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
	wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1713
1714
1715
    }
}

1716
1717
1718
1719
1720
1721
1722
1723
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
}

1724
1725
1726
1727
1728
/* Convert buf into a string that can be displayed on screen.  The
 * caller wants to display buf starting with column start_col, and
 * extending for at most len columns.  start_col is zero-based.  len is
 * one-based, so len == 0 means you get "" returned.  The returned
 * string is dynamically allocated, and should be freed. */
1729
char *display_string(const char *buf, size_t start_col, size_t len)
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
{
    size_t start_index;
	/* Index in buf of first character shown in return value. */
    size_t column;
	/* Screen column start_index corresponds to. */
    size_t end_index;
	/* Index in buf of last character shown in return value. */
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */

    if (len == 0)
	return mallocstrcpy(NULL, "");

    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
    assert(column <= start_col);
    end_index = actual_x(buf, start_col + len - 1);
    alloc_len = strnlenpt(buf, end_index + 1) - column;
    if (len > alloc_len + column - start_col)
	len = alloc_len + column - start_col;
    converted = charalloc(alloc_len + 1);
    buf += start_index;
    index = 0;

    for (; index < alloc_len; buf++) {
1759
1760
1761
1762
1763
1764
1765
	if (*buf == '\t') {
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[0] :
#endif
		' '; 
	    while ((column + index) % tabsize)
1766
		converted[index++] = ' ';
1767
	} else if (is_cntrl_char(*buf)) {
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
	    converted[index++] = '^';
	    if (*buf == '\n')
		/* Treat newlines embedded in a line as encoded nulls;
		 * the line in question should be run through unsunder()
		 * before reaching here. */
		converted[index++] = '@';
	    else if (*buf == NANO_CONTROL_8)
		converted[index++] = '?';
	    else
		converted[index++] = *buf + 64;
1778
1779
1780
1781
1782
1783
1784
	} else if (*buf == ' ')
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[1] :
#endif
		' ';
	else
1785
1786
1787
1788
1789
1790
1791
1792
1793
	    converted[index++] = *buf;
    }
    assert(len <= alloc_len + column - start_col);
    charmove(converted, converted + start_col - column, len);
    null_at(&converted, len);

    return charealloc(converted, len + 1);
}

Chris Allegretta's avatar
Chris Allegretta committed
1794
/* Repaint the statusbar when getting a character in nanogetstr().  buf
1795
 * should be no longer than max(0, COLS - 4).
Chris Allegretta's avatar
Chris Allegretta committed
1796
 *
Chris Allegretta's avatar
Chris Allegretta committed
1797
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
1798
 * off! */
1799
void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
1800
{
1801
1802
    size_t x_real = strnlenpt(inputbuf, x);
    int wid = COLS - strlen(buf) - 2;
1803

Chris Allegretta's avatar
Chris Allegretta committed
1804
1805
    assert(0 <= x && x <= strlen(inputbuf));

1806
    wattron(bottomwin, A_REVERSE);
1807
    blank_statusbar();
1808

Chris Allegretta's avatar
Chris Allegretta committed
1809
1810
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824

    if (COLS > 1)
	waddch(bottomwin, x_real < wid ? ' ' : '$');
    if (COLS > 2) {
	size_t page_start = x_real - x_real % wid;
	char *expanded = display_string(inputbuf, page_start, wid);

	assert(wid > 0);
	assert(strlen(expanded) <= wid);
	waddstr(bottomwin, expanded);
	free(expanded);
	wmove(bottomwin, 0, COLS - wid + x_real - page_start);
    } else
	wmove(bottomwin, 0, COLS - 1);
1825
    wattroff(bottomwin, A_REVERSE);
1826
1827
}

1828
/* Get the input from the keyboard; this should only be called from
Chris Allegretta's avatar
Chris Allegretta committed
1829
 * statusq(). */
1830
int nanogetstr(bool allow_tabs, const char *buf, const char *def,
1831
1832
1833
#ifndef NANO_SMALL
		historyheadtype *history_list,
#endif
1834
		const shortcut *s
1835
#ifndef DISABLE_TABCOMP
1836
		, bool *list
1837
#endif
1838
		)
Chris Allegretta's avatar
Chris Allegretta committed
1839
1840
{
    int kbinput;
1841
    bool meta_key, func_key;
1842
    static size_t x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
1843
	/* the cursor position in 'answer' */
1844
    size_t xend;
Chris Allegretta's avatar
Chris Allegretta committed
1845
	/* length of 'answer', the status bar text */
1846
    bool tabbed = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1847
1848
1849
	/* used by input_tab() */
    const shortcut *t;

1850
1851
1852
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
1853
    char *currentbuf = NULL;
1854
    char *complete = NULL;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1855
1856
1857
1858
1859
1860
1861
1862
1863
    int last_kbinput = 0;

    /* This variable is used in the search history code.  use_cb == 0 
       means that we're using the existing history and ignoring
       currentbuf.  use_cb == 1 means that the entry in answer should be
       moved to currentbuf or restored from currentbuf to answer. 
       use_cb == 2 means that the entry in currentbuf should be moved to
       answer or restored from answer to currentbuf. */
    int use_cb = 0;
1864
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1865
    xend = strlen(def);
Chris Allegretta's avatar
Chris Allegretta committed
1866

1867
    /* Only put x at the end of the string if it's uninitialized, if it
1868
1869
1870
1871
       would be past the end of the string as it is, or if
       resetstatuspos is TRUE.  Otherwise, leave it alone.  This is so
       the cursor position stays at the same place if a prompt-changing
       toggle is pressed. */
1872
    if (x == (size_t)-1 || x > xend || resetstatuspos)
Chris Allegretta's avatar
Chris Allegretta committed
1873
1874
	x = xend;

Chris Allegretta's avatar
Chris Allegretta committed
1875
    answer = charealloc(answer, xend + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1876
1877
1878
1879
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1880

1881
#if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
1882
    currshortcut = s;
1883
1884
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1885
    /* Get the input! */
1886

Chris Allegretta's avatar
Chris Allegretta committed
1887
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
1888

1889
1890
    /* Make sure any editor screen updates are displayed before getting
       input */
1891
1892
    wnoutrefresh(edit);
    wrefresh(bottomwin);
1893

1894
1895
1896
1897
1898
    /* If we're using restricted mode, we aren't allowed to change the
     * name of a file once it has one because that would allow writing
     * to files not specified on the command line.  In this case,
     * disable all keys that would change the text if the filename isn't
     * blank and we're at the "Write File" prompt. */
1899
    while ((kbinput = get_kbinput(bottomwin, &meta_key, &func_key)) !=
1900
	NANO_CANCEL_KEY && kbinput != NANO_ENTER_KEY) {
1901
	for (t = s; t != NULL; t = t->next) {
1902
#ifdef DEBUG
1903
	    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
1904
1905
#endif

1906
1907
1908
	    /* Temporary hack to interpret NANO_HELP_FKEY correctly. */
	    if (kbinput == t->funcval)
		kbinput = t->ctrlval;
1909

1910
	    if (kbinput == t->ctrlval && is_cntrl_char(kbinput)) {
1911

1912
#ifndef DISABLE_HELP
1913
1914
		/* Have to do this here, it would be too late to do it
		   in statusq() */
1915
		if (kbinput == NANO_HELP_KEY) {
1916
1917
1918
1919
		    do_help();
		    break;
		}
#endif
1920
1921
#ifndef NANO_SMALL
		/* Have to handle these here too, for the time being */
1922
		if (kbinput == NANO_PREVLINE_KEY || kbinput == NANO_NEXTLINE_KEY)
1923
1924
		    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1925

1926
		return t->ctrlval;
Chris Allegretta's avatar
Chris Allegretta committed
1927
1928
	    }
	}
1929
	assert(x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
1930

Chris Allegretta's avatar
Chris Allegretta committed
1931
	if (kbinput != '\t')
1932
	    tabbed = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1933

Chris Allegretta's avatar
Chris Allegretta committed
1934
	switch (kbinput) {
1935
#ifndef DISABLE_MOUSE
1936
	case KEY_MOUSE:
1937
1938
1939
1940
	    {
		int mouse_x, mouse_y;
		get_mouseinput(&mouse_x, &mouse_y, TRUE);
	    }
1941
	    break;
1942
#endif
1943
1944
1945
	case NANO_REFRESH_KEY:
	    total_refresh();
	    break;
1946
	case NANO_HOME_KEY:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1947
1948
#ifndef NANO_SMALL
	    if (ISSET(SMART_HOME)) {
1949
		size_t old_x = x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1950
1951
1952
1953
1954
1955
1956
1957
1958

		for (x = 0; isblank(answer[x]) && x < xend; x++)
		    ;

		if (x == old_x || x == xend)
		    x = 0;
	    } else
#endif
		x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1959
	    break;
1960
	case NANO_END_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1961
	    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
1962
	    break;
1963
	case NANO_FORWARD_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1964
1965
1966
	    if (x < xend)
		x++;
	    break;
1967
	case NANO_DELETE_KEY:
1968
1969
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable Delete. */
1970
1971
1972
1973
1974
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		if (x < xend) {
		    charmove(answer + x, answer + x + 1, xend - x);
		    xend--;
		}
Chris Allegretta's avatar
Chris Allegretta committed
1975
1976
	    }
	    break;
1977
	case NANO_CUT_KEY:
1978
	    /* If we're using restricted mode, the filename isn't blank,
1979
	     * and we're at the "Write File" prompt, disable Cut. */
1980
1981
1982
1983
1984
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		null_at(&answer, 0);
		xend = 0;
		x = 0;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1985
	    break;
1986
	case NANO_BACKSPACE_KEY:
1987
1988
1989
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable
	     * Backspace. */
1990
1991
1992
1993
1994
1995
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		if (x > 0) {
		    charmove(answer + x - 1, answer + x, xend - x + 1);
		    x--;
		    xend--;
		}
Chris Allegretta's avatar
Chris Allegretta committed
1996
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1997
	    break;
1998
	case NANO_TAB_KEY:
1999
2000
#ifndef NANO_SMALL
	    /* tab history completion */
Chris Allegretta's avatar
Chris Allegretta committed
2001
	    if (history_list != NULL) {
2002
		if (!complete || last_kbinput != NANO_TAB_KEY) {
2003
2004
2005
		    history_list->current = (historytype *)history_list;
		    history_list->len = strlen(answer);
		}
Chris Allegretta's avatar
Chris Allegretta committed
2006

Chris Allegretta's avatar
Chris Allegretta committed
2007
		if (history_list->len > 0) {
2008
2009
		    complete = get_history_completion(history_list, answer);
		    xend = strlen(complete);
Chris Allegretta's avatar
Chris Allegretta committed
2010
		    x = xend;
2011
2012
		    answer = mallocstrcpy(answer, complete);
		}
2013
	    }
2014
#ifndef DISABLE_TABCOMP
2015
2016
	    else
#endif
2017
2018
#endif
#ifndef DISABLE_TABCOMP
2019
	    if (allow_tabs) {
2020
2021
2022
2023
2024
2025
2026
		int shift = 0;

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
		x += shift;
		if (x > xend)
		    x = xend;
2027
2028
2029
	    }
#endif
	    break;
2030
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
2031
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2032
2033
		x--;
	    break;
2034
	case NANO_PREVLINE_KEY:
2035
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2036
	    if (history_list != NULL) {
2037

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2038
2039
2040
2041
2042
2043
		/* if currentbuf is NULL, or if use_cb is 1, currentbuf
		   isn't NULL, and currentbuf is different from answer,
		   it means that we're scrolling up at the top of the
		   search history, and we need to save the current
		   answer in currentbuf; do this and reset use_cb to
		   0 */
2044
2045
		if (currentbuf == NULL || (use_cb == 1 &&
			strcmp(currentbuf, answer) != 0)) {
2046
		    currentbuf = mallocstrcpy(currentbuf, answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2047
		    use_cb = 0;
2048
2049
		}

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2050
2051
2052
2053
2054
2055
		/* if currentbuf isn't NULL, use_cb is 2, and currentbuf 
		   is different from answer, it means that we're
		   scrolling up at the bottom of the search history, and
		   we need to make the string in currentbuf the current
		   answer; do this, blow away currentbuf since we don't
		   need it anymore, and reset use_cb to 0 */
2056
2057
		if (currentbuf != NULL && use_cb == 2 &&
			strcmp(currentbuf, answer) != 0) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
		    answer = mallocstrcpy(answer, currentbuf);
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
		    use_cb = 0;

		/* else get older search from the history list and save
		   it in answer; if there is no older search, blank out 
		   answer */
		} else if ((history = get_history_older(history_list)) != NULL) {
2068
2069
2070
2071
2072
2073
2074
2075
2076
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
2077
	    break;
2078
	case NANO_NEXTLINE_KEY:
2079
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2080
	    if (history_list != NULL) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2081
2082
2083

		/* get newer search from the history list and save it 
		   in answer */
Chris Allegretta's avatar
Chris Allegretta committed
2084
		if ((history = get_history_newer(history_list)) != NULL) {
2085
2086
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
2087

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2088
2089
2090
2091
2092
2093
2094
2095
2096
		/* if there is no newer search, we're here */
		
		/* if currentbuf isn't NULL and use_cb isn't 2, it means 
		   that we're scrolling down at the bottom of the search
		   history and we need to make the string in currentbuf
		   the current answer; do this, blow away currentbuf
		   since we don't need it anymore, and set use_cb to
		   1 */
		} else if (currentbuf != NULL && use_cb != 2) {
2097
		    answer = mallocstrcpy(answer, currentbuf);
Chris Allegretta's avatar
Chris Allegretta committed
2098
2099
2100
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2101
2102
2103
2104
		    use_cb = 1;

		/* otherwise, if currentbuf is NULL and use_cb isn't 2, 
		   it means that we're scrolling down at the bottom of
2105
2106
2107
2108
		   the search history and the current answer (if it's
		   not blank) needs to be saved in currentbuf; do this,
		   blank out answer (if necessary), and set use_cb to
		   2 */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2109
		} else if (use_cb != 2) {
2110
2111
2112
2113
		    if (answer[0] != '\0') {
			currentbuf = mallocstrcpy(currentbuf, answer);
			answer = mallocstrcpy(answer, "");
		    }
2114
		    xend = 0;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2115
		    use_cb = 2;
2116
2117
2118
2119
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2120
	    break;
2121
2122
	    default:

2123
		for (t = s; t != NULL; t = t->next) {
2124
#ifdef DEBUG
2125
		    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
Chris Allegretta's avatar
Chris Allegretta committed
2126
			    kbinput);
2127
#endif
2128
		    if (meta_key && (kbinput == t->metaval || kbinput == t->miscval))
2129
			/* We hit a meta key.  Do like above.  We don't
2130
			 * just put back the letter and let it get
2131
2132
2133
			 * caught above cause that screws the
			 * keypad... */
			return kbinput;
2134
		}
Chris Allegretta's avatar
Chris Allegretta committed
2135

2136
2137
2138
2139
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, act as though the
	     * unhandled character we got is a control character and
	     * throw it away. */
2140
	    if (is_cntrl_char(kbinput) || (ISSET(RESTRICTED) && filename[0] != '\0' && s == writefile_list))
Chris Allegretta's avatar
Chris Allegretta committed
2141
		break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2142
	    answer = charealloc(answer, xend + 2);
2143
	    charmove(answer + x + 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2144
2145
	    xend++;
	    answer[x] = kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2146
2147
2148
	    x++;

#ifdef DEBUG
2149
	    fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
2150
#endif
2151
	} /* switch (kbinput) */
2152
#ifndef NANO_SMALL
2153
	last_kbinput = kbinput;
2154
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2155
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
2156
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
2157
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
2158

Chris Allegretta's avatar
Chris Allegretta committed
2159
    /* We finished putting in an answer; reset x */
2160
    x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
2161

2162
    return kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2163
2164
}

2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
/* Ask a question on the statusbar.  Answer will be stored in answer
 * global.  Returns -1 on aborted enter, -2 on a blank string, and 0
 * otherwise, the valid shortcut key caught.  def is any editable text
 * we want to put up by default.
 *
 * New arg tabs tells whether or not to allow tab completion. */
int statusq(bool allow_tabs, const shortcut *s, const char *def,
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
		const char *msg, ...)
{
    va_list ap;
    char *foo = charalloc(COLS - 3);
    int ret;
#ifndef DISABLE_TABCOMP
    bool list = FALSE;
#endif

    bottombars(s);

    va_start(ap, msg);
    vsnprintf(foo, COLS - 4, msg, ap);
    va_end(ap);
    foo[COLS - 4] = '\0';

    ret = nanogetstr(allow_tabs, foo, def,
#ifndef NANO_SMALL
		which_history,
#endif
		s
#ifndef DISABLE_TABCOMP
		, &list
#endif
		);
    free(foo);
    resetstatuspos = FALSE;

    switch (ret) {
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
	case NANO_FIRSTLINE_KEY:
	case NANO_FIRSTLINE_FKEY:
	    do_first_line();
	    resetstatuspos = TRUE;
	    break;
	case NANO_LASTLINE_KEY:
	case NANO_LASTLINE_FKEY:
	    do_last_line();
	    resetstatuspos = TRUE;
	    break;
2214
#ifndef DISABLE_JUSTIFY
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
	case NANO_PARABEGIN_KEY:
	case NANO_PARABEGIN_ALTKEY1:
	case NANO_PARABEGIN_ALTKEY2:
	    do_para_begin();
	    resetstatuspos = TRUE;
	    break;
	case NANO_PARAEND_KEY:
	case NANO_PARAEND_ALTKEY1:
	case NANO_PARAEND_ALTKEY2:
	    do_para_end();
	    resetstatuspos = TRUE;
	    break;
	case NANO_FULLJUSTIFY_KEY:
	case NANO_FULLJUSTIFY_ALTKEY:
	    if (!ISSET(VIEW_MODE))
		do_full_justify();
	    resetstatuspos = TRUE;
	    break;
2233
#endif
2234
2235
2236
2237
2238
2239
2240
2241
	case NANO_CANCEL_KEY:
	    ret = -1;
	    resetstatuspos = TRUE;
	    break;
	case NANO_ENTER_KEY:
	    ret = (answer[0] == '\0') ? -2 : 0;
	    resetstatuspos = TRUE;
	    break;
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
    }
    blank_statusbar();

#ifdef DEBUG
    fprintf(stderr, "I got \"%s\"\n", answer);
#endif

#ifndef DISABLE_TABCOMP
	/* if we've done tab completion, there might be a list of
	   filename matches on the edit window at this point; make sure
	   they're cleared off. */
	if (list)
	    edit_refresh();
#endif

    return ret;
}

void statusq_abort(void)
{
    resetstatuspos = TRUE;
}

2265
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2266
{
2267
    int space;
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
	/* The space we have available for display. */
    size_t verlen = strlen(VERMSG) + 1;
	/* The length of the version message. */
    const char *prefix;
	/* "File:", "Dir:", or "New Buffer".  Goes before filename. */
    size_t prefixlen;
	/* strlen(prefix) + 1. */
    const char *state;
	/* "Modified", "View", or spaces the length of "Modified".
	 * Tells the state of this buffer. */
    size_t statelen = 0;
	/* strlen(state) + 1. */
    char *exppath = NULL;
	/* The file name, expanded for display. */
2282
    size_t exppathlen = 0;
2283
	/* strlen(exppath) + 1. */
2284
    bool newfie = FALSE;
2285
	/* Do we say "New Buffer"? */
2286
    bool dots = FALSE;
2287
2288
2289
2290
	/* Do we put an ellipsis before the path? */

    assert(path != NULL || filename != NULL);
    assert(COLS >= 0);
Chris Allegretta's avatar
Chris Allegretta committed
2291
2292

    wattron(topwin, A_REVERSE);
2293

2294
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2295

2296
2297
2298
2299
    if (COLS <= 5 || COLS - 5 < verlen)
	space = 0;
    else {
	space = COLS - 5 - verlen;
2300
2301
2302
2303
	/* Reserve 2/3 of the screen plus one column for after the
	 * version message. */
	if (space < COLS - (COLS / 3) + 1)
	    space = COLS - (COLS / 3) + 1;
2304
    }
Chris Allegretta's avatar
Chris Allegretta committed
2305

2306
    if (COLS > 4) {
2307
2308
2309
	/* The version message should only take up 1/3 of the screen
	 * minus one column. */
	mvwaddnstr(topwin, 0, 2, VERMSG, (COLS / 3) - 3);
2310
2311
	waddstr(topwin, "  ");
    }
Chris Allegretta's avatar
Chris Allegretta committed
2312
2313

    if (ISSET(MODIFIED))
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
	state = _("Modified");
    else if (path == NULL && ISSET(VIEW_MODE))
	state = _("View");
    else {
	if (space > 0)
	    statelen = strnlen(_("Modified"), space - 1) + 1;
	state = &hblank[COLS - statelen];
    }
    statelen = strnlen(state, COLS);
    /* We need a space before state. */
    if ((ISSET(MODIFIED) || ISSET(VIEW_MODE)) && statelen < COLS)
	statelen++;

    assert(space >= 0);
    if (space == 0 || statelen >= space)
	goto the_end;

#ifndef DISABLE_BROWSER
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
    if (filename[0] == '\0') {
	prefix = _("New Buffer");
2338
	newfie = TRUE;
2339
2340
2341
2342
    } else
	prefix = _("File:");
    assert(statelen < space);
    prefixlen = strnlen(prefix, space - statelen);
2343
2344
    /* If newfie is FALSE, we need a space after prefix. */
    if (!newfie && prefixlen + statelen < space)
2345
2346
2347
2348
	prefixlen++;

    if (path == NULL)
	path = filename;
2349
2350
2351
2352
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
2353
	/* space is now the room we have for the file name. */
2354
    if (!newfie) {
2355
2356
2357
2358
2359
2360
2361
2362
	size_t lenpt = strlenpt(path), start_col;

	if (lenpt > space)
	    start_col = actual_x(path, lenpt - space);
	else
	    start_col = 0;
	exppath = display_string(path, start_col, space);
	dots = (lenpt > space);
2363
	exppathlen = strlen(exppath);
2364
2365
2366
2367
    }

    if (!dots) {
	/* There is room for the whole filename, so we center it. */
2368
	waddnstr(topwin, hblank, (space - exppathlen) / 3);
2369
	waddnstr(topwin, prefix, prefixlen);
2370
	if (!newfie) {
2371
2372
2373
2374
2375
2376
2377
	    assert(strlen(prefix) + 1 == prefixlen);
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    } else {
	/* We will say something like "File: ...ename". */
	waddnstr(topwin, prefix, prefixlen);
2378
	if (space == 0 || newfie)
2379
2380
2381
2382
2383
2384
	    goto the_end;
	waddch(topwin, ' ');
	waddnstr(topwin, "...", space);
	if (space <= 3)
	    goto the_end;
	space -= 3;
2385
	assert(exppathlen == space + 3);
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
	waddnstr(topwin, exppath + 3, space);
    }

  the_end:
    free(exppath);

    if (COLS <= 1 || statelen >= COLS - 1)
	mvwaddnstr(topwin, 0, 0, state, COLS);
    else {
	assert(COLS - statelen - 2 >= 0);
	mvwaddch(topwin, 0, COLS - statelen - 2, ' ');
	mvwaddnstr(topwin, 0, COLS - statelen - 1, state, statelen);
    }
2399

Chris Allegretta's avatar
Chris Allegretta committed
2400
    wattroff(topwin, A_REVERSE);
2401

2402
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2403
    reset_cursor();
2404
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2405
2406
}

2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
/* If modified is not already set, set it and update titlebar. */
void set_modified(void)
{
    if (!ISSET(MODIFIED)) {
	SET(MODIFIED);
	titlebar(NULL);
    }
}

void statusbar(const char *msg, ...)
{
    va_list ap;

    va_start(ap, msg);

    /* Curses mode is turned off.  If we use wmove() now, it will muck
     * up the terminal settings.  So we just use vfprintf(). */
    if (curses_ended) {
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

    /* Blank out the line. */
    blank_statusbar();

    if (COLS >= 4) {
	char *bar;
	char *foo;
	size_t start_x = 0, foo_len;
2437
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2438
	bool old_whitespace = ISSET(WHITESPACE_DISPLAY);
2439
2440
	UNSET(WHITESPACE_DISPLAY);
#endif
2441
2442
2443
2444
	bar = charalloc(COLS - 3);
	vsnprintf(bar, COLS - 3, msg, ap);
	va_end(ap);
	foo = display_string(bar, 0, COLS - 4);
2445
2446
2447
2448
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	if (old_whitespace)
	    SET(WHITESPACE_DISPLAY);
#endif
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
	free(bar);
	foo_len = strlen(foo);
	start_x = (COLS - foo_len - 4) / 2;

	wmove(bottomwin, 0, start_x);
	wattron(bottomwin, A_REVERSE);

	waddstr(bottomwin, "[ ");
	waddstr(bottomwin, foo);
	free(foo);
	waddstr(bottomwin, " ]");
	wattroff(bottomwin, A_REVERSE);
	wnoutrefresh(bottomwin);
	reset_cursor();
	wrefresh(edit);
	    /* Leave the cursor at its position in the edit window, not
	     * in the statusbar. */
    }

    SET(DISABLE_CURPOS);
2469
    statusblank = 26;
2470
2471
}

2472
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
2473
{
2474
    size_t i, colwidth, slen;
2475

Chris Allegretta's avatar
Chris Allegretta committed
2476
2477
2478
    if (ISSET(NO_HELP))
	return;

2479
2480
    if (s == main_list) {
	slen = MAIN_VISIBLE;
2481
	assert(slen <= length_of_list(s));
2482
    } else {
2483
2484
	slen = length_of_list(s);

2485
	/* Don't show any more shortcuts than the main list does. */
2486
2487
2488
2489
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2490
2491
2492
    /* There will be this many characters per column.  We need at least
     * 3 to display anything properly.*/
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2493

2494
    blank_bottombars();
2495

2496
    for (i = 0; i < slen; i++, s = s->next) {
2497
	const char *keystr;
2498

2499
	/* Yucky sentinel values we can't handle a better way. */
2500
#ifndef NANO_SMALL
2501
	if (s->ctrlval == NANO_HISTORY_KEY)
2502
2503
	    keystr = _("Up");
	else {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2504
#endif
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
	    char foo[4];

	    if (s->ctrlval == NANO_CONTROL_SPACE)
		strcpy(foo, "^ ");
	    else if (s->ctrlval == NANO_CONTROL_8)
		strcpy(foo, "^?");
	    /* Normal values.  Assume that the shortcut has an
	     * equivalent control key, meta key sequence, or both. */
	    else if (s->ctrlval != NANO_NO_KEY)
		sprintf(foo, "^%c", s->ctrlval + 64);
	    else if (s->metaval != NANO_NO_KEY)
		sprintf(foo, "M-%c", toupper(s->metaval));
2517

2518
2519
2520
2521
2522
2523
	    keystr = foo;
#ifndef NANO_SMALL
	}
#endif

	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2524
	onekey(keystr, s->desc, colwidth);
Chris Allegretta's avatar
Chris Allegretta committed
2525
    }
2526

2527
2528
2529
    wnoutrefresh(bottomwin);
    reset_cursor();
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2530
2531
}

2532
2533
2534
2535
2536
2537
/* Write a shortcut key to the help area at the bottom of the window.
 * keystroke is e.g. "^G" and desc is e.g. "Get Help".  We are careful
 * to write at most len characters, even if len is very small and
 * keystroke and desc are long.  Note that waddnstr(,,(size_t)-1) adds
 * the whole string!  We do not bother padding the entry with blanks. */
void onekey(const char *keystroke, const char *desc, size_t len)
Chris Allegretta's avatar
Chris Allegretta committed
2538
{
2539
    assert(keystroke != NULL && desc != NULL && len >= 0);
2540
2541
2542
    wattron(bottomwin, A_REVERSE);
    waddnstr(bottomwin, keystroke, len);
    wattroff(bottomwin, A_REVERSE);
2543
    len -= strlen(keystroke) + 1;
2544
2545
2546
    if (len > 0) {
	waddch(bottomwin, ' ');
	waddnstr(bottomwin, desc, len);
Chris Allegretta's avatar
Chris Allegretta committed
2547
2548
2549
    }
}

2550
/* And so start the display update routines. */
Chris Allegretta's avatar
Chris Allegretta committed
2551

2552
2553
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
2554
{
2555
2556
    int check_line = 0;
    const filestruct *filetmp;
2557

2558
2559
2560
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
2561
}
2562
#endif
2563

2564
2565
2566
2567
2568
/* nano scrolls horizontally within a line in chunks.  This function
 * returns the column number of the first character displayed in the
 * window when the cursor is at the given column.  Note that
 * 0 <= column - get_page_start(column) < COLS. */
size_t get_page_start(size_t column)
Chris Allegretta's avatar
Chris Allegretta committed
2569
{
2570
2571
2572
2573
    assert(COLS > 0);
    if (column == 0 || column < COLS - 1)
	return 0;
    else if (COLS > 9)
2574
	return column - 7 - (column - 7) % (COLS - 8);
2575
2576
2577
2578
2579
    else if (COLS > 2)
	return column - (COLS - 2);
    else
	return column - (COLS - 1);
		/* The parentheses are necessary to avoid overflow. */
Chris Allegretta's avatar
Chris Allegretta committed
2580
2581
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2582
/* Resets current_y, based on the position of current, and puts the
2583
 * cursor in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2584
2585
void reset_cursor(void)
{
2586
2587
2588
2589
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
    if (edittop == NULL || current == NULL) {
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2590
	return;
2591
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2592

2593
2594
2595
2596
2597
    current_y = current->lineno - edittop->lineno;
    if (current_y < editwinrows) {
	size_t x = xplustabs();
	wmove(edit, current_y, x - get_page_start(x));
     }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2598
}
Chris Allegretta's avatar
Chris Allegretta committed
2599

2600
2601
2602
2603
/* edit_add() takes care of the job of actually painting a line into the
 * edit window.  fileptr is the line to be painted, at row yval of the
 * window.  converted is the actual string to be written to the window,
 * with tabs and control characters replaced by strings of regular
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2604
2605
 * characters.  start is the column number of the first character of
 * this page.  That is, the first character of converted corresponds to
2606
 * character number actual_x(fileptr->data, start) of the line. */
2607
2608
void edit_add(const filestruct *fileptr, const char *converted, int
	yval, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2609
{
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
#if defined(ENABLE_COLOR) || !defined(NANO_SMALL)
    size_t startpos = actual_x(fileptr->data, start);
	/* The position in fileptr->data of the leftmost character
	 * that displays at least partially on the window. */
    size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1;
	/* The position in fileptr->data of the first character that is
	 * completely off the window to the right.
	 *
	 * Note that endpos might be beyond the null terminator of the
	 * string. */
2620
2621
#endif

2622
2623
2624
    assert(fileptr != NULL && converted != NULL);
    assert(strlen(converted) <= COLS);

2625
    /* Just paint the string in any case (we'll add color or reverse on
2626
2627
     * just the text that needs it). */
    mvwaddstr(edit, yval, 0, converted);
2628

Chris Allegretta's avatar
Chris Allegretta committed
2629
#ifdef ENABLE_COLOR
2630
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
2631
2632
2633
2634
2635
2636
	const colortype *tmpcolor = colorstrings;

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
2637
		/* Number of chars to paint on this line.  There are COLS
2638
		 * characters on a whole line. */
2639
2640
	    regmatch_t startmatch;	/* match position for start_regexp */
	    regmatch_t endmatch;	/* match position for end_regexp */
2641
2642
2643
2644

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2645
2646
	    /* Two notes about regexec().  Return value 0 means there is
	     * a match.  Also, rm_eo is the first non-matching character
2647
2648
2649
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
2650
	    if (tmpcolor->end == NULL) {
2651
2652
2653
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2654
2655
2656
2657
2658
2659
2660
2661
		 * last match.  Even though two matches may overlap, we
		 * want to ignore them, so that we can highlight
		 * C-strings correctly. */
		while (k < endpos) {
		    /* Note the fifth parameter to regexec().  It says
		     * not to match the beginning-of-line character
		     * unless k is 0.  If regexec() returns REG_NOMATCH,
		     * there are no more matches in the line. */
2662
		    if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
2663
			&startmatch, k == 0 ? 0 : REG_NOTBOL) == REG_NOMATCH)
2664
			break;
2665
2666
2667
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2668
2669
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
2670
			statusbar(_("Refusing 0 length regex match"));
2671
2672
2673
		    } else if (startmatch.rm_so < endpos &&
				startmatch.rm_eo > startpos) {
			if (startmatch.rm_so <= startpos)
2674
			    x_start = 0;
2675
			else
2676
2677
2678
2679
			    x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
			paintlen = strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start;
2680
2681
2682
2683
2684
2685
			if (paintlen > COLS - x_start)
			    paintlen = COLS - x_start;

			assert(0 <= x_start && 0 < paintlen &&
				x_start + paintlen <= COLS);
			mvwaddnstr(edit, yval, x_start,
2686
2687
				converted + x_start, paintlen);
		    }
2688
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2689
		}
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
	    } else {
		/* This is a multi-line regexp.  There are two steps. 
		 * First, we have to see if the beginning of the line is
		 * colored by a start on an earlier line, and an end on
		 * this line or later.
		 *
		 * We find the first line before fileptr matching the
		 * start.  If every match on that line is followed by an
		 * end, then go to step two.  Otherwise, find the next line
		 * after start_line matching the end.  If that line is not
		 * before fileptr, then paint the beginning of this line. */

		const filestruct *start_line = fileptr->prev;
2703
		    /* the first line before fileptr matching start */
2704
2705
2706
2707
2708
		regoff_t start_col;
		    /* where it starts in that line */
		const filestruct *end_line;

		while (start_line != NULL &&
2709
			regexec(&tmpcolor->start, start_line->data, 1,
2710
			&startmatch, 0) == REG_NOMATCH) {
2711
2712
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
2713
2714
		    if (regexec(tmpcolor->end, start_line->data, 0,
			NULL, 0) == 0)
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
			goto step_two;
		    start_line = start_line->prev;
		}
		/* No start found, so skip to the next step. */
		if (start_line == NULL)
		    goto step_two;
		/* Now start_line is the first line before fileptr
		 * containing a start match.  Is there a start on this
		 * line not followed by an end on this line? */

		start_col = 0;
2726
		while (TRUE) {
2727
2728
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
2729
 		    if (regexec(tmpcolor->end,
2730
			start_line->data + start_col + startmatch.rm_eo,
2731
2732
2733
			0, NULL, start_col + startmatch.rm_eo == 0 ? 0 :
			REG_NOTBOL) == REG_NOMATCH)
			/* No end found after this start. */
2734
			break;
2735
		    start_col++;
2736
		    if (regexec(&tmpcolor->start,
2737
2738
			start_line->data + start_col, 1,
			&startmatch, REG_NOTBOL) == REG_NOMATCH)
2739
2740
			/* No later start on this line. */
			goto step_two;
2741
		}
2742
2743
		/* Indeed, there is a start not followed on this line by
		 * an end. */
2744
2745
2746

		/* We have already checked that there is no end before
		 * fileptr and after the start.  Is there an end after
2747
2748
		 * the start at all?  We don't paint unterminated
		 * starts. */
2749
		end_line = fileptr;
2750
		while (end_line != NULL &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2751
2752
			regexec(tmpcolor->end, end_line->data, 1,
			&endmatch, 0) == REG_NOMATCH)
2753
2754
2755
		    end_line = end_line->next;

		/* No end found, or it is too early. */
2756
2757
		if (end_line == NULL ||
			(end_line == fileptr && endmatch.rm_eo <= startpos))
2758
2759
2760
		    goto step_two;

		/* Now paint the start of fileptr. */
2761
2762
		paintlen = end_line != fileptr ? COLS :
			strnlenpt(fileptr->data, endmatch.rm_eo) - start;
2763
2764
2765
2766
		if (paintlen > COLS)
		    paintlen = COLS;

		assert(0 < paintlen && paintlen <= COLS);
2767
		mvwaddnstr(edit, yval, 0, converted, paintlen);
2768
2769
2770
2771
2772

		/* We have already painted the whole line. */
		if (paintlen == COLS)
		    goto skip_step_two;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2773
2774
  step_two:
		/* Second step, we look for starts on this line. */
2775
		start_col = 0;
2776
		while (start_col < endpos) {
2777
2778
2779
2780
		    if (regexec(&tmpcolor->start,
			fileptr->data + start_col, 1, &startmatch,
			start_col == 0 ? 0 : REG_NOTBOL) == REG_NOMATCH ||
			start_col + startmatch.rm_so >= endpos)
2781
2782
2783
2784
2785
2786
2787
			/* No more starts on this line. */
			break;
		    /* Translate the match to be relative to the
		     * beginning of the line. */
		    startmatch.rm_so += start_col;
		    startmatch.rm_eo += start_col;

2788
		    if (startmatch.rm_so <= startpos)
2789
			x_start = 0;
2790
		    else
2791
2792
2793
2794
2795
			x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
		    if (regexec(tmpcolor->end,
			fileptr->data + startmatch.rm_eo, 1, &endmatch,
			startmatch.rm_eo == 0 ? 0 : REG_NOTBOL) == 0) {
2796
			/* Translate the end match to be relative to the
2797
			 * beginning of the line. */
2798
2799
2800
			endmatch.rm_so += startmatch.rm_eo;
			endmatch.rm_eo += startmatch.rm_eo;
			/* There is an end on this line.  But does it
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2801
2802
			 * appear on this page, and is the match more
			 * than zero characters long? */
2803
			if (endmatch.rm_eo > startpos &&
2804
				endmatch.rm_eo > startmatch.rm_so) {
2805
2806
			    paintlen = strnlenpt(fileptr->data,
				endmatch.rm_eo) - start - x_start;
2807
2808
2809
2810
			    if (x_start + paintlen > COLS)
				paintlen = COLS - x_start;

			    assert(0 <= x_start && 0 < paintlen &&
2811
				x_start + paintlen <= COLS);
2812
			    mvwaddnstr(edit, yval, x_start,
2813
				converted + x_start, paintlen);
2814
			}
2815
		    } else {
2816
2817
2818
			/* There is no end on this line.  But we haven't
			 * yet looked for one on later lines. */
			end_line = fileptr->next;
2819
2820
2821
			while (end_line != NULL &&
				regexec(tmpcolor->end, end_line->data, 0,
				NULL, 0) == REG_NOMATCH)
2822
2823
2824
2825
			    end_line = end_line->next;
			if (end_line != NULL) {
			    assert(0 <= x_start && x_start < COLS);
			    mvwaddnstr(edit, yval, x_start,
2826
				converted + x_start, COLS - x_start);
2827
2828
2829
			    /* We painted to the end of the line, so
			     * don't bother checking any more starts. */
			    break;
2830
2831
			}
		    }
2832
		    start_col = startmatch.rm_so + 1;
2833
		} /* while start_col < endpos */
2834
	    } /* if (tmp_color->end != NULL) */
2835

2836
  skip_step_two:
2837
2838
2839
2840
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
2841
#endif				/* ENABLE_COLOR */
2842

2843
2844
2845
2846
2847
2848
2849
2850
#ifndef NANO_SMALL
    if (ISSET(MARK_ISSET)
	    && (fileptr->lineno <= mark_beginbuf->lineno
		|| fileptr->lineno <= current->lineno)
	    && (fileptr->lineno >= mark_beginbuf->lineno
		|| fileptr->lineno >= current->lineno)) {
	/* fileptr is at least partially selected. */

2851
2852
2853
2854
2855
2856
	const filestruct *top;
	    /* Either current or mark_beginbuf, whichever is first. */
	size_t top_x;
	    /* current_x or mark_beginx, corresponding to top. */
	const filestruct *bot;
	size_t bot_x;
2857
2858
2859
	int x_start;
	    /* Starting column for mvwaddnstr.  Zero-based. */
	int paintlen;
2860
	    /* Number of chars to paint on this line.  There are COLS
2861
2862
	     * characters on a whole line. */

2863
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2864
2865
2866
2867
2868

	if (top->lineno < fileptr->lineno || top_x < startpos)
	    top_x = startpos;
	if (bot->lineno > fileptr->lineno || bot_x > endpos)
	    bot_x = endpos;
Chris Allegretta's avatar
Chris Allegretta committed
2869

2870
	/* The selected bit of fileptr is on this page. */
2871
2872
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2873
2874
2875
2876

	    /* x_start is the expanded location of the beginning of the
	     * mark minus the beginning of the page. */
	    x_start = strnlenpt(fileptr->data, top_x) - start;
2877
2878

	    if (bot_x >= endpos)
2879
2880
2881
2882
		/* If the end of the mark is off the page, paintlen is
		 * -1, meaning that everything on the line gets
		 * painted. */
		paintlen = -1;
2883
	    else
2884
2885
2886
		/* Otherwise, paintlen is the expanded location of the
		 * end of the mark minus the expanded location of the
		 * beginning of the mark. */
2887
2888
		paintlen = strnlenpt(fileptr->data, bot_x)
			- (x_start + start);
2889
2890
2891
2892
2893
2894
2895
2896

	    /* If x_start is before the beginning of the page, shift
	     * paintlen x_start characters to compensate, and put
	     * x_start at the beginning of the page. */
	    if (x_start < 0) {
		paintlen += x_start;
		x_start = 0;
	    }
2897
2898
2899

	    assert(x_start >= 0 && x_start <= strlen(converted));

2900
	    wattron(edit, A_REVERSE);
2901
	    mvwaddnstr(edit, yval, x_start, converted + x_start, paintlen);
2902
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
2903
	}
2904
    }
2905
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2906
2907
}

2908
/* Just update one line in the edit buffer.  Basically a wrapper for
2909
2910
 * edit_add().
 *
2911
2912
2913
 * If fileptr != current, then index is considered 0.  The line will be
 * displayed starting with fileptr->data[index].  Likely args are
 * current_x or 0. */
2914
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2915
{
2916
2917
2918
2919
2920
2921
    int line;
	/* line in the edit window for CURSES calls */
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2922

2923
    assert(fileptr != NULL);
2924

2925
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2926

2927
2928
    /* We assume the line numbers are valid.  Is that really true? */
    assert(line < 0 || line == check_linenumbers(fileptr));
2929

2930
2931
    if (line < 0 || line >= editwinrows)
	return;
2932

2933
2934
2935
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

2936
2937
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2938
    index = (fileptr == current) ? strnlenpt(fileptr->data, index) : 0;
2939
    page_start = get_page_start(index);
2940

2941
2942
2943
    /* Expand the line, replacing Tab by spaces, and control characters
     * by their display form. */
    converted = display_string(fileptr->data, page_start, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
2944

Chris Allegretta's avatar
Chris Allegretta committed
2945
    /* Now, paint the line */
2946
    edit_add(fileptr, converted, line, page_start);
2947
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2948

2949
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2950
	mvwaddch(edit, line, 0, '$');
2951
    if (strlenpt(fileptr->data) > page_start + COLS)
2952
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
2953
2954
}

2955
2956
/* Return a nonzero value if we need an update after moving
 * horizontally.  We need one if the mark is on or if old_pww and
2957
 * placewewant are on different pages. */
2958
int need_horizontal_update(size_t old_pww)
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
{
    return
#ifndef NANO_SMALL
	ISSET(MARK_ISSET) ||
#endif
	get_page_start(old_pww) != get_page_start(placewewant);
}

/* Return a nonzero value if we need an update after moving vertically.
 * We need one if the mark is on or if old_pww and placewewant
2969
 * are on different pages. */
2970
int need_vertical_update(size_t old_pww)
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
{
    return
#ifndef NANO_SMALL
	ISSET(MARK_ISSET) ||
#endif
	get_page_start(old_pww) != get_page_start(placewewant);
}

/* Scroll the edit window in the given direction and the given number
 * of lines, and draw new lines on the blank lines left after the
 * scrolling.  direction is the direction to scroll, either UP or DOWN,
 * and nlines is the number of lines to scroll.  Don't redraw the old
 * topmost or bottommost line (where we assume current is) before
 * scrolling or draw the new topmost or bottommost line after scrolling
 * (where we assume current will be), since we don't know where we are
2986
 * on the page or whether we'll stay there. */
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
void edit_scroll(updown direction, int nlines)
{
    filestruct *foo;
    int i, scroll_rows = 0;

    /* Scrolling less than one line or more than editwinrows lines is
     * redundant, so don't allow it. */
    if (nlines < 1 || nlines > editwinrows)
	return;

    /* Move the top line of the edit window up or down (depending on the
     * value of direction) nlines lines.  If there are fewer lines of
     * text than that left, move it to the top or bottom line of the
     * file (depending on the value of direction).  Keep track of
     * how many lines we moved in scroll_rows. */
    for (i = nlines; i > 0; i--) {
	if (direction == UP) {
	    if (edittop->prev == NULL)
		break;
	    edittop = edittop->prev;
	    scroll_rows--;
	} else {
	    if (edittop->next == NULL)
		break;
	    edittop = edittop->next;
	    scroll_rows++;
	}
    }

    /* Scroll the text on the screen up or down scroll_rows lines,
     * depending on the value of direction. */
    scrollok(edit, TRUE);
    wscrl(edit, scroll_rows);
    scrollok(edit, FALSE);

    foo = edittop;
    if (direction != UP) {
	int slines = editwinrows - nlines;
	for (; slines > 0 && foo != NULL; slines--)
	    foo = foo->next;
    }

    /* And draw new lines on the blank top or bottom lines of the edit
     * window, depending on the value of direction.  Don't draw the new
     * topmost or new bottommost line. */
    while (scroll_rows != 0 && foo != NULL) {
	if (foo->next != NULL)
	    update_line(foo, 0);
	if (direction == UP)
	    scroll_rows++;
	else
	    scroll_rows--;
	foo = foo->next;
    }
}

/* Update any lines between old_current and current that need to be
3044
 * updated. */
3045
void edit_redraw(const filestruct *old_current, size_t old_pww)
3046
{
3047
3048
    int do_refresh = need_vertical_update(0) ||
	need_vertical_update(old_pww);
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
    const filestruct *foo;

    /* If either old_current or current is offscreen, refresh the screen
     * and get out. */
    if (old_current->lineno < edittop->lineno || old_current->lineno >=
	edittop->lineno + editwinrows || current->lineno <
	edittop->lineno || current->lineno >= edittop->lineno +
	editwinrows) {
	edit_refresh();
	return;
    }

3061
3062
3063
    /* Update old_current and current if we're not on the first page
     * and/or we're not on the same page as before.  If the mark is on,
     * update all the lines between old_current and current too. */
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
    foo = old_current;
    while (foo != current) {
	if (do_refresh)
	    update_line(foo, 0);
#ifndef NANO_SMALL
	if (!ISSET(MARK_ISSET))
#endif
	    break;
	if (foo->lineno > current->lineno)
	    foo = foo->prev;
	else
	    foo = foo->next;
    }
    if (do_refresh)
	update_line(current, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
3081
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
3082
3083
void edit_refresh(void)
{
3084
3085
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
3086
3087
3088
3089
3090
3091
3092
3093
	/* Note that edit_update() changes edittop so that it's in range
	 * of current.  Thus, when it then calls edit_refresh(), there
	 * is no danger of getting an infinite loop. */
	edit_update(
#ifndef NANO_SMALL
		ISSET(SMOOTHSCROLL) ? NONE :
#endif
		CENTER);
3094
3095
    else {
	int nlines = 0;
3096
	const filestruct *foo = edittop;
Chris Allegretta's avatar
Chris Allegretta committed
3097

3098
#ifdef DEBUG
3099
	fprintf(stderr, "edit_refresh(): edittop->lineno = %d\n", edittop->lineno);
3100
#endif
3101

3102
	while (nlines < editwinrows) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3103
	    update_line(foo, foo == current ? current_x : 0);
3104
	    nlines++;
3105
	    if (foo->next == NULL)
3106
		break;
3107
	    foo = foo->next;
3108
3109
3110
3111
3112
	}
	while (nlines < editwinrows) {
	    mvwaddstr(edit, nlines, 0, hblank);
	    nlines++;
	}
3113
	reset_cursor();
3114
3115
	wrefresh(edit);
    }
Chris Allegretta's avatar
Chris Allegretta committed
3116
3117
}

3118
3119
/* A nice generic routine to update the edit buffer.  We keep current in
 * the same place and move edittop to put it in range of current. */
3120
void edit_update(topmidnone location)
Chris Allegretta's avatar
Chris Allegretta committed
3121
{
3122
3123
    filestruct *foo = current;

Chris Allegretta's avatar
Chris Allegretta committed
3124
    if (location != TOP) {
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
	/* If location is CENTER, we move edittop up (editwinrows / 2)
	 * lines.  This puts current at the center of the screen.  If
	 * location is NONE, we move edittop up current_y lines if
	 * current_y is in range of the screen, 0 lines if current_y is
	 * less than 0, or (editwinrows - 1) lines if current_y is
	 * greater than (editwinrows - 1).  This puts current at the
	 * same place on the screen as before, or at the top or bottom
	 * of the screen if edittop is beyond either. */
	int goal;

	if (location == CENTER)
	    goal = editwinrows / 2;
	else {
	    goal = current_y;

	    /* Limit goal to (editwinrows - 1) lines maximum. */
	    if (goal > editwinrows - 1)
		goal = editwinrows - 1;
	}
Chris Allegretta's avatar
Chris Allegretta committed
3144

3145
3146
	for (; goal > 0 && foo->prev != NULL; goal--)
	    foo = foo->prev;
Chris Allegretta's avatar
Chris Allegretta committed
3147
    }
3148

3149
    edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
3150
3151
3152
    edit_refresh();
}

3153
3154
3155
/* Ask a simple yes/no question, specified in msg, on the statusbar.
 * Return 1 for Y, 0 for N, 2 for All (if all is TRUE when passed in)
 * and -1 for abort (^C). */
3156
int do_yesno(bool all, const char *msg)
Chris Allegretta's avatar
Chris Allegretta committed
3157
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3158
    int ok = -2, width = 16;
3159
3160
    const char *yesstr;		/* String of yes characters accepted. */
    const char *nostr;		/* Same for no. */
Chris Allegretta's avatar
Chris Allegretta committed
3161
    const char *allstr;		/* And all, surprise! */
Chris Allegretta's avatar
Chris Allegretta committed
3162

3163
3164
3165
3166
3167
3168
    /* yesstr, nostr, and allstr are strings of any length.  Each string
     * consists of all characters accepted as a valid character for that
     * value.  The first value will be the one displayed in the
     * shortcuts.  Translators: if possible, specify both the shortcuts
     * for your language and English.  For example, in French: "OoYy"
     * for "Oui". */
3169
3170
3171
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
3172
3173

    if (!ISSET(NO_HELP)) {
3174
	char shortstr[3];		/* Temp string for Y, N, A. */
3175

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3176
3177
3178
	if (COLS < 32)
	    width = COLS / 2;

3179
	/* Write the bottom of the screen. */
3180
	blank_bottombars();
3181

3182
	sprintf(shortstr, " %c", yesstr[0]);
3183
	wmove(bottomwin, 1, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3184
	onekey(shortstr, _("Yes"), width);
3185
3186

	if (all) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3187
	    wmove(bottomwin, 1, width);
3188
	    shortstr[1] = allstr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3189
	    onekey(shortstr, _("All"), width);
3190
3191
	}

3192
	wmove(bottomwin, 2, 0);
3193
	shortstr[1] = nostr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3194
	onekey(shortstr, _("No"), width);
3195

3196
	wmove(bottomwin, 2, 16);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3197
	onekey("^C", _("Cancel"), width);
Chris Allegretta's avatar
Chris Allegretta committed
3198
    }
3199

Chris Allegretta's avatar
Chris Allegretta committed
3200
    wattron(bottomwin, A_REVERSE);
3201
3202

    blank_statusbar();
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3203
    mvwaddnstr(bottomwin, 0, 0, msg, COLS - 1);
3204

Chris Allegretta's avatar
Chris Allegretta committed
3205
    wattroff(bottomwin, A_REVERSE);
3206

Chris Allegretta's avatar
Chris Allegretta committed
3207
3208
    wrefresh(bottomwin);

3209
    do {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3210
	int kbinput;
3211
	bool meta_key, func_key;
3212
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3213
	int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
3214
#endif
3215

3216
	kbinput = get_kbinput(edit, &meta_key, &func_key);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3217
3218

	if (kbinput == NANO_CANCEL_KEY)
3219
	    ok = -1;
3220
#ifndef DISABLE_MOUSE
3221
3222
	/* Look, ma!  We get to duplicate lots of code from
	 * do_mouse()!! */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3223
	else if (kbinput == KEY_MOUSE) {
3224
	    get_mouseinput(&mouse_x, &mouse_y, FALSE);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3225
3226

	    if (mouse_x != -1 && mouse_y != -1 && !ISSET(NO_HELP) &&
3227
3228
		wenclose(bottomwin, mouse_y, mouse_x) &&
		mouse_x < (width * 2) && mouse_y >= editwinrows + 3) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
		int x = mouse_x / width;
		    /* Did we click in the first column of shortcuts, or
		     * the second? */
		int y = mouse_y - editwinrows - 3;
		    /* Did we click in the first row of shortcuts? */

		assert(0 <= x && x <= 1 && 0 <= y && y <= 1);

		/* x = 0 means they clicked Yes or No.
		 * y = 0 means Yes or All. */
		ok = -2 * x * y + x - y + 1;

		if (ok == 2 && !all)
		    ok = -2;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
3244
	}
3245
#endif
3246
	/* Look for the kbinput in the yes, no and (optionally) all
3247
	 * strings. */
3248
3249
3250
3251
3252
3253
3254
3255
3256
	else if (strchr(yesstr, kbinput) != NULL)
	    ok = 1;
	else if (strchr(nostr, kbinput) != NULL)
	    ok = 0;
	else if (all && strchr(allstr, kbinput) != NULL)
	    ok = 2;
    } while (ok == -2);

    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
3257
3258
}

3259
void total_refresh(void)
3260
3261
{
    clearok(topwin, TRUE);
3262
    clearok(edit, TRUE);
3263
3264
    clearok(bottomwin, TRUE);
    wnoutrefresh(topwin);
3265
    wnoutrefresh(edit);
3266
3267
3268
    wnoutrefresh(bottomwin);
    doupdate();
    clearok(topwin, FALSE);
3269
    clearok(edit, FALSE);
3270
3271
    clearok(bottomwin, FALSE);
    titlebar(NULL);
3272
3273
    edit_refresh();
    /* FIXME: bottomwin needs to be refreshed too. */
3274
3275
3276
3277
3278
3279
3280
}

void display_main_list(void)
{
    bottombars(main_list);
}

3281
3282
3283
/* If constant is FALSE, the user typed Ctrl-C, so we unconditionally
 * display the cursor position.  Otherwise, we display it only if the
 * character position changed and DISABLE_CURPOS is not set.
3284
 *
3285
3286
3287
 * If constant is TRUE and DISABLE_CURPOS is set, we unset it and update
 * old_i and old_totsize.  That way, we leave the current statusbar
 * alone, but next time we will display. */
3288
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3289
{
3290
    const filestruct *fileptr;
3291
3292
    size_t i = 0;
    static size_t old_i = 0;
3293
    static long old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
3294

3295
    assert(current != NULL && fileage != NULL && totlines != 0);
3296
3297
3298
3299

    if (old_totsize == -1)
	old_totsize = totsize;

3300
3301
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
3302
	i += strlen(fileptr->data) + 1;
3303
    }
3304
    i += current_x;
3305

3306
3307
3308
3309
    /* Check whether totsize is correct.  Else there is a bug
     * somewhere. */
    assert(current != filebot || i == totsize);

3310
3311
3312
3313
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
3314
	return;
3315
    }
Chris Allegretta's avatar
Chris Allegretta committed
3316

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3317
    /* If constant is FALSE, display the position on the statusbar
3318
3319
     * unconditionally; otherwise, only display the position when the
     * character values have changed. */
3320
    if (!constant || old_i != i || old_totsize != totsize) {
3321
3322
	size_t xpt = xplustabs() + 1;
	size_t cur_len = strlenpt(current->data) + 1;
3323
3324
3325
3326
3327
3328
3329
	int linepct = 100 * current->lineno / totlines;
	int colpct = 100 * xpt / cur_len;
	int bytepct = totsize == 0 ? 0 : 100 * i / totsize;

	statusbar(
	    _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
		    current->lineno, totlines, linepct,
3330
		    (unsigned long)xpt, (unsigned long)cur_len, colpct,
3331
		    (unsigned long)i, totsize, bytepct);
3332
	UNSET(DISABLE_CURPOS);
3333
3334
3335
3336
    }

    old_i = i;
    old_totsize = totsize;
Chris Allegretta's avatar
Chris Allegretta committed
3337
3338
}

3339
void do_cursorpos_void(void)
3340
{
3341
    do_cursorpos(FALSE);
3342
3343
}

3344
#ifndef DISABLE_HELP
3345
/* Calculate the next line of help_text, starting at ptr. */
3346
int help_line_len(const char *ptr)
3347
{
3348
    int j = 0;
3349
3350
3351
3352
3353
3354
3355
3356
3357

    while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
	ptr++;
	j++;
    }
    if (j == COLS - 5) {
	/* Don't wrap at the first of two spaces following a period. */
	if (*ptr == ' ' && *(ptr + 1) == ' ')
	    j++;
3358
	/* Don't print half a word if we've run out of space. */
3359
3360
3361
3362
	while (*ptr != ' ' && j > 0) {
	    ptr--;
	    j--;
	}
3363
	/* Word longer than COLS - 5 chars just gets broken. */
3364
3365
3366
3367
3368
3369
3370
	if (j == 0)
	    j = COLS - 5;
    }
    assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
    return j;
}

3371
/* Our dynamic, shortcut-list-compliant help function. */
3372
void do_help(void)
Chris Allegretta's avatar
Chris Allegretta committed
3373
{
3374
3375
3376
    int line = 0;
	/* The line number in help_text of the first displayed help line.
	 * This variable is zero-based. */
3377
    bool no_more = FALSE;
3378
3379
	/* no_more means the end of the help text is shown, so don't go
	 * down any more. */
3380
    int kbinput = ERR;
3381
    bool meta_key, func_key;
3382

3383
    bool old_no_help = ISSET(NO_HELP);
3384
3385
3386
#ifndef DISABLE_MOUSE
    const shortcut *oldshortcut = currshortcut;
	/* We will set currshortcut to allow clicking on the help
3387
	 * screen's shortcut list. */
3388
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3389

3390
    curs_set(0);
Chris Allegretta's avatar
Chris Allegretta committed
3391
    blank_edit();
3392
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
3393
3394
    blank_statusbar();

3395
    /* Set help_text as the string to display. */
3396
    help_init();
3397
    assert(help_text != NULL);
3398

3399
#ifndef DISABLE_MOUSE
3400
    /* Set currshortcut to allow clicking on the help screen's shortcut
3401
     * list, AFTER help_init(). */
3402
    currshortcut = help_list;
3403
#endif
3404

Chris Allegretta's avatar
Chris Allegretta committed
3405
    if (ISSET(NO_HELP)) {
3406
3407
	/* Make sure that the help screen's shortcut list will actually
	 * be displayed. */
Chris Allegretta's avatar
Chris Allegretta committed
3408
	UNSET(NO_HELP);
3409
	window_init();
3410
3411
    }
    bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
3412
3413

    do {
3414
3415
3416
	int i;
	int old_line = line;
	    /* We redisplay the help only if it moved. */
3417
	const char *ptr = help_text;
3418

Chris Allegretta's avatar
Chris Allegretta committed
3419
	switch (kbinput) {
3420
#ifndef DISABLE_MOUSE
3421
	    case KEY_MOUSE:
3422
3423
3424
3425
		{
		    int mouse_x, mouse_y;
		    get_mouseinput(&mouse_x, &mouse_y, TRUE);
		}
3426
		break;
3427
#endif
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
	    case NANO_NEXTPAGE_KEY:
	    case NANO_NEXTPAGE_FKEY:
		if (!no_more)
		    line += editwinrows - 2;
		break;
	    case NANO_PREVPAGE_KEY:
	    case NANO_PREVPAGE_FKEY:
		if (line > 0) {
		    line -= editwinrows - 2;
		    if (line < 0)
			line = 0;
		}
		break;
	    case NANO_PREVLINE_KEY:
		if (line > 0)
		    line--;
		break;
	    case NANO_NEXTLINE_KEY:
		if (!no_more)
		    line++;
		break;
Chris Allegretta's avatar
Chris Allegretta committed
3449
3450
	}

3451
3452
3453
3454
3455
3456
3457
	if (line == old_line && kbinput != ERR)
	    goto skip_redisplay;

	blank_edit();

	assert(COLS > 5);

3458
3459
	/* Calculate where in the text we should be, based on the
	 * page. */
3460
	for (i = 0; i < line; i++) {
3461
	    ptr += help_line_len(ptr);
3462
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
3463
3464
3465
		ptr++;
	}

3466
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
3467
	    int j = help_line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
3468
3469

	    mvwaddnstr(edit, i, 0, ptr, j);
3470
3471
3472
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
3473
	}
3474
	no_more = (*ptr == '\0');
3475

3476
  skip_redisplay:
3477
	kbinput = get_kbinput(edit, &meta_key, &func_key);
3478
3479
    } while (kbinput != NANO_CANCEL_KEY && kbinput != NANO_EXIT_KEY &&
	kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
3480

3481
#ifndef DISABLE_MOUSE
3482
    currshortcut = oldshortcut;
3483
#endif
3484

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3485
    if (old_no_help) {
3486
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
3487
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
3488
	SET(NO_HELP);
3489
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
3490
    } else
3491
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
3492

3493
    curs_set(1);
Chris Allegretta's avatar
Chris Allegretta committed
3494
    edit_refresh();
3495

3496
3497
3498
    /* The help_init() at the beginning allocated help_text.  Since 
     * help_text has now been written to the screen, we don't need it
     * anymore. */
3499
3500
    free(help_text);
    help_text = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
3501
}
3502
#endif /* !DISABLE_HELP */
Chris Allegretta's avatar
Chris Allegretta committed
3503

3504
3505
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
Chris Allegretta's avatar
Chris Allegretta committed
3506
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3507
{
3508
    size_t y = xplustabs();
3509
    size_t word_len = strlen(word);
Chris Allegretta's avatar
Chris Allegretta committed
3510

3511
3512
3513
    y = get_page_start(y) + COLS - y;
	/* Now y is the number of characters we can display on this
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3514
3515

    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
3516

Chris Allegretta's avatar
Chris Allegretta committed
3517
3518
3519
    if (highlight_flag)
	wattron(edit, A_REVERSE);

3520
#ifdef HAVE_REGEX_H
3521
3522
3523
3524
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
	waddstr(edit, " ");
    else
3525
#endif
3526
	waddnstr(edit, word, y - 1);
3527
3528
3529
3530
3531

    if (word_len > y)
	waddch(edit, '$');
    else if (word_len == y)
	waddch(edit, word[word_len - 1]);
Chris Allegretta's avatar
Chris Allegretta committed
3532
3533
3534
3535
3536

    if (highlight_flag)
	wattroff(edit, A_REVERSE);
}

3537
#ifdef DEBUG
3538
3539
3540
/* Dump the passed-in file structure to stderr. */
void dump_buffer(const filestruct *inptr)
{
3541
    if (inptr == fileage)
3542
	fprintf(stderr, "Dumping file buffer to stderr...\n");
3543
    else if (inptr == cutbuffer)
3544
	fprintf(stderr, "Dumping cutbuffer to stderr...\n");
3545
    else
3546
	fprintf(stderr, "Dumping a buffer to stderr...\n");
3547
3548
3549
3550
3551
3552
3553

    while (inptr != NULL) {
	fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data);
	inptr = inptr->next;
    }
}

3554
/* Dump the file structure to stderr in reverse. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3555
3556
void dump_buffer_reverse(void)
{
3557
3558
3559
    const filestruct *fileptr = filebot;

    while (fileptr != NULL) {
3560
	fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
3561
3562
3563
3564
3565
	fileptr = fileptr->prev;
    }
}
#endif /* DEBUG */

3566
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3567
#define CREDIT_LEN 53
3568
3569
#define XLCREDIT_LEN 8

3570
/* Easter egg: Display credits.  Assume nodelay(edit) is FALSE. */
3571
3572
void do_credits(void)
{
3573
3574
3575
3576
    int crpos = 0, xlpos = 0;
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3577
3578
	VERSION,
	"",
3579
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3580
3581
3582
3583
3584
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3585
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3586
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
	"Ken Tyler",
	"Sven Guckes",
	"Florian König",
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3602
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
3603
3604
3605
3606
3607
3608
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3609
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3610
	"Linus Torvalds",
3611
	NULL,				/* "For ncurses:" */
3612
3613
3614
3615
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3616
3617
3618
3619
3620
3621
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3622
	"(c) 1999-2004 Chris Allegretta",
3623
3624
3625
3626
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3627
	"http://www.nano-editor.org/"
3628
3629
    };

3630
    const char *xlcredits[XLCREDIT_LEN] = {
3631
3632
3633
3634
3635
3636
3637
3638
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3639
    };
3640

3641
3642
    curs_set(0);
    nodelay(edit, TRUE);
3643
3644
    scrollok(edit, TRUE);
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
3645
    blank_edit();
3646
3647
3648
    blank_statusbar();
    blank_bottombars();
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3649
    wrefresh(edit);
3650
3651
    wrefresh(bottomwin);

3652
3653
3654
3655
3656
3657
3658
3659
3660
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
	if (wgetch(edit) != ERR)
	    break;
	if (crpos < CREDIT_LEN) {
	    const char *what = credits[crpos];
	    size_t start_x;

	    if (what == NULL) {
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3661
		what = _(xlcredits[xlpos]);
3662
		xlpos++;
Chris Allegretta's avatar
Chris Allegretta committed
3663
	    }
3664
	    start_x = COLS / 2 - strlen(what) / 2 - 1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3665
3666
	    mvwaddstr(edit, editwinrows - 1 - editwinrows % 2, start_x,
		what);
3667
	}
3668
3669
3670
3671
	napms(700);
	scroll(edit);
	wrefresh(edit);
	if (wgetch(edit) != ERR)
3672
	    break;
3673
3674
3675
	napms(700);
	scroll(edit);
	wrefresh(edit);
3676
3677
    }

3678
    scrollok(edit, FALSE);
3679
3680
3681
3682
    nodelay(edit, FALSE);
    curs_set(1);
    display_main_list();
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3683
}
3684
#endif