winio.c 103 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   winio.c                                                              *
 *                                                                        *
5
 *   Copyright (C) 1999, 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
1126
1127
1128
1129
	    statusbar(_("Unknown Command"));
	    beep();
	}
    }

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

    return retval;
}

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

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

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

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

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

    return retval;
}

1209
#ifdef ENABLE_UTF8
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
/* 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;
}

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

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

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

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

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

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

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

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

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

1343
1344
    return retval;
}
1345

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

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

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

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

1361
    unget_input(input, output_len);
1362

1363
    free(input);
1364
1365
}

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

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

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

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

1390
    return retval;
1391
1392
}

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

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

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

	/* 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) {
1429
1430
		while ((kbinput = get_input(win, 1)) == NULL)
		    ;
1431
1432
		uni = get_unicode_kbinput(*kbinput);
	    }
1433

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

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

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

1443
	    unget_input(seq, uni_mb_len);
1444

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

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

1454
1455
    free(kbinput);

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

    return retval;
}

1464
#ifndef DISABLE_MOUSE
1465
/* Handle any mouse event that may have occurred.  We currently handle
1466
1467
1468
1469
1470
1471
1472
 * 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
1473
1474
1475
1476
1477
1478
 * 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. */
1479
int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1480
1481
{
    MEVENT mevent;
1482
    bool in_bottomwin;
1483
    subnfunc *f;
1484
1485
1486
1487
1488
1489

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

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1498
    /* Handle releases/clicks of the first mouse button. */
1499
    if (mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) {
1500
1501
	/* If we're allowing shortcuts, the current shortcut list is
	 * being displayed on the last two lines of the screen, and the
1502
1503
1504
	 * 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. */
1505
	if (allow_shortcuts && !ISSET(NO_HELP) && in_bottomwin) {
1506
1507
1508
1509
	    int i;
		/* The width of all the shortcuts, except for the last
		 * two, in the shortcut list in bottomwin. */
	    int j;
1510
		/* The calculated index number of the clicked item. */
1511
1512
1513
1514
	    size_t currslen;
		/* The number of shortcuts in the current shortcut
		 * list. */

1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
	    /* 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;
	    }
1529

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

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

1542
1543
1544
1545
1546
1547
1548
1549
	    /* 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));

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

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

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

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

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

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

1606
1607
1608
1609
1610
	    /* 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) ?
1611
			sc_seq_or(do_up_void, 0) : sc_seq_or(do_down_void, 0),
1612
			FALSE, FALSE);
1613
1614
1615
1616
1617
1618
1619

	    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;
1620
1621
    }
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1622
1623
1624

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

1628
1629
1630
1631
/* 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. */
1632
const sc *get_shortcut(int *kbinput)
1633
{
1634
    sc *s;
1635

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

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

    return NULL;
}

1657
1658
1659
1660
1661
/* 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
1662

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

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

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

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

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

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

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

1707
1708
1709
/* 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. */
1710
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1711
{
1712
    if (statusblank > 0) {
1713
	statusblank--;
1714

1715
1716
1717
1718
1719
1720
	if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
	    blank_statusbar();
	    wnoutrefresh(bottomwin);
	    reset_cursor();
	    wnoutrefresh(edit);
	}
Chris Allegretta's avatar
Chris Allegretta committed
1721
1722
1723
    }
}

1724
1725
1726
1727
/* 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
1728
1729
1730
1731
1732
 * 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)
1733
1734
{
    size_t start_index;
1735
	/* Index in buf of the first character shown. */
1736
    size_t column;
1737
	/* Screen column that start_index corresponds to. */
1738
1739
1740
1741
1742
1743
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */
1744
    char *buf_mb;
1745
1746
    int buf_mb_len;

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

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

1755
1756
    buf_mb = charalloc(mb_cur_max());

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

1760
    assert(column <= start_col);
1761

1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
    /* 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);
1777

1778
    index = 0;
1779
    seen_wide = FALSE;
1780

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

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

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

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

1798
		start_col += mbwidth(ctrl_buf_mb);
1799

1800
		free(ctrl_buf_mb);
1801

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

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

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

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

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

1826
1827
1828
1829
1830
1831
1832
1833
	/* 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);
	}

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

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

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

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

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

1864
	    start_col += mbwidth(ctrl_buf_mb);
1865

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

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

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

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

1897
1898
1899
1900
1901
1902
	    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);
1903
1904
	}

1905
	start_index += buf_mb_len;
1906
1907
    }

1908
1909
    free(buf_mb);

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

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

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

1919
    return converted;
1920
1921
}

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

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1961
1962
1963
1964
    /* 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)
1965
1966
	space = 0;
    else {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1967
1968
1969
1970
	/* 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;
1971
    }
Chris Allegretta's avatar
Chris Allegretta committed
1972

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

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

1984
1985
1986
1987
1988
1989
1990
1991
1992
#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
1993
    statelen = strlenpt((*state == '\0' && path == NULL) ?
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1994
	_("Modified") : state);
1995

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

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

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

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

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

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

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

2035
2036
2037
	/* 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
2038
	dots = (space >= 8 && lenpt >= space);
2039
2040
2041
2042
2043
2044
2045
2046

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

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

2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
    /* 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 {
2062
2063
2064
	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
	    /* The length of the expanded filename. */

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

  the_end:
    free(exppath);

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

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2096
2097
2098
/* 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. */
2099
2100
2101
void statusbar(const char *msg, ...)
{
    va_list ap;
2102
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
2103
    size_t start_x;
2104
2105
2106
2107
#ifndef NANO_TINY
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

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

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

    blank_statusbar();

2122
2123
2124
2125
    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
2126
    free(bar);
2127
2128

#ifndef NANO_TINY
2129
2130
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2131
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2132
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2133

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

2150
    disable_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2151
2152
2153

    /* If we're doing quick statusbar blanking, and constant cursor
     * position display is off, blank the statusbar after only one
2154
2155
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2156
    statusblank =
2157
#ifndef NANO_TINY
2158
	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2159
#endif
2160
	26;
2161
2162
}

2163
2164
/* Display the shortcut list corresponding to menu on the last two rows
 * of the bottom portion of the window. */
2165
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2166
{
2167
    size_t i, colwidth, slen;
2168
2169
    subnfunc *f;
    const sc *s;
2170

2171
2172
2173
    /* Set the global variable to the given menu. */
    currmenu = menu;

Chris Allegretta's avatar
Chris Allegretta committed
2174
2175
2176
    if (ISSET(NO_HELP))
	return;

2177
    if (menu == MMAIN) {
2178
	slen = MAIN_VISIBLE;
2179

2180
	assert(slen <= length_of_list(menu));
2181
    } else {
2182
	slen = length_of_list(menu);
2183

2184
	/* Don't show any more shortcuts than the main list does. */
2185
2186
2187
2188
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2189
2190
2191
2192
    /* 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. */
2193
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2194

2195
    blank_bottombars();
2196

2197
2198
2199
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2200

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

2203
#ifdef DEBUG
2204
	fprintf(stderr, "Checking menu items....");
2205
#endif
2206
	if ((f->menus & menu) == 0)
2207
	    continue;
2208

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

2227
2228
    wnoutrefresh(bottomwin);
    reset_cursor();
2229
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2230
2231
}

2232
2233
2234
2235
2236
2237
/* 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
2238
{
2239
2240
    size_t keystroke_len = strlenpt(keystroke) + 1;

2241
2242
    assert(keystroke != NULL && desc != NULL);

2243
2244
2245
    if (interface_color_pair[KEY_COMBO].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2246
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2247
2248
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2249
2250
2251
2252
2253
2254

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

2255
2256
    if (len > 0) {
	waddch(bottomwin, ' ');
2257
2258
2259
	if (interface_color_pair[FUNCTION_TAG].bright)
	    wattron(bottomwin, A_BOLD);
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
2260
	waddnstr(bottomwin, desc, actual_x(desc, len));
2261
2262
	wattroff(bottomwin, A_BOLD);
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
Chris Allegretta's avatar
Chris Allegretta committed
2263
2264
2265
    }
}

2266
2267
/* 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
2268
2269
void reset_cursor(void)
{
2270
    size_t xpt;
2271
2272
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
2273
    if (openfile == NULL) {
2274
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2275
	return;
2276
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2277

2278
    xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2279

2280
#ifndef NANO_TINY
2281
2282
    if (ISSET(SOFTWRAP)) {
	filestruct *tmp;
2283
2284
	openfile->current_y = 0;

2285
	for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next)
2286
	    openfile->current_y += (strlenpt(tmp->data) / COLS) + 1;
2287

2288
	openfile->current_y += xplustabs() / COLS;
2289
	if (openfile->current_y < editwinrows)
2290
	    wmove(edit, openfile->current_y, xpt % COLS);
2291
2292
2293
    } else
#endif
    {
2294
2295
2296
2297
2298
2299
	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
2300
}
Chris Allegretta's avatar
Chris Allegretta committed
2301

2302
2303
2304
2305
2306
2307
2308
2309
/* 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. */
2310
void edit_draw(filestruct *fileptr, const char *converted, int
2311
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2312
{
2313
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2314
2315
2316
2317
2318
2319
2320
2321
2322
    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. */
2323
2324
#endif

2325
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2326
    assert(strlenpt(converted) <= COLS);
2327

2328
2329
2330
    /* 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);
2331

2332
#ifndef USE_SLANG
2333
2334
2335
    /* 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. */
2336
2337
    if (seen_wide)
	wredrawln(edit, line, 1);
2338
#endif
2339

2340
#ifndef DISABLE_COLOR
2341
2342
2343
2344
    /* 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;
2345

2346
2347
2348
2349
	/* If there are multiline regexes, make sure there is a cache. */
	if (openfile->syntax->nmultis > 0)
	    alloc_multidata_if_needed(fileptr);

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

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2366
2367
2368
	    /* 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. */
2369

2370
	    /* First case, tmpcolor is a single-line expression. */
2371
	    if (tmpcolor->end == NULL) {
2372
2373
2374
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2375
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2376
2377
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2378
2379
2380
		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
2381
2382
2383
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2384
		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2385
2386
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2387
			break;
2388
2389
		    /* Translate the match to the beginning of the
		     * line. */
2390
2391
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2392
2393
2394

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2395
			startmatch.rm_eo++;
2396
		    else if (startmatch.rm_so < endpos &&
2397
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2398
2399
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2400
				startmatch.rm_so) - start;
2401

2402
2403
2404
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2405
2406
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2407
2408
2409

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

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

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

2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
		/* 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. */

2452
		while (start_line != NULL && regexec(tmpcolor->start,
2453
2454
2455
2456
			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)
2457
2458
2459
			goto step_two;
		    start_line = start_line->prev;
		}
2460

2461
2462
2463
2464
		/* If no start was found, skip to the next step. */
		if (start_line == NULL)
		    goto step_two;

2465
		/* If a found start has been qualified as an end earlier,
2466
		 * believe it and skip to the next step. */
2467
		if (start_line->multidata != NULL &&
2468
2469
			(start_line->multidata[tmpcolor->id] == CBEGINBEFORE ||
			start_line->multidata[tmpcolor->id] == CSTARTENDHERE))
2470
2471
		    goto step_two;

2472
		/* Skip over a zero-length regex match. */
2473
		if (startmatch.rm_so == startmatch.rm_eo)
2474
2475
		    goto tail_of_loop;

2476
2477
2478
2479
2480
2481
2482
2483
		/* 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 +
2484
2485
2486
				start_col + startmatch.rm_eo, 0, NULL,
				(start_col + startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH)
2487
2488
2489
2490
			/* No end found after this start. */
			break;
		    start_col++;
		    if (regexec(tmpcolor->start, start_line->data +
2491
2492
				start_col, 1, &startmatch,
				REG_NOTBOL) == REG_NOMATCH)
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
			/* 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
2503
			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2504
		    end_line = end_line->next;
2505

2506
2507
2508
2509
2510
2511
2512
		/* 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;
		}
2513

2514
2515
2516
2517
2518
2519
2520
2521
		/* 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;
2522
2523
2524
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CWHOLELINE\n", tmpcolor->id, line);
#endif
2525
2526
2527
2528
		} else {
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
						endmatch.rm_eo) - start);
		    fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
2529
2530
2531
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CBEGINBEFORE\n", tmpcolor->id, line);
#endif
2532
2533
2534
2535
2536
2537
		}
		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;
2538
  step_two:
2539
2540
2541
2542
2543
2544
2545
2546
		/* 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 ||
2547
				start_col + startmatch.rm_so >= endpos)
2548
2549
			/* No more starts on this line. */
			break;
2550

2551
2552
2553
2554
2555
2556
2557
		    /* 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,
2558
				startmatch.rm_so) - start;
2559

2560
		    index = actual_x(converted, x_start);
2561

2562
		    if (regexec(tmpcolor->end, fileptr->data +
2563
				startmatch.rm_eo, 1, &endmatch,
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
				(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 &&
2574
				endmatch.rm_eo > startmatch.rm_so) {
2575
			    paintlen = actual_x(converted + index,
2576
					strnlenpt(fileptr->data,
2577
					endmatch.rm_eo) - start - x_start);
2578

2579
			    assert(0 <= x_start && x_start < COLS);
2580

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

2599
			while (end_line != NULL &&
2600
2601
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
2602
			    end_line = end_line->next;
2603

2604
2605
2606
			/* If there is no end, we're done on this line. */
			if (end_line == NULL)
			    break;
2607

2608
2609
2610
2611
2612
			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;
2613
2614
2615
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CENDAFTER\n", tmpcolor->id, line);
#endif
2616
2617
2618
			/* We've painted to the end of the line, so don't
			 * bother checking for any more starts. */
			break;
2619
		    }
2620
2621
		}
	    }
2622
  tail_of_loop:
2623
2624
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2625
	}
2626
    }
2627
#endif /* !DISABLE_COLOR */
2628

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

2651
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2652
2653
2654
2655
2656

	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
2657

2658
	/* The selected bit of fileptr is on this page. */
2659
2660
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2661
2662
2663
2664

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

2666
2667
2668
2669
2670
	    /* 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. */
2671
2672
2673
2674
2675
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
			start);
2676
2677
2678
2679
2680
2681
2682
2683

	    /* 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;
	    }
2684
2685
2686

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

2687
	    index = actual_x(converted, x_start);
2688

2689
2690
2691
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2692
	    wattron(edit, hilite_attribute);
2693
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2694
		paintlen);
2695
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2696
	}
2697
    }
2698
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2699
2700
}

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

2715
    assert(fileptr != NULL);
2716

2717
#ifndef NANO_TINY
2718
    if (ISSET(SOFTWRAP)) {
2719
2720
	filestruct *tmp;

2721
2722
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2723
    } else
2724
#endif
2725
2726
	line = fileptr->lineno - openfile->edittop->lineno;

2727
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2728
	return 1;
2729

2730
    /* First, blank out the line. */
2731
    blank_line(edit, line, 0, COLS);
2732

2733
2734
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2735
#ifndef NANO_TINY
2736
2737
2738
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2739
#endif
2740
	index = strnlenpt(fileptr->data, index);
2741
    page_start = get_page_start(index);
2742

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

2755
    /* Paint the line. */
2756
    edit_draw(fileptr, converted, line, page_start);
2757
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2758

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

	    /* Expand the line, replacing tabs with spaces, and control
2777
	     * characters with their displayed forms. */
2778
2779
2780
2781
2782
	    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
2783
2784
2785

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2786
	    free(converted);
2787
2788
2789
	    extralinesused++;
	}
    }
