winio.c 102 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, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,  *
6
 *   2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.    *
Chris Allegretta's avatar
Chris Allegretta committed
7
8
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
9
 *   the Free Software Foundation; either version 3, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
10
11
 *   any later version.                                                   *
 *                                                                        *
12
13
14
15
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
Chris Allegretta's avatar
Chris Allegretta committed
16
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
19
20
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
21
22
23
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

26
#include <stdio.h>
Chris Allegretta's avatar
Chris Allegretta committed
27
28
#include <stdarg.h>
#include <string.h>
29
#include <unistd.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
30
#include <ctype.h>
Chris Allegretta's avatar
Chris Allegretta committed
31

32
static int *key_buffer = NULL;
33
34
	/* The keystroke buffer, containing all the keystrokes we
	 * haven't handled yet at a given point. */
35
static size_t key_buffer_len = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
36
	/* The length of the keystroke buffer. */
37
static int statusblank = 0;
38
39
	/* The number of keystrokes left after we call statusbar(),
	 * before we actually blank the statusbar. */
40
static bool disable_cursorpos = FALSE;
41
42
	/* Should we temporarily disable constant cursor position
	 * display? */
43
44
static bool seen_wide = FALSE;
	/* Whether we've seen a multicolumn character in the current line. */
45

46
#ifndef NANO_TINY
47
static sig_atomic_t sigwinch_counter_save = 0;
48
#endif
49

50
51
52
53
54
55
56
57
58
59
60
61
/* 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.
62
 * - NANO_CONTROL_8 is Ctrl-8 (Ctrl-?), which is Delete under ASCII,
63
64
 *   ANSI, VT100, and VT220, and which is Backspace under VT320.
 *
65
 * Note: VT220 and VT320 also generate Esc [ 3 ~ for Delete.  By
66
67
 * default, xterm assumes it's running on a VT320 and generates Ctrl-8
 * (Ctrl-?) for Backspace and Esc [ 3 ~ for Delete.  This causes
68
 * problems for VT100-derived terminals such as the FreeBSD console,
69
 * which expect Ctrl-H for Backspace and Ctrl-8 (Ctrl-?) for Delete, and
70
71
72
73
74
75
76
77
78
 * 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
79
80
81
 * console, the FreeBSD console, the Mach console, xterm, rxvt, Eterm,
 * and Terminal.  Among these, there are several conflicts and
 * omissions, outlined as follows:
82
83
84
85
86
87
 *
 * - 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
88
 *   keypad key, because the latter has no value when NumLock is off.)
89
90
91
92
 * - 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.)
93
 * - F9 on FreeBSD console == PageDown on Mach console; the former is
94
95
96
 *   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.)
97
 * - F10 on FreeBSD console == PageUp on Mach console; the former is
98
 *   omitted.  (Same as above.)
99
 * - F13 on FreeBSD console == End on Mach console; the former is
100
 *   omitted.  (Same as above.)
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
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
107
 *   omitted.  (Same as above.) */
108

109
/* Read in a sequence of keystrokes from win and save them in the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
110
111
 * keystroke buffer.  This should only be called when the keystroke
 * buffer is empty. */
112
void get_key_buffer(WINDOW *win)
113
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
114
115
    int input;
    size_t errcount;
116
117
118
119
120

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

121
122
123
124
    /* Just before reading in the first character, display any pending
     * screen updates. */
    doupdate();

125
    /* Read in the first character using whatever mode we're in. */
126
    errcount = 0;
127
    if (nodelay_mode) {
128
	if ((input = wgetch(win)) == ERR)
129
	    return;
130
    } else {
131
	while ((input = wgetch(win)) == ERR) {
132
133
134
135
136
137
138
139
140
#ifndef NANO_TINY
	    /* Did we get SIGWINCH since we were last here? */
	    if (sigwinch_counter != sigwinch_counter_save) {
		sigwinch_counter_save = sigwinch_counter;
		regenerate_screen();
		input = KEY_WINCH;
		break;
	    } else
#endif
141
142
143
144
145
146
147
148
149
150
	    errcount++;

	    /* If we've failed to get a character MAX_BUF_SIZE times in a
	     * row, assume that the input source we were using is gone and
	     * die gracefully.  We could check if errno is set to EIO
	     * ("Input/output error") and die gracefully in that case, but
	     * it's not always set properly.  Argh. */
	    if (errcount == MAX_BUF_SIZE)
		handle_hupterm(0);
	}
151
    }
152

153
154
    /* Increment the length of the keystroke buffer, and save the value
     * of the keystroke at the end of it. */
155
    key_buffer_len++;
156
157
    key_buffer = (int *)nmalloc(sizeof(int));
    key_buffer[0] = input;
158

159
160
161
162
163
164
165
#ifndef NANO_TINY
    /* If we got SIGWINCH, get out immediately since the win argument is
     * no longer valid. */
    if (input == KEY_WINCH)
	return;
#endif

166
167
168
169
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
170
	input = wgetch(win);
171

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

176
177
	/* Otherwise, increment the length of the keystroke buffer, and
	 * save the value of the keystroke at the end of it. */
178
	key_buffer_len++;
179
180
181
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
	key_buffer[key_buffer_len - 1] = input;
182
183
    }

184
    /* Switch back to waiting mode for input. */
185
    nodelay(win, FALSE);
186
187

#ifdef DEBUG
188
189
    {
	size_t i;
190
	fprintf(stderr, "\nget_key_buffer(): the sequence of hex codes:");
191
192
193
194
	for (i = 0; i < key_buffer_len; i++)
	    fprintf(stderr, " %3x", key_buffer[i]);
	fprintf(stderr, "\n");
    }