2790
#endif /* !NANO_TINY */
2791
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2792
2793
}

2794
2795
2796
2797
/* 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)
2798
2799
{
    return
2800
#ifndef NANO_TINY
2801
	openfile->mark_set ||
2802
#endif
2803
	get_page_start(pww_save) !=
2804
	get_page_start(openfile->placewewant);
2805
2806
}

2807
/* When edittop changes, try and figure out how many lines
2808
 * we really have to work with (i.e. set maxrows). */
2809
2810
2811
2812
2813
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2814
2815
2816
2817
2818
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2819
2820
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2821
	maxrows++;
2822
	n += strlenpt(foo->data) / COLS;
2823
2824
2825
	foo = foo->next;
    }

2826
2827
2828
    if (n < editwinrows)
	maxrows += editwinrows - n;

2829
#ifdef DEBUG
2830
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2831
2832
2833
#endif
}

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

2846
2847
    /* Don't bother scrolling less than one line. */
    if (nlines < 1)
2848
2849
	return;

2850
2851
2852
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2881
2882
    /* 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
2883
     * call edit_refresh() beforehand if we need to. */
2884
    if (nlines == 0 || do_redraw || nlines >= editwinrows) {
2885
	if (do_redraw || nlines >= editwinrows)
2886
	    edit_refresh_needed = TRUE;
2887
2888
	return;
    }