195
#endif
196
}
197

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
198
/* Return the length of the keystroke buffer. */
199
size_t get_key_buffer_len(void)
200
201
202
203
{
    return key_buffer_len;
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
204
/* Add the keystrokes in input to the keystroke buffer. */
205
void unget_input(int *input, size_t input_len)
206
207
{
    /* If input is empty, get out. */
208
    if (input_len == 0)
209
210
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
211
212
    /* If adding input would put the keystroke buffer beyond maximum
     * capacity, only add enough of input to put it at maximum
213
     * capacity. */
214
215
    if (key_buffer_len + input_len < key_buffer_len)
	input_len = (size_t)-1 - key_buffer_len;
216

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
217
218
219
    /* Add the length of input to the length of the keystroke buffer,
     * and reallocate the keystroke buffer so that it has enough room
     * for input. */
220
221
222
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
223

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
224
225
    /* If the keystroke buffer wasn't empty before, move its beginning
     * forward far enough so that we can add input to its beginning. */
226
227
228
    if (key_buffer_len > input_len)
	memmove(key_buffer + input_len, key_buffer,
		(key_buffer_len - input_len) * sizeof(int));
229

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
230
    /* Copy input to the beginning of the keystroke buffer. */
231
    memcpy(key_buffer, input, input_len * sizeof(int));
232
233
}

234
/* Put back the character stored in kbinput, putting it in byte range
235
236
 * beforehand.  If metakey is TRUE, put back the Escape character after
 * putting back kbinput.  If funckey is TRUE, put back the function key
237
 * (a value outside byte range) without putting it in byte range. */
238
void unget_kbinput(int kbinput, bool metakey, bool funckey)
239
{
240
    if (!funckey)
241
	kbinput = (char)kbinput;
242

243
    unget_input(&kbinput, 1);
244

245
    if (metakey) {
246
247
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
248
249
250
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
251
252
253
254
255
/* Try to read input_len characters from the keystroke buffer.  If the
 * keystroke buffer is empty and win isn't NULL, try to read in more
 * characters from win and add them to the keystroke buffer before doing
 * anything else.  If the keystroke buffer is empty and win is NULL,
 * return NULL. */
256
int *get_input(WINDOW *win, size_t input_len)
257
{
258
    int *input;
259
260

    if (key_buffer_len == 0) {
261
	if (win != NULL) {
262
	    get_key_buffer(win);
263

264
265
266
	    if (key_buffer_len == 0)
		return NULL;
	} else
267
268
269
	    return NULL;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
270
271
    /* If input_len is greater than the length of the keystroke buffer,
     * only read the number of characters in the keystroke buffer. */
272
273
274
    if (input_len > key_buffer_len)
	input_len = key_buffer_len;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
275
276
277
    /* Subtract input_len from the length of the keystroke buffer, and
     * allocate input so that it has enough room for input_len
     * keystrokes. */
278
    key_buffer_len -= input_len;
279
    input = (int *)nmalloc(input_len * sizeof(int));
280

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
281
282
    /* Copy input_len keystrokes from the beginning of the keystroke
     * buffer into input. */
283
    memcpy(input, key_buffer, input_len * sizeof(int));
284

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
285
    /* If the keystroke buffer is empty, mark it as such. */
286
287
288
    if (key_buffer_len == 0) {
	free(key_buffer);
	key_buffer = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
289
290
291
    /* If the keystroke buffer isn't empty, move its beginning forward
     * far enough so that the keystrokes in input are no longer at its
     * beginning. */
292
293
    } else {
	memmove(key_buffer, key_buffer + input_len, key_buffer_len *
294
295
296
		sizeof(int));
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
297
298
299
    }

    return input;
300
301
}

302
303
/* 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
304
305
 * sequences, and/or extended keypad values.  Supported extended keypad
 * values consist of
306
307
308
 * [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
309
 * off. */
310
int get_kbinput(WINDOW *win)
311
312
313
314
315
{
    int kbinput;

    /* Read in a character and interpret it.  Continue doing this until
     * we get a recognized value or sequence. */
316
    while ((kbinput = parse_kbinput(win)) == ERR)
317
	;
318

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
319
320
321
    /* If we read from the edit window, blank the statusbar if we need
     * to. */
    if (win == edit)
322
323
	check_statusblank();

324
325
326
327
328
329
    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
330
 * a function key. */
331
int parse_kbinput(WINDOW *win)
332
{
333
    static int escapes = 0, byte_digits = 0;
334
    int *kbinput, retval = ERR;
335

336
337
    meta_key = FALSE;
    func_key = FALSE;
338

339
    /* Read in a character. */
340
341
342
343
344
    if (nodelay_mode) {
	kbinput = get_input(win, 1);
	if (kbinput == 0)
	    return 0;
    } else
345
346
	while ((kbinput = get_input(win, 1)) == NULL)
	    ;
347

348
349
350
351
352
353
354
355
356
357
358
    switch (*kbinput) {
	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. */
359
360
		case 3:
		    /* Three escapes: wait for more input. */
361
362
		    break;
		default:
363
364
365
		    /* More than three escapes: limit the escape counter
		     * to no more than two, and wait for more input. */
		    escapes %= 3;
366
367
368
369
370
	    }
	    break;
	default:
	    switch (escapes) {
		case 0:
371
372
373
		    /* One non-escape: normal input mode.  Save the
		     * non-escape character as the result. */
		    retval = *kbinput;
374
375
		    break;
		case 1:
376
		    /* Reset the escape counter. */
377
		    escapes = 0;
378
		    if (get_key_buffer_len() == 0) {
379
			/* One escape followed by a non-escape, and
380
381
382
383
			 * there aren't any other keystrokes waiting:
			 * meta key sequence mode.  Set meta_key to
			 * TRUE, and save the lowercase version of the
			 * non-escape character as the result. */
384
			meta_key = TRUE;
385
			retval = tolower(*kbinput);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
386
		    } else
387
			/* One escape followed by a non-escape, and
388
389
390
			 * there are other keystrokes waiting: escape
			 * sequence mode.  Interpret the escape
			 * sequence. */
391
			retval = parse_escape_sequence(win, *kbinput);
392
393
		    break;
		case 2:
394
395
396
397
398
399
		    if (get_key_buffer_len() == 0) {
			if (('0' <= *kbinput && *kbinput <= '2' &&
				byte_digits == 0) || ('0' <= *kbinput &&
				*kbinput <= '9' && byte_digits > 0)) {
			    /* Two escapes followed by one or more
			     * decimal digits, and there aren't any
400
401
402
403
404
405
406
407
408
			     * other keystrokes waiting: byte sequence
			     * mode.  If the byte sequence's range is
			     * limited to 2XX (the first digit is in the
			     * '0' to '2' range and it's the first
			     * digit, or it's in the '0' to '9' range
			     * and it's not the first digit), increment
			     * the byte sequence counter and interpret
			     * the digit.  If the byte sequence's range
			     * is not limited to 2XX, fall through. */
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
			    int byte;

			    byte_digits++;
			    byte = get_byte_kbinput(*kbinput);

			    if (byte != ERR) {
				char *byte_mb;
				int byte_mb_len, *seq, i;

				/* If we've read in a complete byte
				 * sequence, reset the escape counter
				 * and the byte sequence counter, and
				 * put back the corresponding byte
				 * value. */
				escapes = 0;
				byte_digits = 0;

				/* Put back the multibyte equivalent of
				 * the byte value. */
				byte_mb = make_mbchar((long)byte,
					&byte_mb_len);

				seq = (int *)nmalloc(byte_mb_len *
					sizeof(int));

				for (i = 0; i < byte_mb_len; i++)
				    seq[i] = (unsigned char)byte_mb[i];

				unget_input(seq, byte_mb_len);

				free(byte_mb);
				free(seq);
			    }
			} else {
			    /* Reset the escape counter. */
444
			    escapes = 0;
445
446
447
448
449
450
			    if (byte_digits == 0)
				/* Two escapes followed by a non-decimal
				 * digit or a decimal digit that would
				 * create a byte sequence greater than
				 * 2XX, we're not in the middle of a
				 * byte sequence, and there aren't any
451
452
453
454
455
				 * other keystrokes waiting: control
				 * character sequence mode.  Interpret
				 * the control sequence and save the
				 * corresponding control character as
				 * the result. */
456
457
458
459
460
461
462
463
464
				retval = get_control_kbinput(*kbinput);
			    else {
				/* If we're in the middle of a byte
				 * sequence, reset the byte sequence
				 * counter and save the character we got
				 * as the result. */
				byte_digits = 0;
				retval = *kbinput;
			    }
465
			}
466
		    } else {
467
			/* Two escapes followed by a non-escape, and
468
469
470
471
			 * there are other keystrokes waiting: combined
			 * meta and escape sequence mode.  Reset the
			 * escape counter, set meta_key to TRUE, and
			 * interpret the escape sequence. */
472
			escapes = 0;
473
			meta_key = TRUE;
474
			retval = parse_escape_sequence(win, *kbinput);
475
		    }
476
		    break;
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
		case 3:
		    /* Reset the escape counter. */
		    escapes = 0;
		    if (get_key_buffer_len() == 0)
			/* Three escapes followed by a non-escape, and
			 * there aren't any other keystrokes waiting:
			 * normal input mode.  Save the non-escape
			 * character as the result. */
			retval = *kbinput;
		    else
			/* Three escapes followed by a non-escape, and
			 * there are other keystrokes waiting: combined
			 * control character and escape sequence mode.
			 * Interpret the escape sequence, and interpret
			 * the result as a control sequence. */
			retval = get_control_kbinput(
493
				parse_escape_sequence(win, *kbinput));
494
		    break;
495
496
	    }
    }
497

498
    if (retval != ERR) {
499
500
	switch (retval) {
	    case NANO_CONTROL_8:
501
502
		retval = ISSET(REBIND_DELETE) ? sc_seq_or(do_delete, 0) :
			sc_seq_or(do_backspace, 0);
503
504
		break;
	    case KEY_DOWN:
505
506
507
508
#ifdef KEY_SDOWN
	    /* ncurses and Slang don't support KEY_SDOWN. */
	    case KEY_SDOWN:
#endif
509
		retval = sc_seq_or(do_down_void, *kbinput);
510
511
		break;
	    case KEY_UP:
512
513
514
515
#ifdef KEY_SUP
	    /* ncurses and Slang don't support KEY_SUP. */
	    case KEY_SUP:
#endif
516
		retval = sc_seq_or(do_up_void, *kbinput);
517
518
		break;
	    case KEY_LEFT:
519
520
521
522
#ifdef KEY_SLEFT
	    /* Slang doesn't support KEY_SLEFT. */
	    case KEY_SLEFT:
#endif
523
		retval = sc_seq_or(do_left, *kbinput);
524
525
		break;
	    case KEY_RIGHT:
526
527
528
529
#ifdef KEY_SRIGHT
	    /* Slang doesn't support KEY_SRIGHT. */
	    case KEY_SRIGHT:
#endif
530
		retval = sc_seq_or(do_right, *kbinput);
531
		break;
532
533
534
535
536
537
#ifdef KEY_SHOME
	    /* HP-UX 10-11 and Slang don't support KEY_SHOME. */
	    case KEY_SHOME:
#endif
	    case KEY_A1:	/* Home (7) on numeric keypad with
				 * NumLock off. */
538
		retval = sc_seq_or(do_home, *kbinput);
539
		break;
540
	    case KEY_BACKSPACE:
541
		retval = sc_seq_or(do_backspace, *kbinput);
542
		break;
543
544
545
546
#ifdef KEY_SDC
	    /* Slang doesn't support KEY_SDC. */
	    case KEY_SDC:
		if (ISSET(REBIND_DELETE))
547
		    retval = sc_seq_or(do_delete, *kbinput);
548
		else
549
		    retval = sc_seq_or(do_backspace, *kbinput);
550
		break;
551
#endif
552
553
554
#ifdef KEY_SIC
	    /* Slang doesn't support KEY_SIC. */
	    case KEY_SIC:
555
		retval = sc_seq_or(do_insertfile_void, *kbinput);
556
		break;
557
#endif
558
	    case KEY_C3:	/* PageDown (4) on numeric keypad with
559
				 * NumLock off. */
560
		retval = sc_seq_or(do_page_down, *kbinput);
561
562
563
		break;
	    case KEY_A3:	/* PageUp (9) on numeric keypad with
				 * NumLock off. */
564
		retval = sc_seq_or(do_page_up, *kbinput);
565
566
		break;
	    case KEY_ENTER:
567
		retval = sc_seq_or(do_enter, *kbinput);
568
569
570
571
572
573
574
		break;
	    case KEY_B2:	/* Center (5) on numeric keypad with
				 * NumLock off. */
		retval = ERR;
		break;
	    case KEY_C1:	/* End (1) on numeric keypad with
				 * NumLock off. */
575
576
577
578
#ifdef KEY_SEND
	    /* HP-UX 10-11 and Slang don't support KEY_SEND. */
	    case KEY_SEND:
#endif
579
		retval = sc_seq_or(do_end, *kbinput);
580
581
582
583
584
585
586
587
		break;
#ifdef KEY_BEG
	    /* Slang doesn't support KEY_BEG. */
	    case KEY_BEG:	/* Center (5) on numeric keypad with
				 * NumLock off. */
		retval = ERR;
		break;
#endif
588
589
590
#ifdef KEY_CANCEL
	    /* Slang doesn't support KEY_CANCEL. */
	    case KEY_CANCEL:
591
592
593
#ifdef KEY_SCANCEL
	    /* Slang doesn't support KEY_SCANCEL. */
	    case KEY_SCANCEL:
594
#endif
595
		retval = first_sc_for(currmenu, do_cancel)->seq;
596
597
598
599
600
601
602
603
604
605
606
607
		break;
#endif
#ifdef KEY_SBEG
	    /* Slang doesn't support KEY_SBEG. */
	    case KEY_SBEG:	/* Center (5) on numeric keypad with
				 * NumLock off. */
		retval = ERR;
		break;
#endif
#ifdef KEY_SSUSPEND
	    /* Slang doesn't support KEY_SSUSPEND. */
	    case KEY_SSUSPEND:
608
		retval = sc_seq_or(do_suspend_void, 0);
609
610
611
612
613
		break;
#endif
#ifdef KEY_SUSPEND
	    /* Slang doesn't support KEY_SUSPEND. */
	    case KEY_SUSPEND:
614
		retval = sc_seq_or(do_suspend_void, 0);
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
		break;
#endif
#ifdef PDCURSES
	    case KEY_SHIFT_L:
	    case KEY_SHIFT_R:
	    case KEY_CONTROL_L:
	    case KEY_CONTROL_R:
	    case KEY_ALT_L:
	    case KEY_ALT_R:
		retval = ERR;
		break;
#endif
#if !defined(NANO_TINY) && defined(KEY_RESIZE)
	    /* Since we don't change the default SIGWINCH handler when
	     * NANO_TINY is defined, KEY_RESIZE is never generated.
	     * Also, Slang and SunOS 5.7-5.9 don't support
	     * KEY_RESIZE. */
	    case KEY_RESIZE:
		retval = ERR;
		break;
#endif
636
637
638
639
640
#ifndef NANO_TINY
	    case KEY_WINCH:
		retval = KEY_WINCH;
		break;
#endif
641
	}
642

643
644
645
646
647
648
649
#ifndef NANO_TINY
	if (retval == controlleft)
	    retval = sc_seq_or(do_prev_word_void, 0);
	else if (retval == controlright)
	    retval = sc_seq_or(do_next_word_void, 0);
#endif

650
	/* If our result is an extended keypad value (i.e. a value
651
	 * outside of byte range), set func_key to TRUE. */
652
	if (retval != ERR)
653
	    func_key = !is_byte(retval);
654
    }
655
656

#ifdef DEBUG
657
    fprintf(stderr, "parse_kbinput(): kbinput = %d, meta_key = %s, func_key = %s, escapes = %d, byte_digits = %d, retval = %d\n", *kbinput, meta_key ? "TRUE" : "FALSE", func_key ? "TRUE" : "FALSE", escapes, byte_digits, retval);
658
659
#endif

660
661
    free(kbinput);

662
    /* Return the result. */
663
664
665
    return retval;
}

666
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
667
 * keypad values, into their corresponding key values.  These sequences
668
669
 * are generated when the keypad doesn't support the needed keys.
 * Assume that Escape has already been read in. */
670
int convert_sequence(const int *seq, size_t seq_len)
671
{
672
    if (seq_len > 1) {
673
	switch (seq[0]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
674
	    case 'O':
675
		switch (seq[1]) {
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
		    case '1':
			if (seq_len >= 3) {
			    switch (seq[2]) {
				case ';':
    if (seq_len >= 4) {
	switch (seq[3]) {
	    case '2':
		if (seq_len >= 5) {
		    switch (seq[4]) {
			case 'A': /* Esc O 1 ; 2 A == Shift-Up on
				   * Terminal. */
			case 'B': /* Esc O 1 ; 2 B == Shift-Down on
				   * Terminal. */
			case 'C': /* Esc O 1 ; 2 C == Shift-Right on
				   * Terminal. */
			case 'D': /* Esc O 1 ; 2 D == Shift-Left on
				   * Terminal. */
693
			    return arrow_from_abcd(seq[4]);
694
			case 'P': /* Esc O 1 ; 2 P == F13 on Terminal. */
695
			    return KEY_F(13);
696
			case 'Q': /* Esc O 1 ; 2 Q == F14 on Terminal. */
697
			    return KEY_F(14);
698
			case 'R': /* Esc O 1 ; 2 R == F15 on Terminal. */
699
			    return KEY_F(15);
700
			case 'S': /* Esc O 1 ; 2 S == F16 on Terminal. */
701
			    return KEY_F(16);
702
703
704
705
706
707
		    }
		}
		break;
	    case '5':
		if (seq_len >= 5) {
		    switch (seq[4]) {
708
709
			case 'A': /* Esc O 1 ; 5 A == Ctrl-Up on Terminal. */
			case 'B': /* Esc O 1 ; 5 B == Ctrl-Down on Terminal. */
710
			    return arrow_from_abcd(seq[4]);
711
			case 'C': /* Esc O 1 ; 5 C == Ctrl-Right on Terminal. */
712
			    return CONTROL_RIGHT;
713
			case 'D': /* Esc O 1 ; 5 D == Ctrl-Left on Terminal. */
714
			    return CONTROL_LEFT;
715
716
717
718
719
720
721
722
723
		    }
		}
		break;
	}
    }
				    break;
			    }
			}
			break;
724
		    case '2':
725
			if (seq_len >= 3) {
726
			    switch (seq[2]) {
727
				case 'P': /* Esc O 2 P == F13 on xterm. */
728
				    return KEY_F(13);
729
				case 'Q': /* Esc O 2 Q == F14 on xterm. */
730
				    return KEY_F(14);
731
				case 'R': /* Esc O 2 R == F15 on xterm. */
732
				    return KEY_F(15);
733
				case 'S': /* Esc O 2 S == F16 on xterm. */
734
				    return KEY_F(16);
735
736
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
737
			break;
738
		    case 'A': /* Esc O A == Up on VT100/VT320/xterm. */
739
740
741
		    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. */
742
			return arrow_from_abcd(seq[1]);
743
744
		    case 'E': /* Esc O E == Center (5) on numeric keypad
			       * with NumLock off on xterm. */
745
			return KEY_B2;
746
		    case 'F': /* Esc O F == End on xterm/Terminal. */
747
			return sc_seq_or(do_end, 0);
748
		    case 'H': /* Esc O H == Home on xterm/Terminal. */
749
			return sc_seq_or(do_home, 0);
750
		    case 'M': /* Esc O M == Enter on numeric keypad with
751
			       * NumLock off on VT100/VT220/VT320/xterm/
752
			       * rxvt/Eterm. */
753
			return sc_seq_or(do_home, 0);
754
		    case 'P': /* Esc O P == F1 on VT100/VT220/VT320/Mach
755
			       * console. */
756
			return KEY_F(1);
757
		    case 'Q': /* Esc O Q == F2 on VT100/VT220/VT320/Mach
758
			       * console. */
759
			return KEY_F(2);
760
		    case 'R': /* Esc O R == F3 on VT100/VT220/VT320/Mach
761
			       * console. */
762
			return KEY_F(3);
763
		    case 'S': /* Esc O S == F4 on VT100/VT220/VT320/Mach
764
			       * console. */
765
			return KEY_F(4);
766
		    case 'T': /* Esc O T == F5 on Mach console. */
767
			return KEY_F(5);
768
		    case 'U': /* Esc O U == F6 on Mach console. */
769
			return KEY_F(6);
770
		    case 'V': /* Esc O V == F7 on Mach console. */
771
			return KEY_F(7);
772
		    case 'W': /* Esc O W == F8 on Mach console. */
773
			return KEY_F(8);
774
		    case 'X': /* Esc O X == F9 on Mach console. */
775
			return KEY_F(9);
776
		    case 'Y': /* Esc O Y == F10 on Mach console. */
777
			return KEY_F(10);
778
779
		    case 'a': /* Esc O a == Ctrl-Up on rxvt. */
		    case 'b': /* Esc O b == Ctrl-Down on rxvt. */
780
			return arrow_from_abcd(seq[1]);
781
		    case 'c': /* Esc O c == Ctrl-Right on rxvt. */
782
			return CONTROL_RIGHT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
783
		    case 'd': /* Esc O d == Ctrl-Left on rxvt. */
784
			return CONTROL_LEFT;
785
		    case 'j': /* Esc O j == '*' on numeric keypad with
786
			       * NumLock off on VT100/VT220/VT320/xterm/
787
			       * rxvt/Eterm/Terminal. */
788
			return '*';
789
		    case 'k': /* Esc O k == '+' on numeric keypad with
790
			       * NumLock off on VT100/VT220/VT320/xterm/
791
			       * rxvt/Eterm/Terminal. */
792
			return '+';
793
		    case 'l': /* Esc O l == ',' on numeric keypad with
794
			       * NumLock off on VT100/VT220/VT320/xterm/
795
			       * rxvt/Eterm/Terminal. */
796
			return ',';
797
		    case 'm': /* Esc O m == '-' on numeric keypad with
798
			       * NumLock off on VT100/VT220/VT320/xterm/
799
			       * rxvt/Eterm/Terminal. */
800
			return '-';
801
		    case 'n': /* Esc O n == Delete (.) on numeric keypad
802
			       * with NumLock off on VT100/VT220/VT320/
803
			       * xterm/rxvt/Eterm/Terminal. */
804
			return sc_seq_or(do_delete, 0);
805
		    case 'o': /* Esc O o == '/' on numeric keypad with
806
			       * NumLock off on VT100/VT220/VT320/xterm/
807
			       * rxvt/Eterm/Terminal. */
808
			return '/';
809
		    case 'p': /* Esc O p == Insert (0) on numeric keypad
810
			       * with NumLock off on VT100/VT220/VT320/
811
			       * rxvt/Eterm/Terminal. */
812
			return sc_seq_or(do_insertfile_void, 0);
813
		    case 'q': /* Esc O q == End (1) on numeric keypad
814
			       * with NumLock off on VT100/VT220/VT320/
815
			       * rxvt/Eterm/Terminal. */
816
			return sc_seq_or(do_end, 0);
817
		    case 'r': /* Esc O r == Down (2) on numeric keypad
818
			       * with NumLock off on VT100/VT220/VT320/
819
			       * rxvt/Eterm/Terminal. */
820
			return sc_seq_or(do_down_void, 0);
821
		    case 's': /* Esc O s == PageDown (3) on numeric
822
			       * keypad with NumLock off on VT100/VT220/
823
			       * VT320/rxvt/Eterm/Terminal. */
824
			return sc_seq_or(do_page_down, 0);
825
		    case 't': /* Esc O t == Left (4) on numeric keypad
826
			       * with NumLock off on VT100/VT220/VT320/
827
			       * rxvt/Eterm/Terminal. */
828
			return sc_seq_or(do_left, 0);
829
		    case 'u': /* Esc O u == Center (5) on numeric keypad
830
831
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt/Eterm. */
832
			return KEY_B2;
833
		    case 'v': /* Esc O v == Right (6) on numeric keypad
834
			       * with NumLock off on VT100/VT220/VT320/
835
			       * rxvt/Eterm/Terminal. */
836
			return sc_seq_or(do_right, 0);
837
		    case 'w': /* Esc O w == Home (7) on numeric keypad
838
			       * with NumLock off on VT100/VT220/VT320/
839
			       * rxvt/Eterm/Terminal. */
840
			return sc_seq_or(do_home, 0);
841
		    case 'x': /* Esc O x == Up (8) on numeric keypad
842
			       * with NumLock off on VT100/VT220/VT320/
843
			       * rxvt/Eterm/Terminal. */
844
			return sc_seq_or(do_up_void, 0);
845
		    case 'y': /* Esc O y == PageUp (9) on numeric keypad
846
			       * with NumLock off on VT100/VT220/VT320/
847
			       * rxvt/Eterm/Terminal. */
848
			return sc_seq_or(do_page_up, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
849
850
851
		}
		break;
	    case 'o':
852
		switch (seq[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
853
854
		    case 'a': /* Esc o a == Ctrl-Up on Eterm. */
		    case 'b': /* Esc o b == Ctrl-Down on Eterm. */
855
			return arrow_from_abcd(seq[1]);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
856
		    case 'c': /* Esc o c == Ctrl-Right on Eterm. */
857
			return CONTROL_RIGHT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
858
		    case 'd': /* Esc o d == Ctrl-Left on Eterm. */
859
			return CONTROL_LEFT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
860
861
862
		}
		break;
	    case '[':
863
		switch (seq[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
864
		    case '1':
865
			if (seq_len >= 3) {
866
			    switch (seq[2]) {
867
				case '1': /* Esc [ 1 1 ~ == F1 on rxvt/Eterm. */
868
				    return KEY_F(1);
869
				case '2': /* Esc [ 1 2 ~ == F2 on rxvt/Eterm. */
870
				    return KEY_F(2);
871
				case '3': /* Esc [ 1 3 ~ == F3 on rxvt/Eterm. */
872
				    return KEY_F(3);
873
				case '4': /* Esc [ 1 4 ~ == F4 on rxvt/Eterm. */
874
				    return KEY_F(4);
875
876
				case '5': /* Esc [ 1 5 ~ == F5 on xterm/
					   * rxvt/Eterm. */
877
				    return KEY_F(5);
878
				case '7': /* Esc [ 1 7 ~ == F6 on
879
880
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
881
				    return KEY_F(6);
882
				case '8': /* Esc [ 1 8 ~ == F7 on
883
884
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
885
				    return KEY_F(7);
886
				case '9': /* Esc [ 1 9 ~ == F8 on
887
888
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
889
				    return KEY_F(8);
890
				case ';':
891
    if (seq_len >= 4) {
892
	switch (seq[3]) {
893
	    case '2':
894
		if (seq_len >= 5) {
895
		    switch (seq[4]) {
896
897
898
899
			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. */
900
			    return arrow_from_abcd(seq[4]);
901
902
903
904
		    }
		}
		break;
	    case '5':
905
		if (seq_len >= 5) {
906
		    switch (seq[4]) {
907
908
			case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on xterm. */
			case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on xterm. */
909
			    return arrow_from_abcd(seq[4]);
910
			case 'C': /* Esc [ 1 ; 5 C == Ctrl-Right on xterm. */
911
			    return CONTROL_RIGHT;
912
			case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on xterm. */
913
			    return CONTROL_LEFT;
914
915
916
917
918
919
		    }
		}
		break;
	}
    }
				    break;
920
921
				default: /* Esc [ 1 ~ == Home on
					  * VT320/Linux console. */
922
				    return sc_seq_or(do_home, 0);
923
924
925
926
			    }
			}
			break;
		    case '2':
927
			if (seq_len >= 3) {
928
			    switch (seq[2]) {
929
930
				case '0': /* Esc [ 2 0 ~ == F9 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
931
				    return KEY_F(9);
932
933
				case '1': /* Esc [ 2 1 ~ == F10 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
934
				    return KEY_F(10);
935
936
				case '3': /* Esc [ 2 3 ~ == F11 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
937
				    return KEY_F(11);
938
939
				case '4': /* Esc [ 2 4 ~ == F12 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
940
				    return KEY_F(12);
941
942
				case '5': /* Esc [ 2 5 ~ == F13 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
943
				    return KEY_F(13);
944
945
				case '6': /* Esc [ 2 6 ~ == F14 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
946
				    return KEY_F(14);
947
948
				case '8': /* Esc [ 2 8 ~ == F15 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
949
				    return KEY_F(15);
950
951
				case '9': /* Esc [ 2 9 ~ == F16 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
952
				    return KEY_F(16);
953
954
				default: /* Esc [ 2 ~ == Insert on VT220/VT320/
					  * Linux console/xterm/Terminal. */
955
				    return sc_seq_or(do_insertfile_void, 0);
956
			    }
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
957
958
			}
			break;
959
		    case '3': /* Esc [ 3 ~ == Delete on VT220/VT320/
960
			       * Linux console/xterm/Terminal. */
961
			return sc_seq_or(do_delete, 0);
962
		    case '4': /* Esc [ 4 ~ == End on VT220/VT320/Linux
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
963
			       * console/xterm. */
964
			return sc_seq_or(do_end, 0);
965
		    case '5': /* Esc [ 5 ~ == PageUp on VT220/VT320/
966
967
			       * Linux console/xterm/Terminal;
			       * Esc [ 5 ^ == PageUp on Eterm. */
968
			return sc_seq_or(do_page_up, 0);
969
		    case '6': /* Esc [ 6 ~ == PageDown on VT220/VT320/
970
			       * Linux console/xterm/Terminal;
971
			       * Esc [ 6 ^ == PageDown on Eterm. */
972
			return sc_seq_or(do_page_down, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
973
		    case '7': /* Esc [ 7 ~ == Home on rxvt. */
974
			return sc_seq_or(do_home, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
975
		    case '8': /* Esc [ 8 ~ == End on rxvt. */
976
			return sc_seq_or(do_end, 0);
977
		    case '9': /* Esc [ 9 == Delete on Mach console. */
978
			return sc_seq_or(do_delete, 0);
979
		    case '@': /* Esc [ @ == Insert on Mach console. */
980
			return sc_seq_or(do_insertfile_void, 0);
981
		    case 'A': /* Esc [ A == Up on ANSI/VT220/Linux
982
			       * console/FreeBSD console/Mach console/
983
			       * rxvt/Eterm/Terminal. */
984
		    case 'B': /* Esc [ B == Down on ANSI/VT220/Linux
985
			       * console/FreeBSD console/Mach console/
986
			       * rxvt/Eterm/Terminal. */
987
		    case 'C': /* Esc [ C == Right on ANSI/VT220/Linux
988
			       * console/FreeBSD console/Mach console/
989
			       * rxvt/Eterm/Terminal. */
990
		    case 'D': /* Esc [ D == Left on ANSI/VT220/Linux
991
			       * console/FreeBSD console/Mach console/
992
			       * rxvt/Eterm/Terminal. */
993
			return arrow_from_abcd(seq[1]);
994
		    case 'E': /* Esc [ E == Center (5) on numeric keypad
995
996
			       * with NumLock off on FreeBSD console/
			       * Terminal. */
997
			return KEY_B2;
998
		    case 'F': /* Esc [ F == End on FreeBSD console/Eterm. */
999
			return sc_seq_or(do_end, 0);
1000
		    case 'G': /* Esc [ G == PageDown on FreeBSD console. */
1001
			return sc_seq_or(do_page_down, 0);
1002
		    case 'H': /* Esc [ H == Home on ANSI/VT220/FreeBSD
1003
			       * console/Mach console/Eterm. */
1004
			return sc_seq_or(do_home, 0);
1005
		    case 'I': /* Esc [ I == PageUp on FreeBSD console. */
1006
			return sc_seq_or(do_page_up, 0);
1007
		    case 'L': /* Esc [ L == Insert on ANSI/FreeBSD console. */
1008
			return sc_seq_or(do_insertfile_void, 0);
1009
		    case 'M': /* Esc [ M == F1 on FreeBSD console. */
1010
			return KEY_F(1);
1011
		    case 'N': /* Esc [ N == F2 on FreeBSD console. */
1012
			return KEY_F(2);
1013
		    case 'O':
1014
			if (seq_len >= 3) {
1015
			    switch (seq[2]) {
1016
				case 'P': /* Esc [ O P == F1 on xterm. */
1017
				    return KEY_F(1);
1018
				case 'Q': /* Esc [ O Q == F2 on xterm. */
1019
				    return KEY_F(2);
1020
				case 'R': /* Esc [ O R == F3 on xterm. */
1021
				    return KEY_F(3);
1022
				case 'S': /* Esc [ O S == F4 on xterm. */
1023
				    return KEY_F(4);
1024
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1025
			} else
1026
			    /* Esc [ O == F3 on FreeBSD console. */
1027
			    return KEY_F(3);
1028
1029
			break;
		    case 'P': /* Esc [ P == F4 on FreeBSD console. */
1030
			return KEY_F(4);
1031
		    case 'Q': /* Esc [ Q == F5 on FreeBSD console. */
1032
			return KEY_F(5);
1033
		    case 'R': /* Esc [ R == F6 on FreeBSD console. */
1034
			return KEY_F(6);
1035
		    case 'S': /* Esc [ S == F7 on FreeBSD console. */
1036
			return KEY_F(7);
1037
		    case 'T': /* Esc [ T == F8 on FreeBSD console. */
1038
			return KEY_F(8);
1039
		    case 'U': /* Esc [ U == PageDown on Mach console. */
1040
			return sc_seq_or(do_page_down, 0);
1041
		    case 'V': /* Esc [ V == PageUp on Mach console. */
1042
			return sc_seq_or(do_page_up, 0);
1043
		    case 'W': /* Esc [ W == F11 on FreeBSD console. */
1044
			return KEY_F(11);
1045
		    case 'X': /* Esc [ X == F12 on FreeBSD console. */
1046
			return KEY_F(12);
1047
		    case 'Y': /* Esc [ Y == End on Mach console. */
1048
			return sc_seq_or(do_end, 0);
1049
		    case 'Z': /* Esc [ Z == F14 on FreeBSD console. */
1050
			return KEY_F(14);
1051
		    case 'a': /* Esc [ a == Shift-Up on rxvt/Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1052
		    case 'b': /* Esc [ b == Shift-Down on rxvt/Eterm. */
1053
		    case 'c': /* Esc [ c == Shift-Right on rxvt/Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1054
		    case 'd': /* Esc [ d == Shift-Left on rxvt/Eterm. */
1055
			return arrow_from_abcd(seq[1]);
1056
		    case '[':
1057
			if (seq_len >= 3) {
1058
			    switch (seq[2]) {
1059
1060
				case 'A': /* Esc [ [ A == F1 on Linux
					   * console. */
1061
				    return KEY_F(1);
1062
1063
				case 'B': /* Esc [ [ B == F2 on Linux
					   * console. */
1064
				    return KEY_F(2);
1065
1066
				case 'C': /* Esc [ [ C == F3 on Linux
					   * console. */
1067
				    return KEY_F(3);
1068
1069
				case 'D': /* Esc [ [ D == F4 on Linux
					   * console. */
1070
				    return KEY_F(4);
1071
1072
				case 'E': /* Esc [ [ E == F5 on Linux
					   * console. */
1073
				    return KEY_F(5);
1074
1075
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1076
1077
1078
1079
			break;
		}
		break;
	}
1080
1081
    }

1082
    return ERR;
1083
1084
}

1085
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1086
 * letters A (up), B (down), C (right), and D (left).  These are common
1087
 * to many escape sequences. */
1088
int arrow_from_abcd(int kbinput)
1089
1090
1091
{
    switch (tolower(kbinput)) {
	case 'a':
1092
	    return sc_seq_or(do_up_void, 0);
1093
	case 'b':
1094
	    return sc_seq_or(do_down_void, 0);
1095
	case 'c':
1096
	    return sc_seq_or(do_right, 0);
1097
	case 'd':
1098
	    return sc_seq_or(do_left, 0);
1099
1100
1101
1102
1103
	default:
	    return ERR;
    }
}

1104
/* Interpret the escape sequence in the keystroke buffer, the first
1105
1106
 * character of which is kbinput.  Assume that the keystroke buffer
 * isn't empty, and that the initial escape has already been read in. */
1107
int parse_escape_sequence(WINDOW *win, int kbinput)
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
{
    int retval, *seq;
    size_t seq_len;

    /* Put back the non-escape character, get the complete escape
     * sequence, translate the sequence into its corresponding key
     * value, and save that as the result. */
    unget_input(&kbinput, 1);
    seq_len = get_key_buffer_len();
    seq = get_input(NULL, seq_len);
1118
    retval = convert_sequence(seq, seq_len);
1119
1120
1121

    free(seq);

1122
    /* If we got an unrecognized escape sequence, notify the user. */
1123
    if (retval == ERR) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1124
	if (win == edit) {
1125
	    statusbar(_("Unknown Command"));
1126
	    curs_set(1);
1127
1128
1129
1130
	    beep();
	}
    }

1131
#ifdef DEBUG
1132
1133
    fprintf(stderr, "parse_escape_sequence(): kbinput = %d, seq_len = %lu, retval = %d\n",
		kbinput, (unsigned long)seq_len, retval);
1134
1135
1136
1137
1138
#endif

    return retval;
}

1139
1140
/* Translate a byte sequence: turn a three-digit decimal number (from
 * 000 to 255) into its corresponding byte value. */
1141
int get_byte_kbinput(int kbinput)
1142
{
1143
    static int byte_digits = 0, byte = 0;
1144
    int retval = ERR;
1145

1146
1147
    /* Increment the byte digit counter. */
    byte_digits++;
1148

1149
    switch (byte_digits) {
1150
	case 1:
1151
1152
	    /* First digit: This must be from zero to two.  Put it in
	     * the 100's position of the byte sequence holder. */
1153
	    if ('0' <= kbinput && kbinput <= '2')
1154
		byte = (kbinput - '0') * 100;
1155
	    else
1156
1157
		/* This isn't the start of a byte sequence.  Return this
		 * character as the result. */
1158
1159
1160
		retval = kbinput;
	    break;
	case 2:
1161
1162
1163
1164
	    /* Second digit: This must be from zero to five if the first
	     * was two, and may be any decimal value if the first was
	     * zero or one.  Put it in the 10's position of the byte
	     * sequence holder. */
1165
1166
1167
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1168
	    else
1169
1170
		/* This isn't the second digit of a byte sequence.
		 * Return this character as the result. */
1171
1172
1173
		retval = kbinput;
	    break;
	case 3:
1174
	    /* Third digit: This must be from zero to five if the first
1175
1176
1177
	     * was two and the second was five, and may be any decimal
	     * value otherwise.  Put it in the 1's position of the byte
	     * sequence holder. */
1178
1179
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
1180
		byte += kbinput - '0';
1181
		/* The byte sequence is complete. */
1182
		retval = byte;
1183
	    } else
1184
1185
		/* This isn't the third digit of a byte sequence.
		 * Return this character as the result. */
1186
1187
		retval = kbinput;
	    break;
1188
	default:
1189
1190
1191
	    /* If there are more than three digits, return this
	     * character as the result.  (Maybe we should produce an
	     * error instead?) */
1192
1193
1194
1195
1196
1197
1198
1199
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1200
	byte = 0;
1201
1202
1203
    }

#ifdef DEBUG
1204
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1205
1206
1207
1208
1209
#endif

    return retval;
}

1210
#ifdef ENABLE_UTF8
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
/* If the character in kbinput is a valid hexadecimal digit, multiply it
 * by factor and add the result to uni. */
long add_unicode_digit(int kbinput, long factor, long *uni)
{
    long retval = ERR;

    if ('0' <= kbinput && kbinput <= '9')
	*uni += (kbinput - '0') * factor;
    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
	*uni += (tolower(kbinput) - 'a' + 10) * factor;
    else
	/* If this character isn't a valid hexadecimal value, save it as
	 * the result. */
	retval = kbinput;

    return retval;
}

1229
/* Translate a Unicode sequence: turn a six-digit hexadecimal number
1230
 * (from 000000 to 10FFFF, case-insensitive) into its corresponding
1231
 * multibyte value. */
1232
long get_unicode_kbinput(int kbinput)
1233
{
1234
1235
1236
    static int uni_digits = 0;
    static long uni = 0;
    long retval = ERR;
1237

1238
    /* Increment the Unicode digit counter. */
1239
    uni_digits++;
1240

1241
    switch (uni_digits) {
1242
	case 1:
1243
1244
	    /* First digit: This must be zero or one.  Put it in the
	     * 0x100000's position of the Unicode sequence holder. */
1245
	    if ('0' <= kbinput && kbinput <= '1')
1246
		uni = (kbinput - '0') * 0x100000;
1247
	    else
1248
1249
		/* This isn't the first digit of a Unicode sequence.
		 * Return this character as the result. */
1250
1251
1252
		retval = kbinput;
	    break;
	case 2:
1253
1254
1255
1256
1257
1258
	    /* Second digit: This must be zero if the first was one, and
	     * may be any hexadecimal value if the first was zero.  Put
	     * it in the 0x10000's position of the Unicode sequence
	     * holder. */
	    if (uni == 0 || '0' == kbinput)
		retval = add_unicode_digit(kbinput, 0x10000, &uni);
1259
	    else
1260
1261
		/* This isn't the second digit of a Unicode sequence.
		 * Return this character as the result. */
1262
1263
1264
		retval = kbinput;
	    break;
	case 3:
1265
1266
1267
1268
	    /* Third digit: This may be any hexadecimal value.  Put it
	     * in the 0x1000's position of the Unicode sequence
	     * holder. */
	    retval = add_unicode_digit(kbinput, 0x1000, &uni);
1269
	    break;
1270
	case 4:
1271
1272
1273
1274
	    /* Fourth digit: This may be any hexadecimal value.  Put it
	     * in the 0x100's position of the Unicode sequence
	     * holder. */
	    retval = add_unicode_digit(kbinput, 0x100, &uni);
1275
	    break;
1276
	case 5:
1277
1278
1279
	    /* Fifth digit: This may be any hexadecimal value.  Put it
	     * in the 0x10's position of the Unicode sequence holder. */
	    retval = add_unicode_digit(kbinput, 0x10, &uni);
1280
	    break;
1281
	case 6:
1282
1283
1284
1285
1286
1287
	    /* Sixth digit: This may be any hexadecimal value.  Put it
	     * in the 0x1's position of the Unicode sequence holder. */
	    retval = add_unicode_digit(kbinput, 0x1, &uni);
	    /* If this character is a valid hexadecimal value, then the
	     * Unicode sequence is complete. */
	    if (retval == ERR)
1288
		retval = uni;
1289
1290
	    break;
	default:
1291
1292
1293
	    /* If there are more than six digits, return this character
	     * as the result.  (Maybe we should produce an error
	     * instead?) */
1294
1295
1296
	    retval = kbinput;
	    break;
    }
1297

1298
1299
    /* If we have a result, reset the Unicode digit counter and the
     * Unicode sequence holder. */
1300
    if (retval != ERR) {
1301
1302
	uni_digits = 0;
	uni = 0;
1303
    }
1304

1305
#ifdef DEBUG
1306
    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n", kbinput, uni_digits, uni, retval);
1307
1308
#endif

1309
1310
    return retval;
}
1311
#endif /* ENABLE_UTF8 */
1312

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

1319
     /* Ctrl-Space (Ctrl-2, Ctrl-@, Ctrl-`) */
1320
1321
    if (kbinput == ' ' || kbinput == '2')
	retval = NANO_CONTROL_SPACE;
1322
1323
    /* Ctrl-/ (Ctrl-7, Ctrl-_) */
    else if (kbinput == '/')
1324
	retval = NANO_CONTROL_7;
1325
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-/, Ctrl-_) */
1326
1327
1328
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
1329
1330
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
1331
1332
    /* Ctrl-@ (Ctrl-Space, Ctrl-2, Ctrl-`) to Ctrl-_ (Ctrl-/, Ctrl-7) */
    else if ('@' <= kbinput && kbinput <= '_')
1333
	retval = kbinput - '@';
1334
1335
    /* Ctrl-` (Ctrl-2, Ctrl-Space, Ctrl-@) to Ctrl-~ (Ctrl-6, Ctrl-^) */
    else if ('`' <= kbinput && kbinput <= '~')
1336
	retval = kbinput - '`';
1337
1338
1339
    else
	retval = kbinput;

1340
#ifdef DEBUG
1341
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1342
1343
#endif

1344
1345
    return retval;
}
1346

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1347
1348
/* Put the output-formatted characters in output back into the keystroke
 * buffer, so that they can be parsed and displayed as output again. */
1349
void unparse_kbinput(char *output, size_t output_len)
1350
{
1351
1352
    int *input;
    size_t i;
1353

1354
1355
1356
1357
    if (output_len == 0)
	return;

    input = (int *)nmalloc(output_len * sizeof(int));
1358

1359
1360
    for (i = 0; i < output_len; i++)
	input[i] = (int)output[i];
1361

1362
    unget_input(input, output_len);
1363

1364
    free(input);
1365
1366
}

1367
/* Read in a stream of characters verbatim, and return the length of the
1368
1369
1370
1371
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1372

1373
    /* Turn off flow control characters if necessary so that we can type
1374
1375
     * them in verbatim, and turn the keypad off if necessary so that we
     * don't get extended keypad values. */
1376
1377
    if (ISSET(PRESERVE))
	disable_flow_control();
1378
1379
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, FALSE);
1380
1381
1382

    /* Read in a stream of characters and interpret it if possible. */
    retval = parse_verbatim_kbinput(win, kbinput_len);
1383
1384

    /* Turn flow control characters back on if necessary and turn the
1385
     * keypad back on if necessary now that we're done. */
1386
1387
    if (ISSET(PRESERVE))
	enable_flow_control();
1388
1389
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, TRUE);
1390

1391
    return retval;
1392
1393
}

1394
1395
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
1396
 * the input into the corresponding multibyte value if possible.  After
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1397
 * that, leave the input as-is. */
1398
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1399
{
1400
    int *kbinput, *retval;
1401

1402
    /* Read in the first keystroke. */
1403
1404
    while ((kbinput = get_input(win, 1)) == NULL)
	;
1405

1406
#ifdef ENABLE_UTF8
1407
1408
1409
    if (using_utf8()) {
	/* Check whether the first keystroke is a valid hexadecimal
	 * digit. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1410
	long uni = get_unicode_kbinput(*kbinput);
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429

	/* If the first keystroke isn't a valid hexadecimal digit, put
	 * back the first keystroke. */
	if (uni != ERR)
	    unget_input(kbinput, 1);

	/* Otherwise, read in keystrokes until we have a complete
	 * Unicode sequence, and put back the corresponding Unicode
	 * value. */
	else {
	    char *uni_mb;
	    int uni_mb_len, *seq, i;

	    if (win == edit)
		/* TRANSLATORS: This is displayed during the input of a
		 * six-digit hexadecimal Unicode character code. */
		statusbar(_("Unicode Input"));

	    while (uni == ERR) {
1430
1431
		while ((kbinput = get_input(win, 1)) == NULL)
		    ;
1432
1433
		uni = get_unicode_kbinput(*kbinput);
	    }
1434

1435
1436
1437
	    /* Put back the multibyte equivalent of the Unicode
	     * value. */
	    uni_mb = make_mbchar(uni, &uni_mb_len);
1438

1439
	    seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1440

1441
1442
	    for (i = 0; i < uni_mb_len; i++)
		seq[i] = (unsigned char)uni_mb[i];
1443

1444
	    unget_input(seq, uni_mb_len);
1445

1446
1447
	    free(seq);
	    free(uni_mb);
1448
	}
1449
    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1450
1451
#endif /* ENABLE_UTF8 */

1452
1453
	/* Put back the first keystroke. */
	unget_input(kbinput, 1);
1454

1455
1456
    free(kbinput);

1457
    /* Get the complete sequence, and save the characters in it as the
1458
     * result. */
1459
    *kbinput_len = get_key_buffer_len();
1460
    retval = get_input(NULL, *kbinput_len);
1461
1462
1463
1464

    return retval;
}

1465
#ifndef DISABLE_MOUSE
1466
/* Handle any mouse event that may have occurred.  We currently handle
1467
1468
1469
1470
1471
1472
1473
 * releases/clicks of the first mouse button.  If allow_shortcuts is
 * TRUE, releasing/clicking on a visible shortcut will put back the
 * keystroke associated with that shortcut.  If NCURSES_MOUSE_VERSION is
 * at least 2, we also currently handle presses of the fourth mouse
 * button (upward rolls of the mouse wheel) by putting back the
 * keystrokes to move up, and presses of the fifth mouse button
 * (downward rolls of the mouse wheel) by putting back the keystrokes to
1474
1475
1476
1477
1478
1479
 * move down.  We also store the coordinates of a mouse event that needs
 * to be handled in mouse_x and mouse_y, relative to the entire screen.
 * Return -1 on error, 0 if the mouse event needs to be handled, 1 if
 * it's been handled by putting back keystrokes that need to be handled.
 * or 2 if it's been ignored.  Assume that KEY_MOUSE has already been
 * read in. */
1480
int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1481
1482
{
    MEVENT mevent;
1483
    bool in_bottomwin;
1484
    subnfunc *f;
1485
1486
1487
1488
1489
1490

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

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1491
	return -1;
1492

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

1497
1498
    in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1499
    /* Handle releases/clicks of the first mouse button. */
1500
    if (mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) {
1501
1502
	/* If we're allowing shortcuts, the current shortcut list is
	 * being displayed on the last two lines of the screen, and the
1503
1504
1505
	 * first mouse button was released on/clicked inside it, we need
	 * to figure out which shortcut was released on/clicked and put
	 * back the equivalent keystroke(s) for it. */
1506
	if (allow_shortcuts && !ISSET(NO_HELP) && in_bottomwin) {
1507
1508
1509
1510
	    int i;
		/* The width of all the shortcuts, except for the last
		 * two, in the shortcut list in bottomwin. */
	    int j;
1511
		/* The calculated index number of the clicked item. */
1512
1513
1514
1515
	    size_t currslen;
		/* The number of shortcuts in the current shortcut
		 * list. */

1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
	    /* Translate the mouse event coordinates so that they're
	     * relative to bottomwin. */
	    wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);

	    /* Handle releases/clicks of the first mouse button on the
	     * statusbar elsewhere. */
	    if (*mouse_y == 0) {
		/* Restore the untranslated mouse event coordinates, so
		 * that they're relative to the entire screen again. */
		*mouse_x = mevent.x;
		*mouse_y = mevent.y;

		return 0;
	    }
1530

1531
	    /* Get the shortcut lists' length. */
1532
	    if (currmenu == MMAIN)
1533
		currslen = MAIN_VISIBLE;
1534
	    else {
1535
		currslen = length_of_list(currmenu);
1536

1537
1538
1539
1540
1541
		/* We don't show any more shortcuts than the main list
		 * does. */
		if (currslen > MAIN_VISIBLE)
		    currslen = MAIN_VISIBLE;
	    }
1542

1543
1544
1545
1546
1547
1548
1549
1550
	    /* Calculate the width of all of the shortcuts in the list
	     * except for the last two, which are longer by (COLS % i)
	     * columns so as to not waste space. */
	    if (currslen < 2)
		i = COLS / (MAIN_VISIBLE / 2);
	    else
		i = COLS / ((currslen / 2) + (currslen % 2));

1551
1552
	    /* Calculate the one-based index in the shortcut list. */
	    j = (*mouse_x / i) * 2 + *mouse_y;
1553

1554
1555
	    /* Adjust the index if we hit the last two wider ones. */
	    if ((j > currslen) && (*mouse_x % i < COLS % i))
1556
		j -= 2;
1557
1558
1559
#ifdef DEBUG
	    fprintf(stderr, "Calculated %i as index in shortcut list, currmenu = %x.\n", j, currmenu);
#endif
1560
1561
	    /* Ignore releases/clicks of the first mouse button beyond
	     * the last shortcut. */
1562
	    if (j > currslen)
1563
		return 2;
1564

1565
1566
	    /* Go through the list of functions to determine which
	     * shortcut in the current menu we released/clicked on. */
1567
	    for (f = allfuncs; f != NULL; f = f->next) {
1568
		if ((f->menus & currmenu) == 0)
1569
1570
		    continue;
		if (first_sc_for(currmenu, f->scfunc) == NULL)
1571
		    continue;
1572
1573
		/* Tick off an actually shown shortcut. */
		j -= 1;
1574
1575
		if (j == 0)
		    break;
1576
	    }
1577
#ifdef DEBUG
1578
	    fprintf(stderr, "Stopped on func %ld present in menus %x\n", (long)f->scfunc, f->menus);
1579
#endif
1580

1581
	    /* And put the corresponding key into the keyboard buffer. */
1582
	    if (f != NULL) {
1583
		const sc *s = first_sc_for(currmenu, f->scfunc);
1584
		unget_kbinput(s->seq, s->type == META, s->type == FKEY);
1585
	    }
1586
	    return 1;
1587
	} else
1588
1589
	    /* Handle releases/clicks of the first mouse button that
	     * aren't on the current shortcut list elsewhere. */
1590
	    return 0;
1591
    }
1592
1593
#if NCURSES_MOUSE_VERSION >= 2
    /* Handle presses of the fourth mouse button (upward rolls of the
1594
1595
1596
     * mouse wheel) and presses of the fifth mouse button (downward
     * rolls of the mouse wheel) . */
    else if (mevent.bstate & (BUTTON4_PRESSED | BUTTON5_PRESSED)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1597
	bool in_edit = wenclose(edit, *mouse_y, *mouse_x);
1598

1599
1600
1601
1602
	if (in_bottomwin)
	    /* Translate the mouse event coordinates so that they're
	     * relative to bottomwin. */
	    wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);
1603

1604
	if (in_edit || (in_bottomwin && *mouse_y == 0)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1605
1606
	    int i;

1607
1608
1609
1610
1611
	    /* One upward roll of the mouse wheel is equivalent to
	     * moving up three lines, and one downward roll of the mouse
	     * wheel is equivalent to moving down three lines. */
	    for (i = 0; i < 3; i++)
		unget_kbinput((mevent.bstate & BUTTON4_PRESSED) ?
1612
			sc_seq_or(do_up_void, 0) : sc_seq_or(do_down_void, 0),
1613
			FALSE, FALSE);
1614
1615
1616
1617
1618
1619
1620

	    return 1;
	} else
	    /* Ignore presses of the fourth mouse button and presses of
	     * the fifth mouse buttons that aren't on the edit window or
	     * the statusbar. */
	    return 2;
1621
1622
    }
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1623
1624
1625

    /* Ignore all other mouse events. */
    return 2;
1626
}
1627
1628
#endif /* !DISABLE_MOUSE */

1629
1630
1631
1632
/* Return the shortcut that corresponds to the values of kbinput (the
 * key itself) and meta_key (whether the key is a meta sequence).  The
 * returned shortcut will be the first in the list that corresponds to
 * the given sequence. */
1633
const sc *get_shortcut(int *kbinput)
1634
{
1635
    sc *s;
1636

1637
#ifdef DEBUG
1638
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %s -- ", *kbinput, meta_key ? "TRUE" : "FALSE");
1639
1640
#endif

1641
    for (s = sclist; s != NULL; s = s->next) {
1642
	if ((s->menus & currmenu) && *kbinput == s->seq
1643
		&& meta_key == (s->type == META)) {
1644
#ifdef DEBUG
1645
	    fprintf (stderr, "matched seq \"%s\", and btw meta was %d (menu is %x from %x)\n",
1646
			     s->keystr, meta_key, currmenu, s->menus);
1647
#endif
1648
	    return s;
1649
1650
	}
    }
1651
#ifdef DEBUG
1652
    fprintf (stderr, "matched nothing, btw meta was %d\n", meta_key);
1653
#endif
1654
1655
1656
1657

    return NULL;
}

1658
1659
1660
1661
1662
/* Move to (x, y) in win, and display a line of n spaces with the
 * current attributes. */
void blank_line(WINDOW *win, int y, int x, int n)
{
    wmove(win, y, x);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1663

1664
1665
1666
1667
    for (; n > 0; n--)
	waddch(win, ' ');
}

1668
/* Blank the first line of the top portion of the window. */
1669
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1670
{
1671
    blank_line(topwin, 0, 0, COLS);
1672
1673
}

1674
1675
/* If the MORE_SPACE flag isn't set, blank the second line of the top
 * portion of the window. */
1676
1677
1678
void blank_topbar(void)
{
    if (!ISSET(MORE_SPACE))
1679
	blank_line(topwin, 1, 0, COLS);
1680
1681
}

1682
/* Blank all the lines of the middle portion of the window, i.e. the
1683
 * edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
1684
1685
void blank_edit(void)
{
1686
    int i;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1687

1688
    for (i = 0; i < editwinrows; i++)
1689
	blank_line(edit, i, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1690
1691
}

1692
/* Blank the first line of the bottom portion of the window. */
Chris Allegretta's avatar
Chris Allegretta committed
1693
1694
void blank_statusbar(void)
{
1695
    blank_line(bottomwin, 0, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1696
1697
}

1698
1699
/* If the NO_HELP flag isn't set, blank the last two lines of the bottom
 * portion of the window. */
1700
1701
1702
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
1703
1704
	blank_line(bottomwin, 1, 0, COLS);
	blank_line(bottomwin, 2, 0, COLS);
1705
1706
1707
    }
}

1708
1709
1710
/* Check if the number of keystrokes needed to blank the statusbar has
 * been pressed.  If so, blank the statusbar, unless constant cursor
 * position display is on. */
1711
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1712
{
1713
    if (statusblank > 0) {
1714
	statusblank--;
1715

1716
1717
1718
1719
1720
1721
	if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
	    blank_statusbar();
	    wnoutrefresh(bottomwin);
	    reset_cursor();
	    wnoutrefresh(edit);
	}
Chris Allegretta's avatar
Chris Allegretta committed
1722
1723
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
1729
1730
1731
1732
1733
 * string is dynamically allocated, and should be freed.  If dollars is
 * TRUE, the caller might put "$" at the beginning or end of the line if
 * it's too long. */
char *display_string(const char *buf, size_t start_col, size_t len, bool
	dollars)
1734
1735
{
    size_t start_index;
1736
	/* Index in buf of the first character shown. */
1737
    size_t column;
1738
	/* Screen column that start_index corresponds to. */
1739
1740
1741
1742
1743
1744
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */
1745
    char *buf_mb;
1746
1747
    int buf_mb_len;

1748
1749
1750
1751
1752
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

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

1756
1757
    buf_mb = charalloc(mb_cur_max());

1758
1759
    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
1760

1761
    assert(column <= start_col);
1762

1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
    /* Make sure there's enough room for the initial character, whether
     * it's a multibyte control character, a non-control multibyte
     * character, a tab character, or a null terminator.  Rationale:
     *
     * multibyte control character followed by a null terminator:
     *     1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0')
     * multibyte non-control character followed by a null terminator:
     *     mb_cur_max() bytes + 1 byte ('\0')
     * tab character followed by a null terminator:
     *     mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0')
     *
     * Since tabsize has a minimum value of 1, it can substitute for 1
     * byte above. */
    alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
    converted = charalloc(alloc_len);
1778

1779
    index = 0;
1780
    seen_wide = FALSE;
1781

1782
1783
    if (buf[start_index] != '\0' && buf[start_index] != '\t' &&
	(column < start_col || (dollars && column > 0))) {
1784
1785
	/* We don't display all of buf[start_index] since it starts to
	 * the left of the screen. */
1786
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1787

1788
	if (is_cntrl_mbchar(buf_mb)) {
1789
	    if (column < start_col) {
1790
1791
		char *ctrl_buf_mb = charalloc(mb_cur_max());
		int ctrl_buf_mb_len, i;
1792

1793
1794
		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
			&ctrl_buf_mb_len);
1795

1796
1797
		for (i = 0; i < ctrl_buf_mb_len; i++)
		    converted[index++] = ctrl_buf_mb[i];
1798

1799
		start_col += mbwidth(ctrl_buf_mb);
1800

1801
		free(ctrl_buf_mb);
1802

1803
		start_index += buf_mb_len;
1804
	    }
1805
	}
1806
#ifdef ENABLE_UTF8
1807
1808
1809
1810
1811
1812
	else if (using_utf8() && mbwidth(buf_mb) == 2) {
	    if (column >= start_col) {
		converted[index++] = ' ';
		start_col++;
	    }

1813
	    converted[index++] = ' ';
1814
	    start_col++;
1815
1816

	    start_index += buf_mb_len;
1817
	}
1818
#endif
1819
1820
    }

1821
    while (buf[start_index] != '\0') {
1822
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1823

1824
1825
1826
	if (mbwidth(buf + start_index) > 1)
	    seen_wide = TRUE;

1827
1828
1829
1830
1831
1832
1833
1834
	/* Make sure there's enough room for the next character, whether
	 * it's a multibyte control character, a non-control multibyte
	 * character, a tab character, or a null terminator. */
	if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) {
	    alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
	    converted = charealloc(converted, alloc_len);
	}

1835
	/* If buf contains a tab character, interpret it. */
1836
	if (*buf_mb == '\t') {
1837
#ifndef NANO_TINY
1838
1839
1840
1841
1842
1843
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = 0; i < whitespace_len[0]; i++)
		    converted[index++] = whitespace[i];
	    } else
1844
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1845
		converted[index++] = ' ';
1846
	    start_col++;
1847
	    while (start_col % tabsize != 0) {
1848
		converted[index++] = ' ';
1849
1850
		start_col++;
	    }
1851
	/* If buf contains a control character, interpret it. */
1852
	} else if (is_cntrl_mbchar(buf_mb)) {
1853
1854
	    char *ctrl_buf_mb = charalloc(mb_cur_max());
	    int ctrl_buf_mb_len, i;
1855

1856
	    converted[index++] = '^';
1857
1858
	    start_col++;

1859
1860
	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
		&ctrl_buf_mb_len);
1861

1862
1863
	    for (i = 0; i < ctrl_buf_mb_len; i++)
		converted[index++] = ctrl_buf_mb[i];
1864

1865
	    start_col += mbwidth(ctrl_buf_mb);
1866

1867
	    free(ctrl_buf_mb);
1868
	/* If buf contains a space character, interpret it. */
1869
	} else if (*buf_mb == ' ') {
1870
#ifndef NANO_TINY
1871
1872
1873
1874
1875
1876
1877
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = whitespace_len[0]; i < whitespace_len[0] +
			whitespace_len[1]; i++)
		    converted[index++] = whitespace[i];
	    } else
1878
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1879
		converted[index++] = ' ';
1880
	    start_col++;
1881
1882
	/* If buf contains a non-control character, interpret it.  If buf
	 * contains an invalid multibyte sequence, display it as such. */
1883
	} else {
1884
1885
	    char *nctrl_buf_mb = charalloc(mb_cur_max());
	    int nctrl_buf_mb_len, i;
1886

1887
#ifdef ENABLE_UTF8
1888
1889
1890
	    /* Make sure an invalid sequence-starter byte is properly
	     * terminated, so that it doesn't pick up lingering bytes
	     * of any previous content. */
1891
1892
1893
	    if (using_utf8() && buf_mb_len == 1)
		buf_mb[1] = '\0';
#endif
1894

1895
1896
	    nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
		&nctrl_buf_mb_len);