2889
2890
2891

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2892
    scrollok(edit, TRUE);
2893
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
2894
2895
    scrollok(edit, FALSE);

2896
2897
2898
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

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

2907
2908
2909
2910
2911
2912
    /* 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
2913

2914
2915
    if (nlines > editwinrows)
	nlines = editwinrows;
2916
2917
2918

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

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

2928
2929
2930
2931
2932
2933
    /* 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--) {
2934
2935
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
2936
2937
2938
2939
2940
	    if (do_redraw)
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
2941
		openfile->current_x : 0);
2942
	foo = foo->next;
2943
    }
2944
    compute_maxrows();
2945
2946
2947
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2948
 * updated.  Use this if we've moved without changing any text. */
2949
void edit_redraw(filestruct *old_current, size_t pww_save)
2950
{
2951
    filestruct *foo = NULL;
2952
    bool do_redraw = need_screen_update(0) || need_screen_update(pww_save);
2953

2954
2955
    /* If either old_current or current is offscreen, scroll the edit
     * window until it's onscreen and get out. */
2956
    if (old_current->lineno < openfile->edittop->lineno ||
2957
2958
2959
		old_current->lineno >= openfile->edittop->lineno + maxrows ||
		openfile->current->lineno < openfile->edittop->lineno ||
		openfile->current->lineno >= openfile->edittop->lineno + maxrows) {
2960
#ifdef DEBUG
2961
2962
	fprintf(stderr, "edit_redraw(): line %ld was offscreen, oldcurrent = %ld edittop = %ld",
		(long)openfile->current->lineno, (long)old_current->lineno, (long)openfile->edittop->lineno);
2963
#endif
2964

2965
2966
#ifndef NANO_TINY
	/* If the mark is on, update all the lines between old_current
2967
2968
	 * and either the old first line or old last line (depending on
	 * whether we've scrolled up or down) of the edit window. */
2969
	if (openfile->mark_set) {
2970
2971
	    ssize_t old_lineno;

2972
2973
2974
2975
	    if (openfile->edittop->lineno + maxrows <= openfile->filebot->lineno)
		old_lineno = openfile->edittop->lineno + editwinrows;
	    else
		old_lineno = openfile->filebot->lineno;
2976
2977
2978

	    foo = old_current;

2979
	    while (foo->lineno != old_lineno) {
2980
2981
		update_line(foo, 0);

2982
2983
		foo = (foo->lineno > old_lineno) ?
			foo->prev : foo->next;
2984
2985
2986
2987
	    }
	}
#endif /* !NANO_TINY */

2988
	/* Make sure the current line is on the screen. */
2989
	edit_update((ISSET(SMOOTH_SCROLL) && !focusing) ? NONE : CENTER);
2990

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2991
2992
	/* Update old_current if we're not on the same page as
	 * before. */
2993
2994
2995
	if (do_redraw)
	    update_line(old_current, 0);

2996
#ifndef NANO_TINY
2997
2998
2999
	/* If the mark is on, update all the lines between the old first
	 * line or old last line of the edit window (depending on
	 * whether we've scrolled up or down) and current. */
3000
	if (openfile->mark_set) {
3001
	    while (foo->lineno != openfile->current->lineno) {
3002
3003
		update_line(foo, 0);

3004
		foo = (foo->lineno > openfile->current->lineno) ?
3005
3006
3007
3008
3009
			foo->prev : foo->next;
	    }
	}
#endif /* !NANO_TINY */

3010
3011
3012
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3013
3014
3015
    /* Update old_current and current if we're not on the same page as
     * before.  If the mark is on, update all the lines between
     * old_current and current too. */
3016
    foo = old_current;
3017

3018
    while (foo != openfile->current) {
3019
	if (do_redraw)
3020
	    update_line(foo, 0);
3021

3022
#ifndef NANO_TINY
3023
	if (!openfile->mark_set)
3024
3025
#endif
	    break;
3026

3027
#ifndef NANO_TINY
3028
3029
	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
		foo->next;
3030
#endif
3031
    }
3032

3033
    if (do_redraw)
3034
	update_line(openfile->current, openfile->current_x);
3035
3036
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3037
3038
/* 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
3039
3040
void edit_refresh(void)
{
3041
    filestruct *foo;
3042
    int nlines;
3043

3044
    /* Figure out what maxrows should really be. */
3045
    compute_maxrows();
3046

3047
3048
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
3049
3050
	maxrows) {
#ifdef DEBUG
3051
3052
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
3053
3054
#endif

3055
	/* Make sure the current line is on the screen. */
3056
	edit_update(ISSET(SMOOTH_SCROLL) ? NONE : CENTER);
3057
    }
Chris Allegretta's avatar
Chris Allegretta committed
3058

3059
3060
    foo = openfile->edittop;

3061
#ifdef DEBUG
3062
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3063
#endif
3064

3065
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3066
	nlines += update_line(foo, (foo == openfile->current) ?
3067
		openfile->current_x : 0);
3068
3069
3070
	foo = foo->next;
    }