1897

1898
1899
1900
1901
1902
1903
	    for (i = 0; i < nctrl_buf_mb_len; i++)
		converted[index++] = nctrl_buf_mb[i];

	    start_col += mbwidth(nctrl_buf_mb);

	    free(nctrl_buf_mb);
1904
1905
	}

1906
	start_index += buf_mb_len;
1907
1908
    }

1909
1910
    free(buf_mb);

1911
1912
    assert(alloc_len >= index + 1);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1913
    /* Null-terminate converted. */
1914
    converted[index] = '\0';
1915
1916
1917

    /* Make sure converted takes up no more than len columns. */
    index = actual_x(converted, len);
1918
    null_at(&converted, index);
1919

1920
    return converted;
1921
1922
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1923
1924
1925
1926
1927
1928
/* If path is NULL, we're in normal editing mode, so display the current
 * version of nano, the current filename, and whether the current file
 * has been modified on the titlebar.  If path isn't NULL, we're in the
 * file browser, and path contains the directory to start the file
 * browser in, so display the current version of nano and the contents
 * of path on the titlebar. */
1929
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
1930
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1931
    int space = COLS;
1932
	/* The space we have available for display. */
1933
    size_t verlen = strlenpt(PACKAGE_STRING) + 1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1934
1935
	/* The length of the version message in columns, plus one for
	 * padding. */