3071
    for (; nlines < editwinrows; nlines++)
3072
3073
3074
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
3075
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3076
3077
}

3078
3079
3080
3081
/* 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. */
3082
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
3083
{
3084
    filestruct *foo = openfile->current;
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
    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)
3096
	goal = editwinrows / 2;
3097
3098
    else {
	goal = openfile->current_y;
3099

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

3105
    for (; goal > 0 && foo->prev != NULL; goal--) {
3106
	foo = foo->prev;
3107
#ifndef NANO_TINY
3108
3109
	if (ISSET(SOFTWRAP) && foo)
	    goal -= strlenpt(foo->data) / COLS;
3110
#endif
3111
    }
3112
    openfile->edittop = foo;
3113
#ifdef DEBUG
3114
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
3115
#endif
3116
    compute_maxrows();
3117
    edit_refresh_needed = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
3118
3119
}

3120
/* Unconditionally redraw the entire screen. */
3121
void total_redraw(void)
3122
{
3123
3124
3125
3126
3127
3128
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
3129
    wrefresh(curscr);
3130
#endif
3131
3132
}

3133
3134
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
3135
3136
void total_refresh(void)
{
3137
    total_redraw();
3138
    titlebar(NULL);
3139
    edit_refresh();
3140
    bottombars(currmenu);
3141
3142
}

3143
3144
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
3145
3146
void display_main_list(void)
{
3147
#ifndef DISABLE_COLOR
3148
3149
3150
    if (openfile->syntax
	  && (openfile->syntax->formatter || openfile->syntax->linter))
	set_lint_or_format_shortcuts();
3151
3152
3153
3154
    else
	set_spell_shortcuts();
#endif

3155
    bottombars(MMAIN);
3156
3157
}

3158
3159
3160
3161
3162
3163
/* 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. */
3164
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3165
{
3166
    filestruct *f;
3167
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3168
    size_t i, cur_xpt = xplustabs() + 1;
3169
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3170
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3171

3172
    assert(openfile->fileage != NULL && openfile->current != NULL);
3173

3174
    f = openfile->current->next;
3175
    c = openfile->current->data[openfile->current_x];
3176
3177

    openfile->current->next = NULL;
3178
    openfile->current->data[openfile->current_x] = '\0';
3179
3180
3181

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

3182
    openfile->current->data[openfile->current_x] = c;
3183
    openfile->current->next = f;
3184

3185
3186
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
3187
	return;
3188
    }
Chris Allegretta's avatar
Chris Allegretta committed
3189

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3190
    /* Display the current cursor position on the statusbar, and set
3191
     * disable_cursorpos to FALSE. */