1936
    const char *prefix;
1937
	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
1938
    size_t prefixlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1939
	/* The length of the prefix in columns, plus one for padding. */
1940
    const char *state;
1941
1942
	/* "Modified", "View", or "".  Shows the state of this
	 * buffer. */
1943
    size_t statelen = 0;
1944
	/* The length of the state in columns, or the length of
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1945
1946
	 * "Modified" if the state is blank and we're not in the file
	 * browser. */
1947
    char *exppath = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1948
	/* The filename, expanded for display. */
1949
    bool newfie = FALSE;
1950
	/* Do we say "New Buffer"? */
1951
    bool dots = FALSE;
1952
1953
	/* Do we put an ellipsis before the path? */

1954
    assert(path != NULL || openfile->filename != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1955

1956
1957
1958
    if (interface_color_pair[TITLE_BAR].bright)
	wattron(topwin, A_BOLD);
    wattron(topwin, interface_color_pair[TITLE_BAR].pairnum);
1959

1960
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
1961

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1962
1963
1964
1965
    /* space has to be at least 4: two spaces before the version message,
     * at least one character of the version message, and one space
     * after the version message. */
    if (space < 4)
1966
1967
	space = 0;
    else {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1968
1969
1970
1971
	/* Limit verlen to 1/3 the length of the screen in columns,
	 * minus three columns for spaces. */
	if (verlen > (COLS / 3) - 3)
	    verlen = (COLS / 3) - 3;
1972
    }
Chris Allegretta's avatar
Chris Allegretta committed
1973

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1974
    if (space >= 4) {
1975
1976
	/* Add a space after the version message, and account for both
	 * it and the two spaces before it. */
1977
1978
	mvwaddnstr(topwin, 0, 2, PACKAGE_STRING,
		actual_x(PACKAGE_STRING, verlen));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1979
1980
1981
1982
	verlen += 3;

	/* Account for the full length of the version message. */
	space -= verlen;
1983
    }
Chris Allegretta's avatar
Chris Allegretta committed
1984

1985
1986
1987
1988
1989
1990
1991
1992
1993
#ifndef DISABLE_BROWSER
    /* Don't display the state if we're in the file browser. */
    if (path != NULL)
	state = "";
    else
#endif
	state = openfile->modified ? _("Modified") : ISSET(VIEW_MODE) ?
		_("View") : "";

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1994
    statelen = strlenpt((*state == '\0' && path == NULL) ?
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1995
	_("Modified") : state);
1996

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1997
1998
    /* If possible, add a space before state. */
    if (space > 0 && statelen < space)
1999
	statelen++;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2000
    else
2001
2002
2003
	goto the_end;

#ifndef DISABLE_BROWSER
2004
    /* path should be a directory if we're in the file browser. */
2005
2006
2007
2008
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
2009
    if (openfile->filename[0] == '\0') {
2010
	prefix = _("New Buffer");
2011
	newfie = TRUE;
2012
2013
    } else
	prefix = _("File:");
2014

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2015
    prefixlen = strnlenpt(prefix, space - statelen) + 1;
2016

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2017
    /* If newfie is FALSE, add a space after prefix. */
2018
    if (!newfie && prefixlen + statelen < space)
2019
2020
	prefixlen++;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2021
    /* If we're not in the file browser, set path to the current
2022
     * filename. */
2023
    if (path == NULL)
2024
	path = openfile->filename;
2025

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2026
    /* Account for the full lengths of the prefix and the state. */
2027
2028
2029
2030
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2031
	/* space is now the room we have for the filename. */
2032

2033
    if (!newfie) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2034
	size_t lenpt = strlenpt(path), start_col;
2035

2036
2037
2038
	/* Don't set dots to TRUE if we have fewer than eight columns
	 * (i.e. one column for padding, plus seven columns for a
	 * filename). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2039
	dots = (space >= 8 && lenpt >= space);
2040
2041
2042
2043
2044
2045
2046
2047

	if (dots) {
	    start_col = lenpt - space + 3;
	    space -= 3;
	} else
	    start_col = 0;

	exppath = display_string(path, start_col, space, FALSE);
2048
2049
    }

2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
    /* If dots is TRUE, we will display something like "File:
     * ...ename". */
    if (dots) {
	mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix,
		prefixlen));
	if (space <= -3 || newfie)
	    goto the_end;
	waddch(topwin, ' ');
	waddnstr(topwin, "...", space + 3);
	if (space <= 0)
	    goto the_end;
	waddstr(topwin, exppath);
    } else {
2063
2064
2065
	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
	    /* The length of the expanded filename. */

2066
	/* There is room for the whole filename, so we center it. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2067
2068
	mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3),
		prefix, actual_x(prefix, prefixlen));
2069
	if (!newfie) {
2070
2071
2072
2073
2074
2075
2076
2077
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    }

  the_end:
    free(exppath);

2078
    if (state[0] != '\0') {
2079
	if (statelen >= COLS - 1)
2080
2081
	    mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
	else {
2082
	    assert(COLS - statelen - 1 >= 0);
2083

2084
	    mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2085
		actual_x(state, statelen));
2086
	}
2087
    }
2088

2089
2090
    wattroff(topwin, A_BOLD);
    wattroff(topwin, interface_color_pair[TITLE_BAR].pairnum);
2091

2092
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2093
    reset_cursor();
2094
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2095
2096
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2097
2098
2099
/* Display a message on the statusbar, and set disable_cursorpos to
 * TRUE, so that the message won't be immediately overwritten if
 * constant cursor position display is on. */
2100
2101
2102
void statusbar(const char *msg, ...)
{
    va_list ap;
2103
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
2104
    size_t start_x;
2105
2106
2107
2108
#ifndef NANO_TINY
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

    UNSET(WHITESPACE_DISPLAY);
2109
#endif
2110
2111
2112
2113
2114

    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(). */
2115
    if (isendwin()) {
2116
2117
2118
2119
2120
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

2121
2122
2123
    /* Turn the cursor off while fiddling in the statusbar. */
    curs_set(0);

2124
2125
    blank_statusbar();

2126
2127
2128
2129
    bar = charalloc(mb_cur_max() * (COLS - 3));
    vsnprintf(bar, mb_cur_max() * (COLS - 3), msg, ap);
    va_end(ap);
    foo = display_string(bar, 0, COLS - 4, FALSE);
Benno Schulenberg's avatar
Benno Schulenberg committed
2130
    free(bar);
2131
2132

#ifndef NANO_TINY
2133
2134
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2135
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2136
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2137

2138
    wmove(bottomwin, 0, start_x);
2139
2140
2141
    if (interface_color_pair[STATUS_BAR].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2142
2143
2144
2145
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
2146
2147
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2148
2149
2150
2151
2152
    wnoutrefresh(bottomwin);
    reset_cursor();
    wnoutrefresh(edit);
	/* Leave the cursor at its position in the edit window, not in
	 * the statusbar. */
2153

2154
    disable_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2155

2156
2157
2158
    /* Push the message to the screen straightaway. */
    doupdate();

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2159
2160
    /* If we're doing quick statusbar blanking, and constant cursor
     * position display is off, blank the statusbar after only one
2161
2162
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2163
    statusblank =
2164
#ifndef NANO_TINY
2165
	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2166
#endif
2167
	26;
2168
2169
}

2170
2171
/* Display the shortcut list corresponding to menu on the last two rows
 * of the bottom portion of the window. */
2172
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2173
{
2174
    size_t i, colwidth, slen;
2175
2176
    subnfunc *f;
    const sc *s;
2177

2178
2179
2180
    /* Set the global variable to the given menu. */
    currmenu = menu;

Chris Allegretta's avatar
Chris Allegretta committed
2181
2182
2183
    if (ISSET(NO_HELP))
	return;

2184
    if (menu == MMAIN) {
2185
	slen = MAIN_VISIBLE;
2186

2187
	assert(slen <= length_of_list(menu));
2188
    } else {
2189
	slen = length_of_list(menu);
2190

2191
	/* Don't show any more shortcuts than the main list does. */
2192
2193
2194
2195
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2196
2197
2198
2199
    /* There will be this many characters per column, except for the
     * last two, which will be longer by (COLS % colwidth) columns so as
     * to not waste space.  We need at least three columns to display
     * anything properly. */
2200
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2201

2202
    blank_bottombars();
2203

2204
2205
2206
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2207

2208
    for (f = allfuncs, i = 0; i < slen && f != NULL; f = f->next) {
2209

2210
#ifdef DEBUG
2211
	fprintf(stderr, "Checking menu items....");
2212
#endif
2213
	if ((f->menus & menu) == 0)
2214
	    continue;
2215

2216
#ifdef DEBUG
2217
	fprintf(stderr, "found one! f->menus = %x, desc = \"%s\"\n", f->menus, f->desc);
2218
#endif
2219
2220
	s = first_sc_for(menu, f->scfunc);
	if (s == NULL) {
2221
2222
2223
#ifdef DEBUG
	    fprintf(stderr, "Whoops, guess not, no shortcut key found for func!\n");
#endif
2224
2225
	    continue;
	}
2226
	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2227
#ifdef DEBUG
2228
	fprintf(stderr, "Calling onekey with keystr \"%s\" and desc \"%s\"\n", s->keystr, f->desc);
2229
#endif
2230
	onekey(s->keystr, _(f->desc), colwidth + (COLS % colwidth));
2231
	i++;
Chris Allegretta's avatar
Chris Allegretta committed
2232
    }
2233

2234
2235
    wnoutrefresh(bottomwin);
    reset_cursor();
2236
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2237
2238
}

2239
2240
2241
2242
2243
2244
/* 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
2245
{
2246
2247
    size_t keystroke_len = strlenpt(keystroke) + 1;

2248
2249
    assert(keystroke != NULL && desc != NULL);

2250
2251
2252
    if (interface_color_pair[KEY_COMBO].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2253
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2254
2255
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2256
2257
2258
2259
2260
2261

    if (len > keystroke_len)
	len -= keystroke_len;
    else
	len = 0;

2262
2263
    if (len > 0) {
	waddch(bottomwin, ' ');
2264
2265
2266
	if (interface_color_pair[FUNCTION_TAG].bright)
	    wattron(bottomwin, A_BOLD);
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
2267
	waddnstr(bottomwin, desc, actual_x(desc, len));
2268
2269
	wattroff(bottomwin, A_BOLD);
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
Chris Allegretta's avatar
Chris Allegretta committed
2270
2271
2272
    }
}

2273
2274
/* Reset current_y, based on the position of current, and put the cursor
 * in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2275
2276
void reset_cursor(void)
{
2277
    size_t xpt;
2278
2279
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
2280
    if (openfile == NULL) {
2281
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2282
	return;
2283
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2284

2285
    xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2286

2287
#ifndef NANO_TINY
2288
2289
    if (ISSET(SOFTWRAP)) {
	filestruct *tmp;
2290
2291
	openfile->current_y = 0;

2292
	for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next)
2293
	    openfile->current_y += (strlenpt(tmp->data) / COLS) + 1;
2294

2295
	openfile->current_y += xplustabs() / COLS;
2296
	if (openfile->current_y < editwinrows)
2297
	    wmove(edit, openfile->current_y, xpt % COLS);
2298
2299
2300
    } else
#endif
    {
2301
2302
2303
2304
2305
2306
	openfile->current_y = openfile->current->lineno -
	    openfile->edittop->lineno;

	if (openfile->current_y < editwinrows)
	    wmove(edit, openfile->current_y, xpt - get_page_start(xpt));
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2307
}
Chris Allegretta's avatar
Chris Allegretta committed
2308

2309
2310
2311
2312
2313
2314
2315
2316
/* edit_draw() takes care of the job of actually painting a line into
 * the edit window.  fileptr is the line to be painted, at row line of
 * the window.  converted is the actual string to be written to the
 * window, with tabs and control characters replaced by strings of
 * regular characters.  start is the column number of the first
 * character of this page.  That is, the first character of converted
 * corresponds to character number actual_x(fileptr->data, start) of the
 * line. */
2317
void edit_draw(filestruct *fileptr, const char *converted, int
2318
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2319
{
2320
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2321
2322
2323
2324
2325
2326
2327
2328
2329
    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. */
2330
2331
#endif

2332
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2333
    assert(strlenpt(converted) <= COLS);
2334

2335
2336
2337
    /* First simply paint the line -- then we'll add colors or the
     * marking highlight on just the pieces that need it. */
    mvwaddstr(edit, line, 0, converted);
2338

2339
#ifndef USE_SLANG
2340
2341
2342
    /* Tell ncurses to really redraw the line without trying to optimize
     * for what it thinks is already there, because it gets it wrong in
     * the case of a wide character in column zero.  See bug #31743. */
2343
2344
    if (seen_wide)
	wredrawln(edit, line, 1);
2345
#endif
2346

2347
#ifndef DISABLE_COLOR
2348
2349
2350
2351
    /* If color syntaxes are available and turned on, we need to display
     * them. */
    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
	const colortype *tmpcolor = openfile->colorstrings;
2352

2353
2354
2355
2356
	/* If there are multiline regexes, make sure there is a cache. */
	if (openfile->syntax->nmultis > 0)
	    alloc_multidata_if_needed(fileptr);

2357
2358
2359
	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
2360
	    int paintlen = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2361
2362
		/* Number of chars to paint on this line.  There are
		 * COLS characters on a whole line. */
2363
	    size_t index;
2364
		/* Index in converted where we paint. */
2365
2366
2367
2368
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2369
2370
2371
2372

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2373
2374
2375
	    /* Two notes about regexec().  A return value of zero means
	     * that there is a match.  Also, rm_eo is the first
	     * non-matching character after the match. */
2376

2377
	    /* First case, tmpcolor is a single-line expression. */
2378
	    if (tmpcolor->end == NULL) {
2379
2380
2381
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2382
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2383
2384
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2385
2386
2387
		while (k < endpos) {
		    /* Note the fifth parameter to regexec().  It says
		     * not to match the beginning-of-line character
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2388
2389
2390
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2391
		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2392
2393
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2394
			break;
2395
2396
		    /* Translate the match to the beginning of the
		     * line. */
2397
2398
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2399
2400
2401

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2402
			startmatch.rm_eo++;
2403
		    else if (startmatch.rm_so < endpos &&
2404
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2405
2406
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2407
				startmatch.rm_so) - start;
2408

2409
2410
2411
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2412
2413
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2414
2415
2416

			assert(0 <= x_start && 0 <= paintlen);

2417
			mvwaddnstr(edit, line, x_start, converted +
2418
				index, paintlen);
2419
		    }
2420
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2421
		}
2422
	    } else {	/* This is a multiline expression. */
2423
		const filestruct *start_line = fileptr->prev;
2424
		    /* The first line before fileptr that matches 'start'. */
2425
		regoff_t start_col;
2426
		    /* Where the match starts in that line. */
2427
		const filestruct *end_line;
2428
		    /* The line that matches 'end'. */
2429

2430
2431
		/* First see if the multidata was maybe already calculated. */
		if (fileptr->multidata[tmpcolor->id] == CNONE)
2432
		    goto tail_of_loop;
2433
		else if (fileptr->multidata[tmpcolor->id] == CWHOLELINE) {
2434
		    mvwaddnstr(edit, line, 0, converted, -1);
2435
		    goto tail_of_loop;
2436
		} else if (fileptr->multidata[tmpcolor->id] == CBEGINBEFORE) {
2437
		    regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0);
2438
2439
		    /* If the coloured part is scrolled off, skip it. */
		    if (endmatch.rm_eo <= startpos)
2440
			goto tail_of_loop;
2441
2442
2443
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
			endmatch.rm_eo) - start);
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2444
		    goto tail_of_loop;
2445
		} if (fileptr->multidata[tmpcolor->id] == -1)
2446
2447
		    /* Assume this until proven otherwise below. */
		    fileptr->multidata[tmpcolor->id] = CNONE;
2448

2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
		/* There is no precalculated multidata, so find it out now.
		 * First check if the beginning of the line is colored by a
		 * start on an earlier line, and an end on this line or later.
		 *
		 * So: 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 a line after start_line that
		 * matches the end.  If that line is not before fileptr, then
		 * paint the beginning of this line. */

2459
		while (start_line != NULL && regexec(tmpcolor->start,
2460
2461
2462
2463
			start_line->data, 1, &startmatch, 0) == REG_NOMATCH) {
		    /* There is no start; but if there is an end on this line,
		     * there is no need to look for starts on earlier lines. */
		    if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) == 0)
2464
2465
2466
			goto step_two;
		    start_line = start_line->prev;
		}
2467

2468
2469
2470
2471
		/* If no start was found, skip to the next step. */
		if (start_line == NULL)
		    goto step_two;

2472
		/* If a found start has been qualified as an end earlier,
2473
		 * believe it and skip to the next step. */
2474
		if (start_line->multidata != NULL &&
2475
2476
			(start_line->multidata[tmpcolor->id] == CBEGINBEFORE ||
			start_line->multidata[tmpcolor->id] == CSTARTENDHERE))
2477
2478
		    goto step_two;

2479
		/* Skip over a zero-length regex match. */
2480
		if (startmatch.rm_so == startmatch.rm_eo)
2481
2482
		    goto tail_of_loop;

2483
2484
2485
2486
2487
2488
2489
2490
		/* Now start_line is the first line before fileptr containing
		 * a start match.  Is there a start on that line not followed
		 * by an end on that line? */
		start_col = 0;
		while (TRUE) {
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
		    if (regexec(tmpcolor->end, start_line->data +
2491
2492
2493
				start_col + startmatch.rm_eo, 0, NULL,
				(start_col + startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH)
2494
2495
2496
2497
			/* No end found after this start. */
			break;
		    start_col++;
		    if (regexec(tmpcolor->start, start_line->data +
2498
2499
				start_col, 1, &startmatch,
				REG_NOTBOL) == REG_NOMATCH)
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
			/* No later start on this line. */
			goto step_two;
		}
		/* Indeed, there is a start without an end on that line. */

		/* We've already checked that there is no end before fileptr
		 * and after the start.  But is there an end after the start
		 * at all?  We don't paint unterminated starts. */
		end_line = fileptr;
		while (end_line != NULL && regexec(tmpcolor->end,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2510
			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2511
		    end_line = end_line->next;
2512

2513
2514
2515
2516
2517
2518
2519
		/* If no end was found, or it is too early, next step. */
		if (end_line == NULL)
		    goto step_two;
		if (end_line == fileptr && endmatch.rm_eo <= startpos) {
		    fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
		    goto step_two;
		}
2520

2521
2522
2523
2524
2525
2526
2527
2528
		/* Now paint the start of fileptr.  If the start of fileptr
		 * is on a different line from the end, paintlen is -1, which
		 * means that everything on the line gets painted.  Otherwise,
		 * paintlen is the expanded location of the end of the match
		 * minus the expanded location of the beginning of the page. */
		if (end_line != fileptr) {
		    paintlen = -1;
		    fileptr->multidata[tmpcolor->id] = CWHOLELINE;
2529
2530
2531
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CWHOLELINE\n", tmpcolor->id, line);
#endif
2532
2533
2534
2535
		} else {
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
						endmatch.rm_eo) - start);
		    fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
2536
2537
2538
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CBEGINBEFORE\n", tmpcolor->id, line);
#endif
2539
2540
2541
2542
2543
2544
		}
		mvwaddnstr(edit, line, 0, converted, paintlen);
		/* If the whole line has been painted, don't bother looking
		 * for any more starts. */
		if (paintlen < 0)
		    goto tail_of_loop;
2545
  step_two:
2546
2547
2548
2549
2550
2551
2552
2553
		/* Second step: look for starts on this line, but start
		 * looking only after an end match, if there is one. */
		start_col = (paintlen == 0) ? 0 : endmatch.rm_eo;

		while (start_col < endpos) {
		    if (regexec(tmpcolor->start, fileptr->data + start_col,
				1, &startmatch, (start_col == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH ||
2554
				start_col + startmatch.rm_so >= endpos)
2555
2556
			/* No more starts on this line. */
			break;
2557

2558
2559
2560
2561
2562
2563
2564
		    /* Translate the match to be relative to the
		     * beginning of the line. */
		    startmatch.rm_so += start_col;
		    startmatch.rm_eo += start_col;

		    x_start = (startmatch.rm_so <= startpos) ?
				0 : strnlenpt(fileptr->data,
2565
				startmatch.rm_so) - start;
2566

2567
		    index = actual_x(converted, x_start);
2568

2569
		    if (regexec(tmpcolor->end, fileptr->data +
2570
				startmatch.rm_eo, 1, &endmatch,
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
				(startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == 0) {
			/* Translate the end match to be relative to
			 * the beginning of the line. */
			endmatch.rm_so += startmatch.rm_eo;
			endmatch.rm_eo += startmatch.rm_eo;
			/* There is an end on this line.  But does
			 * it appear on this page, and is the match
			 * more than zero characters long? */
			if (endmatch.rm_eo > startpos &&
2581
				endmatch.rm_eo > startmatch.rm_so) {
2582
			    paintlen = actual_x(converted + index,
2583
					strnlenpt(fileptr->data,
2584
					endmatch.rm_eo) - start - x_start);
2585

2586
			    assert(0 <= x_start && x_start < COLS);
2587

2588
			    mvwaddnstr(edit, line, x_start,
2589
					converted + index, paintlen);
2590
2591
			    if (paintlen > 0) {
				fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
2592
2593
2594
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CSTARTENDHERE\n", tmpcolor->id, line);
#endif
2595
			    }
2596
2597
			}
			start_col = endmatch.rm_eo;
2598
2599
2600
			/* Skip over a zero-length match. */
			if (endmatch.rm_so == endmatch.rm_eo)
			    start_col += 1;
2601
2602
2603
2604
		    } else {
			/* There is no end on this line.  But we haven't yet
			 * looked for one on later lines. */
			end_line = fileptr->next;
2605

2606
			while (end_line != NULL &&
2607
2608
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
2609
			    end_line = end_line->next;
2610

2611
2612
2613
			/* If there is no end, we're done on this line. */
			if (end_line == NULL)
			    break;
2614

2615
2616
2617
2618
2619
			assert(0 <= x_start && x_start < COLS);

			/* Paint the rest of the line. */
			mvwaddnstr(edit, line, x_start, converted + index, -1);
			fileptr->multidata[tmpcolor->id] = CENDAFTER;
2620
2621
2622
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CENDAFTER\n", tmpcolor->id, line);
#endif
2623
2624
2625
			/* We've painted to the end of the line, so don't
			 * bother checking for any more starts. */
			break;
2626
		    }
2627
2628
		}
	    }
2629
  tail_of_loop:
2630
2631
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2632
	}
2633
    }
2634
#endif /* !DISABLE_COLOR */
2635

2636
#ifndef NANO_TINY
2637
    /* If the mark is on, we need to display it. */
2638
    if (openfile->mark_set && (fileptr->lineno <=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2639
	openfile->mark_begin->lineno || fileptr->lineno <=
2640
	openfile->current->lineno) && (fileptr->lineno >=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2641
	openfile->mark_begin->lineno || fileptr->lineno >=
2642
	openfile->current->lineno)) {
2643
	/* fileptr is at least partially selected. */
2644
	const filestruct *top;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2645
	    /* Either current or mark_begin, whichever is first. */
2646
	size_t top_x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2647
	    /* current_x or mark_begin_x, corresponding to top. */
2648
2649
	const filestruct *bot;
	size_t bot_x;
2650
	int x_start;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2651
	    /* Starting column for mvwaddnstr().  Zero-based. */
2652
	int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2653
2654
	    /* Number of characters to paint on this line.  There are
	     * COLS characters on a whole line. */
2655
	size_t index;
2656
	    /* Index in converted where we paint. */
2657

2658
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2659
2660
2661
2662
2663

	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
2664

2665
	/* The selected bit of fileptr is on this page. */
2666
2667
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2668
2669
2670
2671

	    /* 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;
2672

2673
2674
2675
2676
2677
	    /* If the end of the mark is off the page, paintlen is -1,
	     * meaning that everything on the line gets painted.
	     * Otherwise, paintlen is the expanded location of the end
	     * of the mark minus the expanded location of the beginning
	     * of the mark. */
2678
2679
2680
2681
2682
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
			start);
2683
2684
2685
2686
2687
2688
2689
2690

	    /* 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;
	    }
2691
2692
2693

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

2694
	    index = actual_x(converted, x_start);
2695

2696
2697
2698
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2699
	    wattron(edit, hilite_attribute);
2700
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2701
		paintlen);
2702
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2703
	}
2704
    }
2705
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2706
2707
}

2708
/* Just update one line in the edit buffer.  This is basically a wrapper
2709
 * for edit_draw().  The line will be displayed starting with
2710
 * fileptr->data[index].  Likely arguments are current_x or zero.
2711
 * Returns: Number of additional lines consumed (needed for SOFTWRAP). */
2712
int update_line(filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2713
{
2714
    int line = 0;
2715
	/* The line in the edit window that we want to update. */
2716
    int extralinesused = 0;
2717
2718
2719
2720
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2721

2722
    assert(fileptr != NULL);
2723

2724
#ifndef NANO_TINY
2725
    if (ISSET(SOFTWRAP)) {
2726
2727
	filestruct *tmp;

2728
2729
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2730
    } else
2731
#endif
2732
2733
	line = fileptr->lineno - openfile->edittop->lineno;

2734
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2735
	return 1;
2736

2737
    /* First, blank out the line. */
2738
    blank_line(edit, line, 0, COLS);
2739

2740
2741
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2742
#ifndef NANO_TINY
2743
2744
2745
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2746
#endif
2747
	index = strnlenpt(fileptr->data, index);
2748
    page_start = get_page_start(index);
2749

2750
2751
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2752
2753
2754
#ifdef NANO_TINY
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
#else
2755
2756
2757
    converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2758
	fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2759
#endif
2760
#endif /* !NANO_TINY */
2761

2762
    /* Paint the line. */
2763
    edit_draw(fileptr, converted, line, page_start);
2764
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2765

2766
#ifndef NANO_TINY
2767
    if (!ISSET(SOFTWRAP)) {
2768
#endif
2769
2770
2771
2772
	if (page_start > 0)
	    mvwaddch(edit, line, 0, '$');
	if (strlenpt(fileptr->data) > page_start + COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
2773
#ifndef NANO_TINY
2774
    } else {
2775
	size_t full_length = strlenpt(fileptr->data);
2776
	for (index += COLS; index <= full_length && line < editwinrows - 1; index += COLS) {
2777
2778
	    line++;
#ifdef DEBUG
2779
	    fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
2780
#endif
2781
	    blank_line(edit, line, 0, COLS);
2782
2783

	    /* Expand the line, replacing tabs with spaces, and control
2784
	     * characters with their displayed forms. */
2785
2786
2787
2788
2789
	    converted = display_string(fileptr->data, index, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
	    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
		fprintf(stderr, "update_line(): converted(2) line = %s\n", converted);
#endif
2790
2791
2792

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2793
	    free(converted);
2794
2795
2796
	    extralinesused++;
	}
    }
2797
#endif /* !NANO_TINY */
2798
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2799
2800
}

2801
2802
2803
2804
/* Return TRUE if we need an update after moving the cursor, and
 * FALSE otherwise.  We need an update if the mark is on, or if
 * pww_save and placewewant are on different pages. */
bool need_screen_update(size_t pww_save)
2805
2806
{
    return
2807
#ifndef NANO_TINY
2808
	openfile->mark_set ||
2809
#endif
2810
	get_page_start(pww_save) != get_page_start(openfile->placewewant);
2811
2812
}

2813
/* When edittop changes, try and figure out how many lines
2814
 * we really have to work with (i.e. set maxrows). */
2815
2816
2817
2818
2819
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2820
2821
2822
2823
2824
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2825
2826
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2827
	maxrows++;
2828
	n += strlenpt(foo->data) / COLS;
2829
2830
2831
	foo = foo->next;
    }

2832
2833
2834
    if (n < editwinrows)
	maxrows += editwinrows - n;

2835
#ifdef DEBUG
2836
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2837
2838
2839
#endif
}

2840
2841
/* 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
2842
2843
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
2844
2845
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is FALSE. */
2846
void edit_scroll(scroll_dir direction, ssize_t nlines)
2847
{
2848
    ssize_t i;
2849
    filestruct *foo;
2850
    bool do_redraw = need_screen_update(0);
2851

2852
    assert(nlines > 0);
2853

2854
2855
2856
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

2857
    /* Move the top line of the edit window up or down (depending on the
2858
2859
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
2860
    for (i = nlines; i > 0; i--) {
2861
	if (direction == UPWARD) {
2862
	    if (openfile->edittop == openfile->fileage)
2863
		break;
2864
	    openfile->edittop = openfile->edittop->prev;
2865
	} else {
2866
	    if (openfile->edittop == openfile->filebot)
2867
		break;
2868
	    openfile->edittop = openfile->edittop->next;
2869
	}
2870
2871

#ifndef NANO_TINY
2872
	/* Don't over-scroll on long lines. */
2873
	if (ISSET(SOFTWRAP) && direction == UPWARD) {
2874
	    ssize_t len = strlenpt(openfile->edittop->data) / COLS;
2875
	    i -= len;
2876
2877
2878
	    if (len > 0)
		do_redraw = TRUE;
	}
2879
#endif
2880
2881
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2882
    /* Limit nlines to the number of lines we could scroll. */
2883
    nlines -= i;
2884

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2885
2886
    /* Don't bother scrolling zero lines or more than the number of
     * lines in the edit window minus one; in both cases, get out, and
2887
     * call edit_refresh() beforehand if we need to. */
2888
    if (nlines == 0 || do_redraw || nlines >= editwinrows) {
2889
	if (do_redraw || nlines >= editwinrows)
2890
	    edit_refresh_needed = TRUE;
2891
2892
	return;
    }
2893
2894
2895

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2896
    scrollok(edit, TRUE);
2897
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
2898
2899
    scrollok(edit, FALSE);

2900
2901
2902
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

2903
2904
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
2905
2906
    if ((direction == UPWARD && openfile->edittop ==
	openfile->fileage) || (direction == DOWNWARD &&
2907
2908
	openfile->edittop->lineno + editwinrows - 1 >=
	openfile->filebot->lineno))
2909
	nlines = editwinrows;
2910

2911
2912
2913
2914
2915
2916
    /* If the scrolled region contains only one line, and the line
     * before it is visible in the edit window, we need to draw it too.
     * If the scrolled region contains more than one line, and the lines
     * before and after the scrolled region are visible in the edit
     * window, we need to draw them too. */
    nlines += (nlines == 1) ? 1 : 2;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2917

2918
2919
    if (nlines > editwinrows)
	nlines = editwinrows;
2920
2921
2922

    /* If we scrolled up, we're on the line before the scrolled
     * region. */
2923
    foo = openfile->edittop;
2924

2925
2926
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
2927
    if (direction == DOWNWARD) {
2928
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
2929
2930
2931
	    foo = foo->next;
    }

2932
2933
2934
2935
2936
2937
    /* Draw new lines on any blank lines before or inside the scrolled
     * region.  If we scrolled down and we're on the top line, or if we
     * scrolled up and we're on the bottom line, the line won't be
     * blank, so we don't need to draw it unless the mark is on or we're
     * not on the first page. */
    for (i = nlines; i > 0 && foo != NULL; i--) {
2938
2939
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
2940
2941
2942
2943
2944
	    if (do_redraw)
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
2945
		openfile->current_x : 0);
2946
	foo = foo->next;
2947
    }
2948
    compute_maxrows();
2949
2950
2951
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2952
 * updated.  Use this if we've moved without changing any text. */
2953
void edit_redraw(filestruct *old_current, size_t pww_save)
2954
{
2955
2956
2957
    /* If the current line is offscreen, scroll until it's onscreen. */
    if (openfile->current->lineno >= openfile->edittop->lineno + maxrows ||
		openfile->current->lineno < openfile->edittop->lineno)
2958
	edit_update((ISSET(SMOOTH_SCROLL) && !focusing) ? NONE : CENTER);
2959

2960
#ifndef NANO_TINY
2961
2962
2963
    /* If the mark is on, update all lines between old_current and current. */
    if (openfile->mark_set) {
	filestruct *foo = old_current;
2964

2965
	while (foo != openfile->current) {
2966
	    update_line(foo, 0);
2967

2968
2969
2970
	    foo = (foo->lineno > openfile->current->lineno) ?
			foo->prev : foo->next;
	}
2971
    }
2972
#endif /* !NANO_TINY */
2973

2974
2975
2976
    /* Update old_current and current if we've changed page. */
    if (need_screen_update(0) || need_screen_update(pww_save)) {
	update_line(old_current, 0);
2977
	update_line(openfile->current, openfile->current_x);
2978
    }
2979
2980
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2981
2982
/* Refresh the screen without changing the position of lines.  Use this
 * if we've moved and changed text. */
Chris Allegretta's avatar
Chris Allegretta committed
2983
2984
void edit_refresh(void)
{
2985
    filestruct *foo;
2986
    int nlines;
2987

2988
    /* Figure out what maxrows should really be. */
2989
    compute_maxrows();
2990

2991
2992
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
2993
2994
	maxrows) {
#ifdef DEBUG
2995
2996
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
2997
2998
#endif

2999
	/* Make sure the current line is on the screen. */
3000
	edit_update((ISSET(SMOOTH_SCROLL) && !focusing) ? NONE : CENTER);
3001
    }
Chris Allegretta's avatar
Chris Allegretta committed
3002

3003
3004
    foo = openfile->edittop;

3005
#ifdef DEBUG
3006
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3007
#endif
3008

3009
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3010
	nlines += update_line(foo, (foo == openfile->current) ?
3011
		openfile->current_x : 0);
3012
3013
3014
	foo = foo->next;
    }

3015
    for (; nlines < editwinrows; nlines++)
3016
3017
3018
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
3019
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3020
3021
}

3022
3023
3024
3025
/* Move edittop to put it in range of current, keeping current in the
 * same place.  location determines how we move it: if it's CENTER, we
 * center current, and if it's NONE, we put current current_y lines
 * below edittop. */
3026
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
3027
{
3028
    filestruct *foo = openfile->current;
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
    int goal;

    /* 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. */
    if (location == CENTER)
3040
	goal = editwinrows / 2;
3041
3042
    else {
	goal = openfile->current_y;
3043

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

3049
    for (; goal > 0 && foo->prev != NULL; goal--) {
3050
	foo = foo->prev;
3051
#ifndef NANO_TINY
3052
3053
	if (ISSET(SOFTWRAP) && foo)
	    goal -= strlenpt(foo->data) / COLS;
3054
#endif
3055
    }
3056
    openfile->edittop = foo;
3057
#ifdef DEBUG
3058
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
3059
#endif
3060
    compute_maxrows();
3061
    edit_refresh_needed = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
3062
3063
}

3064
/* Unconditionally redraw the entire screen. */
3065
void total_redraw(void)
3066
{
3067
3068
3069
3070
3071
3072
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
3073
    wrefresh(curscr);
3074
#endif
3075
3076
}

3077
3078
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
3079
3080
void total_refresh(void)
{
3081
    total_redraw();
3082
    titlebar(NULL);
3083
    edit_refresh();
3084
    bottombars(currmenu);
3085
3086
}

3087
3088
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
3089
3090
void display_main_list(void)
{
3091
#ifndef DISABLE_COLOR
3092
3093
3094
    if (openfile->syntax
	  && (openfile->syntax->formatter || openfile->syntax->linter))
	set_lint_or_format_shortcuts();
3095
3096
3097
3098
    else
	set_spell_shortcuts();
#endif

3099
    bottombars(MMAIN);
3100
3101
}

3102
3103
3104
3105
3106
3107
/* If constant is TRUE, we display the current cursor position only if
 * disable_cursorpos is FALSE.  Otherwise, we display it
 * unconditionally and set disable_cursorpos to FALSE.  If constant is
 * TRUE and disable_cursorpos is TRUE, we also set disable_cursorpos to
 * FALSE, so that we leave the current statusbar alone this time, and
 * display the current cursor position next time. */
3108
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3109
{
3110
    filestruct *f;
3111
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3112
    size_t i, cur_xpt = xplustabs() + 1;
3113
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3114
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3115

3116
    assert(openfile->fileage != NULL && openfile->current != NULL);
3117

3118
    f = openfile->current->next;
3119
    c = openfile->current->data[openfile->current_x];
3120
3121

    openfile->current->next = NULL;
3122
    openfile->current->data[openfile->current_x] = '\0';
3123
3124
3125

    i = get_totsize(openfile->fileage, openfile->current);

3126
    openfile->current->data[openfile->current_x] = c;
3127
    openfile->current->next = f;
3128

3129
3130
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
3131
	return;
3132
    }
Chris Allegretta's avatar
Chris Allegretta committed
3133

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3134
    /* Display the current cursor position on the statusbar, and set
3135
     * disable_cursorpos to FALSE. */
3136
    linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
3137
    colpct = 100 * cur_xpt / cur_lenpt;
3138
    charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;
3139
3140

    statusbar(
3141
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3142
	(long)openfile->current->lineno,
3143
	(long)openfile->filebot->lineno, linepct,
3144
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3145
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3146

3147
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3148
3149
}

3150
/* Unconditionally display the current cursor position. */
3151
void do_cursorpos_void(void)
3152
{
3153
    do_cursorpos(FALSE);
3154
3155
}

3156
3157
void enable_nodelay(void)
{
3158
3159
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3160
3161
3162
3163
}

void disable_nodelay(void)
{
3164
3165
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3166
3167
}

3168
3169
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3170
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3171
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3172
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3173

3174
    y = get_page_start(y) + COLS - y;
3175
	/* Now y is the number of columns that we can display on this
3176
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3177

3178
3179
3180
3181
3182
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3183
    reset_cursor();
3184
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3185

3186
    if (highlight)
3187
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3188

3189
    /* This is so we can show zero-length matches. */
3190
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3191
	waddch(edit, ' ');
3192
    else
3193
	waddnstr(edit, word, actual_x(word, y));
3194
3195
3196

    if (word_len > y)
	waddch(edit, '$');
Chris Allegretta's avatar
Chris Allegretta committed
3197

3198
    if (highlight)
3199
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3200
3201
}

3202
#ifndef DISABLE_EXTRA
3203
3204
#define CREDIT_LEN 54
#define XLCREDIT_LEN 9
3205

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3206
3207
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3208
3209
void do_credits(void)
{
3210
3211
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3212
    int kbinput = ERR, crpos = 0, xlpos = 0;
3213
3214
3215
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3216
3217
	VERSION,
	"",
3218
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3219
3220
3221
3222
3223
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3224
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3225
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3226
	"Mark Majeres",
3227
	"Mike Frysinger",
3228
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3239
	NULL,				/* "Special thanks to:" */
3240
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3241
3242
3243
3244
3245
3246
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3247
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3248
	"Linus Torvalds",
3249
	NULL,				/* "the many translators and the TP" */
3250
	NULL,				/* "For ncurses:" */
3251
3252
3253
3254
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3255
3256
3257
3258
3259
3260
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3261
	"(C) 1999 - 2016",
3262
	"Free Software Foundation, Inc.",
3263
3264
3265
3266
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3267
	"http://www.nano-editor.org/"
3268
3269
    };

3270
    const char *xlcredits[XLCREDIT_LEN] = {
3271
3272
3273
3274
3275
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
3276
	N_("the many translators and the TP"),
3277
3278
3279
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3280
    };
3281

3282
3283
3284
3285
3286
3287
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3288
3289
    curs_set(0);
    nodelay(edit, TRUE);
3290

3291
    blank_titlebar();
3292
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3293
    blank_edit();
3294
3295
    blank_statusbar();
    blank_bottombars();
3296

3297
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3298
    wrefresh(edit);
3299
    wrefresh(bottomwin);
3300
    napms(700);
3301

3302
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3303
	if ((kbinput = wgetch(edit)) != ERR)
3304
	    break;
3305

3306
	if (crpos < CREDIT_LEN) {
3307
	    const char *what;
3308
3309
	    size_t start_x;

3310
	    if (credits[crpos] == NULL) {
3311
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3312

3313
		what = _(xlcredits[xlpos]);
3314
		xlpos++;
3315
	    } else
3316
		what = credits[crpos];
3317

3318
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3319
3320
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3321
	}
3322

3323
3324
3325
3326
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3327
	napms(700);
3328

3329
	scrollok(edit, TRUE);
3330
	wscrl(edit, 1);
3331
	scrollok(edit, FALSE);
3332
	wrefresh(edit);
3333

3334
	if ((kbinput = wgetch(edit)) != ERR)
3335
	    break;
3336
	napms(700);
3337

3338
	scrollok(edit, TRUE);
3339
	wscrl(edit, 1);
3340
	scrollok(edit, FALSE);
3341
	wrefresh(edit);
3342
3343
    }

3344
3345
3346
    if (kbinput != ERR)
	ungetch(kbinput);

3347
    if (!old_more_space)
3348
	UNSET(MORE_SPACE);
3349
    if (!old_no_help)
3350
	UNSET(NO_HELP);
3351
    window_init();
3352

3353
    nodelay(edit, FALSE);
3354

3355
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3356
}
3357
#endif /* !DISABLE_EXTRA */