3192
    linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
3193
    colpct = 100 * cur_xpt / cur_lenpt;
3194
    charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;
3195
3196

    statusbar(
3197
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3198
	(long)openfile->current->lineno,
3199
	(long)openfile->filebot->lineno, linepct,
3200
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3201
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3202

3203
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3204
3205
}

3206
/* Unconditionally display the current cursor position. */
3207
void do_cursorpos_void(void)
3208
{
3209
    do_cursorpos(FALSE);
3210
3211
}

3212
3213
void enable_nodelay(void)
{
3214
3215
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3216
3217
3218
3219
}

void disable_nodelay(void)
{
3220
3221
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3222
3223
}

3224
3225
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3226
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3227
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3228
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3229

3230
    y = get_page_start(y) + COLS - y;
3231
	/* Now y is the number of columns that we can display on this
3232
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3233

3234
3235
3236
3237
3238
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3239
    reset_cursor();
3240
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3241

3242
    if (highlight)
3243
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3244

3245
    /* This is so we can show zero-length matches. */
3246
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3247
	waddch(edit, ' ');
3248
    else
3249
	waddnstr(edit, word, actual_x(word, y));
3250
3251
3252

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

3254
    if (highlight)
3255
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3256
3257
}

3258
#ifndef DISABLE_EXTRA
3259
3260
#define CREDIT_LEN 54
#define XLCREDIT_LEN 9
3261

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3262
3263
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3264
3265
void do_credits(void)
{
3266
3267
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3268
    int kbinput = ERR, crpos = 0, xlpos = 0;
3269
3270
3271
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3272
3273
	VERSION,
	"",
3274
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3275
3276
3277
3278
3279
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3280
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3281
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3282
	"Mark Majeres",
3283
	"Mike Frysinger",
3284
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3295
	NULL,				/* "Special thanks to:" */
3296
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3297
3298
3299
3300
3301
3302
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3303
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3304
	"Linus Torvalds",
3305
	NULL,				/* "the many translators and the TP" */
3306
	NULL,				/* "For ncurses:" */
3307
3308
3309
3310
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3311
3312
3313
3314
3315
3316
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3317
	"(C) 1999 - 2016",
3318
	"Free Software Foundation, Inc.",
3319
3320
3321
3322
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3323
	"http://www.nano-editor.org/"
3324
3325
    };

3326
    const char *xlcredits[XLCREDIT_LEN] = {
3327
3328
3329
3330
3331
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
3332
	N_("the many translators and the TP"),
3333
3334
3335
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3336
    };
3337

3338
3339
3340
3341
3342
3343
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3344
3345
    curs_set(0);
    nodelay(edit, TRUE);
3346

3347
    blank_titlebar();
3348
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3349
    blank_edit();
3350
3351
    blank_statusbar();
    blank_bottombars();
3352

3353
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3354
    wrefresh(edit);
3355
    wrefresh(bottomwin);
3356
    napms(700);
3357

3358
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3359
	if ((kbinput = wgetch(edit)) != ERR)
3360
	    break;
3361

3362
	if (crpos < CREDIT_LEN) {
3363
	    const char *what;
3364
3365
	    size_t start_x;

3366
	    if (credits[crpos] == NULL) {
3367
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3368

3369
		what = _(xlcredits[xlpos]);
3370
		xlpos++;
3371
	    } else
3372
		what = credits[crpos];
3373

3374
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3375
3376
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3377
	}
3378

3379
3380
3381
3382
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3383
	napms(700);
3384

3385
	scrollok(edit, TRUE);
3386
	wscrl(edit, 1);
3387
	scrollok(edit, FALSE);
3388
	wrefresh(edit);
3389

3390
	if ((kbinput = wgetch(edit)) != ERR)
3391
	    break;
3392
	napms(700);
3393

3394
	scrollok(edit, TRUE);
3395
	wscrl(edit, 1);
3396
	scrollok(edit, FALSE);
3397
	wrefresh(edit);
3398
3399
    }

3400
3401
3402
    if (kbinput != ERR)
	ungetch(kbinput);

3403
    if (!old_more_space)
3404
	UNSET(MORE_SPACE);
3405
    if (!old_no_help)
3406
	UNSET(NO_HELP);
3407
    window_init();
3408

3409
    curs_set(1);
3410
    nodelay(edit, FALSE);
3411

3412
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3413
}
3414
#endif /* !DISABLE_EXTRA */