winio.c 101 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
2
3
/**************************************************************************
 *   winio.c                                                              *
 *                                                                        *
4
 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,  *
5
 *   2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.    *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 3, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
 *   any later version.                                                   *
 *                                                                        *
11
12
13
14
 *   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
15
16
17
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
18
19
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
20
21
22
 *                                                                        *
 **************************************************************************/

23
#include "proto.h"
24

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

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

43
#ifndef NANO_TINY
44
45
46
47
48
49
50
51
52
53
54
55
static sig_atomic_t last_sigwinch_counter = 0;

/* Did we receive a SIGWINCH since we were last called? */
bool the_window_resized(void)
{
    if (sigwinch_counter == last_sigwinch_counter)
	return FALSE;

    last_sigwinch_counter = sigwinch_counter;
    regenerate_screen();
    return TRUE;
}
56
#endif
57

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

117
/* Read in a sequence of keystrokes from win and save them in the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
118
119
 * keystroke buffer.  This should only be called when the keystroke
 * buffer is empty. */
120
void get_key_buffer(WINDOW *win)
121
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
122
    int input;
123
    size_t errcount = 0;
124
125
126
127
128

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

129
130
131
132
    /* Just before reading in the first character, display any pending
     * screen updates. */
    doupdate();

133
    /* Read in the first character using whatever mode we're in. */
134
135
    input = wgetch(win);

136
137
138
139
140
#ifndef NANO_TINY
    if (the_window_resized())
	input = KEY_WINCH;
#endif

141
142
143
144
145
146
147
148
149
150
151
    if (input == ERR && nodelay_mode)
	return;

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

152
#ifndef NANO_TINY
153
	if (the_window_resized()) {
154
155
	    input = KEY_WINCH;
	    break;
156
	}
157
158
#endif
	input = wgetch(win);
159
    }
160

161
162
    /* Increment the length of the keystroke buffer, and save the value
     * of the keystroke at the end of it. */
163
    key_buffer_len++;
164
165
    key_buffer = (int *)nmalloc(sizeof(int));
    key_buffer[0] = input;
166

167
168
169
170
171
172
173
#ifndef NANO_TINY
    /* If we got SIGWINCH, get out immediately since the win argument is
     * no longer valid. */
    if (input == KEY_WINCH)
	return;
#endif

174
175
176
177
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
178
	input = wgetch(win);
179

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

184
185
	/* Otherwise, increment the length of the keystroke buffer, and
	 * save the value of the keystroke at the end of it. */
186
	key_buffer_len++;
187
188
189
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
	key_buffer[key_buffer_len - 1] = input;
190
191
    }

192
193
194
    /* Restore waiting mode if it was on. */
    if (!nodelay_mode)
	nodelay(win, FALSE);
195
196

#ifdef DEBUG
197
198
    {
	size_t i;
199
	fprintf(stderr, "\nget_key_buffer(): the sequence of hex codes:");
200
201
202
203
	for (i = 0; i < key_buffer_len; i++)
	    fprintf(stderr, " %3x", key_buffer[i]);
	fprintf(stderr, "\n");
    }
204
#endif
205
}
206

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
207
/* Return the length of the keystroke buffer. */
208
size_t get_key_buffer_len(void)
209
210
211
212
{
    return key_buffer_len;
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
213
/* Add the keystrokes in input to the keystroke buffer. */
214
void unget_input(int *input, size_t input_len)
215
216
{
    /* If input is empty, get out. */
217
    if (input_len == 0)
218
219
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
220
221
    /* If adding input would put the keystroke buffer beyond maximum
     * capacity, only add enough of input to put it at maximum
222
     * capacity. */
223
224
    if (key_buffer_len + input_len < key_buffer_len)
	input_len = (size_t)-1 - key_buffer_len;
225

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
226
227
228
    /* 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. */
229
230
231
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
232

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
233
234
    /* If the keystroke buffer wasn't empty before, move its beginning
     * forward far enough so that we can add input to its beginning. */
235
236
237
    if (key_buffer_len > input_len)
	memmove(key_buffer + input_len, key_buffer,
		(key_buffer_len - input_len) * sizeof(int));
238

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
239
    /* Copy input to the beginning of the keystroke buffer. */
240
    memcpy(key_buffer, input, input_len * sizeof(int));
241
242
}

243
/* Put back the character stored in kbinput, putting it in byte range
244
245
 * beforehand.  If metakey is TRUE, put back the Escape character after
 * putting back kbinput.  If funckey is TRUE, put back the function key
246
 * (a value outside byte range) without putting it in byte range. */
247
void unget_kbinput(int kbinput, bool metakey, bool funckey)
248
{
249
    if (!funckey)
250
	kbinput = (char)kbinput;
251

252
    unget_input(&kbinput, 1);
253

254
    if (metakey) {
255
256
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
257
258
259
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
260
261
262
/* 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
263
 * anything else.  If the keystroke buffer is (still) empty, return NULL. */
264
int *get_input(WINDOW *win, size_t input_len)
265
{
266
    int *input;
267

268
269
    if (key_buffer_len == 0 && win != NULL)
	get_key_buffer(win);
270

271
272
    if (key_buffer_len == 0)
	return NULL;
273

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
285
286
    /* Copy input_len keystrokes from the beginning of the keystroke
     * buffer into input. */
287
    memcpy(input, key_buffer, input_len * sizeof(int));
288

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
289
    /* If the keystroke buffer is empty, mark it as such. */
290
291
292
    if (key_buffer_len == 0) {
	free(key_buffer);
	key_buffer = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
293
294
295
    /* 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. */
296
297
    } else {
	memmove(key_buffer, key_buffer + input_len, key_buffer_len *
298
299
300
		sizeof(int));
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
301
302
303
    }

    return input;
304
305
}

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
323
324
325
    /* If we read from the edit window, blank the statusbar if we need
     * to. */
    if (win == edit)
326
327
	check_statusblank();

328
329
330
331
332
333
    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
334
 * a function key. */
335
int parse_kbinput(WINDOW *win)
336
{
337
    static int escapes = 0, byte_digits = 0;
338
    static bool double_esc = FALSE;
339
    int *kbinput, retval = ERR;
340

341
342
    meta_key = FALSE;
    func_key = FALSE;
343

344
    /* Read in a character. */
345
346
347
348
349
350
    kbinput = get_input(win, 1);

    if (kbinput == NULL && nodelay_mode)
	return 0;

    while (kbinput == NULL)
351
	kbinput = get_input(win, 1);
352

353
354
355
356
357
358
    switch (*kbinput) {
	case ERR:
	    break;
	case NANO_CONTROL_3:
	    /* Increment the escape counter. */
	    escapes++;
359
360
361
362
	    /* If there are four consecutive escapes, discard three of them. */
	    if (escapes > 3)
		escapes = 1;
	    /* Wait for more input. */
363
364
365
366
	    break;
	default:
	    switch (escapes) {
		case 0:
367
		    /* One non-escape: normal input mode. */
368
		    retval = *kbinput;
369
370
		    break;
		case 1:
371
		    /* Reset the escape counter. */
372
		    escapes = 0;
373
		    if (get_key_buffer_len() == 0 || key_buffer[0] == 0x1b) {
374
375
			/* One escape followed by a single non-escape:
			 * meta key sequence mode. */
376
			meta_key = TRUE;
377
			retval = tolower(*kbinput);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
378
		    } else
379
380
			/* One escape followed by a non-escape, and there
			 * are more codes waiting: escape sequence mode. */
381
			retval = parse_escape_sequence(win, *kbinput);
382
383
		    break;
		case 2:
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
		    if (double_esc) {
			/* An "ESC ESC [ X" sequence from Option+arrow. */
			switch (*kbinput) {
			    case 'A':
				retval = KEY_HOME;
				break;
			    case 'B':
				retval = KEY_END;
				break;
#ifndef NANO_TINY
			    case 'C':
				retval = controlright;
				break;
			    case 'D':
				retval = controlleft;
				break;
#endif
			    default:
				retval = ERR;
				break;
			}
			double_esc = FALSE;
			escapes = 0;
		    } else if (get_key_buffer_len() == 0) {
408
409
410
			if (('0' <= *kbinput && *kbinput <= '2' &&
				byte_digits == 0) || ('0' <= *kbinput &&
				*kbinput <= '9' && byte_digits > 0)) {
411
412
413
414
415
416
			    /* Two escapes followed by one or more decimal
			     * digits, and there aren't any other codes
			     * waiting: byte sequence mode.  If the range
			     * of the byte sequence is limited to 2XX (the
			     * first digit is between '0' and '2' and the
			     * others between '0' and '9', interpret it. */
417
418
419
420
421
			    int byte;

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

422
423
424
425
			    /* If we've read in a complete byte sequence,
			     * reset the escape counter and the byte sequence
			     * counter, and put the obtained byte value back
			     * into the key buffer. */
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
			    if (byte != ERR) {
				char *byte_mb;
				int byte_mb_len, *seq, i;

				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. */
451
			    escapes = 0;
452
453
			    if (byte_digits == 0)
				/* Two escapes followed by a non-decimal
454
455
456
457
				 * digit (or a decimal digit that would
				 * create a byte sequence greater than 2XX)
				 * and there aren't any other codes waiting:
				 * control character sequence mode. */
458
459
				retval = get_control_kbinput(*kbinput);
			    else {
460
461
462
				/* An invalid digit in the middle of a byte
				 * sequence: reset the byte sequence counter
				 * and save the code we got as the result. */
463
464
465
				byte_digits = 0;
				retval = *kbinput;
			    }
466
			}
467
468
469
		    } else if (*kbinput=='[') {
			/* This is an iTerm2 sequence: ^[ ^[ [ X. */
			double_esc = TRUE;
470
		    } else {
471
472
473
			/* Two escapes followed by a non-escape, and there
			 * are more codes waiting: combined meta and escape
			 * sequence mode. */
474
			escapes = 0;
475
			meta_key = TRUE;
476
			retval = parse_escape_sequence(win, *kbinput);
477
		    }
478
		    break;
479
480
481
482
		case 3:
		    /* Reset the escape counter. */
		    escapes = 0;
		    if (get_key_buffer_len() == 0)
483
484
			/* Three escapes followed by a non-escape, and no
			 * other codes are waiting: normal input mode. */
485
486
			retval = *kbinput;
		    else
487
488
489
490
			/* Three escapes followed by a non-escape, and more
			 * codes are waiting: combined control character and
			 * escape sequence mode.  First interpret the escape
			 * sequence, then the result as a control sequence. */
491
			retval = get_control_kbinput(
492
				parse_escape_sequence(win, *kbinput));
493
		    break;
494
495
	    }
    }
496

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

637
638
639
640
641
642
643
#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

644
	/* If our result is an extended keypad value (i.e. a value
645
	 * outside of byte range), set func_key to TRUE. */
646
	if (retval != ERR)
647
	    func_key = !is_byte(retval);
648
    }
649
650

#ifdef DEBUG
651
    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);
652
653
#endif

654
655
    free(kbinput);

656
    /* Return the result. */
657
658
659
    return retval;
}

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

1076
    return ERR;
1077
1078
}

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

1098
/* Interpret the escape sequence in the keystroke buffer, the first
1099
1100
 * character of which is kbinput.  Assume that the keystroke buffer
 * isn't empty, and that the initial escape has already been read in. */
1101
int parse_escape_sequence(WINDOW *win, int kbinput)
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
{
    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);
1112
    retval = convert_sequence(seq, seq_len);
1113
1114
1115

    free(seq);

1116
    /* If we got an unrecognized escape sequence, notify the user. */
1117
    if (retval == ERR) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1118
	if (win == edit) {
1119
1120
	    /* TRANSLATORS: This refers to a sequence of escape codes
	     * (from the keyboard) that nano does not know about. */
1121
	    statusline(ALERT, _("Unknown sequence"));
1122
	    suppress_cursorpos = FALSE;
1123
	    lastmessage = HUSH;
1124
1125
1126
1127
	    if (currmenu == MMAIN) {
		reset_cursor();
		curs_set(1);
	    }
1128
1129
1130
	}
    }

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

    return retval;
}

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

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

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

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

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

    return retval;
}

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

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

    return retval;
}

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

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

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

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

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

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

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

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

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

1344
1345
    return retval;
}
1346

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

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

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

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

1362
    unget_input(input, output_len);
1363

1364
    free(input);
1365
1366
}

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

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

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

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

1391
    return retval;
1392
1393
}

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

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

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

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

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

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

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

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

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

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

1444
	    unget_input(seq, uni_mb_len);
1445

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

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

1455
1456
    free(kbinput);

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

    return retval;
}

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

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

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

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

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

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

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

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

		return 0;
	    }
1530

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return NULL;
}

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

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

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

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

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

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

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

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

1708
1709
/* Check if the number of keystrokes needed to blank the statusbar has
 * been pressed.  If so, blank the statusbar, unless constant cursor
1710
 * position display is on and we are in the editing screen. */
1711
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1712
{
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
    if (statusblank == 0)
	return;

    statusblank--;

    /* When editing and 'constantshow' is active, skip the blanking. */
    if (currmenu == MMAIN && ISSET(CONST_UPDATE))
	return;

    if (statusblank == 0) {
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
	wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1727
1728
1729
    }
}

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

1753
1754
1755
1756
1757
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

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

1761
1762
    buf_mb = charalloc(mb_cur_max());

1763
1764
    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
1765

1766
    assert(column <= start_col);
1767

1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
    /* 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);
1783

1784
    index = 0;
1785
    seen_wide = FALSE;
1786

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

1793
	if (is_cntrl_mbchar(buf_mb)) {
1794
	    if (column < start_col) {
1795
1796
		char *character = charalloc(mb_cur_max());
		int charlen, i;
1797

1798
		character = control_mbrep(buf_mb, character, &charlen);
1799

1800
1801
		for (i = 0; i < charlen; i++)
		    converted[index++] = character[i];
1802

1803
		start_col += mbwidth(character);
1804

1805
		free(character);
1806

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

1817
	    converted[index++] = ' ';
1818
	    start_col++;
1819
1820

	    start_index += buf_mb_len;
1821
	}
1822
#endif
1823
1824
    }

1825
    while (buf[start_index] != '\0') {
1826
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1827

1828
1829
1830
	if (mbwidth(buf + start_index) > 1)
	    seen_wide = TRUE;

1831
1832
1833
1834
1835
1836
1837
1838
	/* 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);
	}

1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
	if (*buf_mb == ' ') {
	    /* Show a space as a visible character, or as a space. */
#ifndef NANO_TINY
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i = whitespace_len[0];

		while (i < whitespace_len[0] + whitespace_len[1])
		    converted[index++] = whitespace[i++];
	    } else
#endif
		converted[index++] = ' ';
	    start_col++;
	} else if (*buf_mb == '\t') {
1852
	    /* Show a tab as a visible character, or as as a space. */
1853
#ifndef NANO_TINY
1854
	    if (ISSET(WHITESPACE_DISPLAY)) {
1855
		int i = 0;
1856

1857
1858
		while (i < whitespace_len[0])
		    converted[index++] = whitespace[i++];
1859
	    } else
1860
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1861
		converted[index++] = ' ';
1862
	    start_col++;
1863
	    /* Fill the tab up with the required number of spaces. */
1864
	    while (start_col % tabsize != 0) {
1865
		converted[index++] = ' ';
1866
1867
		start_col++;
	    }
1868
	/* If buf contains a control character, interpret it. */
1869
	} else if (is_cntrl_mbchar(buf_mb)) {
1870
1871
	    char *character = charalloc(mb_cur_max());
	    int charlen, i;
1872

1873
	    converted[index++] = '^';
1874
1875
	    start_col++;

1876
	    character = control_mbrep(buf_mb, character, &charlen);
1877

1878
1879
	    for (i = 0; i < charlen; i++)
		converted[index++] = character[i];
1880

1881
	    start_col += mbwidth(character);
1882

1883
	    free(character);
1884
1885
	/* If buf contains a non-control character, interpret it.  If buf
	 * contains an invalid multibyte sequence, display it as such. */
1886
	} else {
1887
1888
	    char *character = charalloc(mb_cur_max());
	    int charlen, i;
1889

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

1899
1900
	    for (i = 0; i < charlen; i++)
		converted[index++] = character[i];
1901

1902
	    start_col += mbwidth(character);
1903

1904
	    free(character);
1905
1906
	}

1907
	start_index += buf_mb_len;
1908
1909
    }

1910
1911
    free(buf_mb);

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

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

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

1921
    return converted;
1922
1923
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1924
1925
1926
1927
1928
1929
/* 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. */
1930
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
1931
{
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
    size_t verlen, prefixlen, pathlen, statelen;
	/* The width of the different titlebar elements, in columns. */
    size_t pluglen = 0;
	/* The width that "Modified" would take up. */
    size_t offset = 0;
	/* The position at which the center part of the titlebar starts. */
    const char *prefix = "";
	/* What is shown before the path -- "File:", "DIR:", or "". */
    const char *state = "";
	/* The state of the current buffer -- "Modified", "View", or "". */
    char *fragment;
	/* The tail part of the pathname when dottified. */
1944

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

1947
1948
1949
    if (interface_color_pair[TITLE_BAR].bright)
	wattron(topwin, A_BOLD);
    wattron(topwin, interface_color_pair[TITLE_BAR].pairnum);
1950

1951
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
1952

1953
1954
1955
    /* Do as Pico: if there is not enough width available for all items,
     * first sacrifice the version string, then eat up the side spaces,
     * then sacrifice the prefix, and only then start dottifying. */
Chris Allegretta's avatar
Chris Allegretta committed
1956

1957
    /* Figure out the path, prefix and state strings. */
1958
#ifndef DISABLE_BROWSER
1959
1960
1961
1962
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
1963
1964
1965
1966
1967
1968
1969
    {
	if (openfile->filename[0] == '\0')
	    path = _("New Buffer");
	else {
	    path = openfile->filename;
	    prefix = _("File:");
	}
1970

1971
1972
1973
1974
	if (openfile->modified)
	    state = _("Modified");
	else if (ISSET(VIEW_MODE))
	    state = _("View");
1975

1976
1977
	pluglen = strlenpt(_("Modified")) + 1;
    }
1978

1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
    /* Determine the widths of the four elements, including their padding. */
    verlen = strlenpt(BRANDING) + 3;
    prefixlen = strlenpt(prefix);
    if (prefixlen > 0)
	prefixlen++;
    pathlen= strlenpt(path);
    statelen = strlenpt(state) + 2;
    if (statelen > 2) {
	pathlen++;
	pluglen = 0;
1989
1990
    }

1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
    /* Only print the version message when there is room for it. */
    if (verlen + prefixlen + pathlen + pluglen + statelen <= COLS)
	mvwaddstr(topwin, 0, 2, BRANDING);
    else {
	verlen = 2;
	/* If things don't fit yet, give up the placeholder. */
	if (verlen + prefixlen + pathlen + pluglen + statelen > COLS)
	    pluglen = 0;
	/* If things still don't fit, give up the side spaces. */
	if (verlen + prefixlen + pathlen + pluglen + statelen > COLS) {
	    verlen = 0;
	    statelen -= 2;
2003
2004
2005
	}
    }

2006
2007
2008
2009
    /* If we have side spaces left, center the path name. */
    if (verlen > 0)
	offset = verlen + (COLS - (verlen + pluglen + statelen) -
					(prefixlen + pathlen)) / 2;
2010

2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
    /* Only print the prefix when there is room for it. */
    if (verlen + prefixlen + pathlen + pluglen + statelen <= COLS) {
	mvwaddstr(topwin, 0, offset, prefix);
	if (prefixlen > 0)
	    waddstr(topwin, " ");
    } else
	wmove(topwin, 0, offset);

    /* Print the full path if there's room; otherwise, dottify it. */
    if (pathlen + pluglen + statelen <= COLS)
	waddstr(topwin, path);
    else if (5 + statelen <= COLS) {
	waddstr(topwin, "...");
	fragment = display_string(path, 3 + pathlen - COLS + statelen,
					COLS - statelen, FALSE);
	waddstr(topwin, fragment);
	free(fragment);
2028
    }
2029

2030
2031
2032
2033
2034
2035
    /* Right-align the state if there's room; otherwise, trim it. */
    if (statelen > 0 && statelen <= COLS)
	mvwaddstr(topwin, 0, COLS - statelen, state);
    else if (statelen > 0)
	mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));

2036
2037
    wattroff(topwin, A_BOLD);
    wattroff(topwin, interface_color_pair[TITLE_BAR].pairnum);
2038

2039
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2040
    reset_cursor();
2041
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2042
2043
}

2044
2045
2046
2047
2048
2049
/* Display a normal message on the statusbar, quietly. */
void statusbar(const char *msg)
{
    statusline(HUSH, msg);
}

2050
/* Display a message on the statusbar, and set suppress_cursorpos to
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2051
2052
 * TRUE, so that the message won't be immediately overwritten if
 * constant cursor position display is on. */
2053
void statusline(message_type importance, const char *msg, ...)
2054
2055
{
    va_list ap;
2056
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
2057
    size_t start_x;
2058
2059
2060
2061
#ifndef NANO_TINY
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

    UNSET(WHITESPACE_DISPLAY);
2062
#endif
2063
2064
2065
2066
2067

    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(). */
2068
    if (isendwin()) {
2069
2070
2071
2072
2073
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

2074
2075
2076
2077
2078
2079
2080
    /* If there already was an alert message, ignore lesser ones. */
    if ((lastmessage == ALERT && importance != ALERT) ||
		(lastmessage == MILD && importance == HUSH))
	return;

    /* Delay another alert message, to allow an earlier one to be noticed. */
    if (lastmessage == ALERT)
2081
2082
	napms(1200);

2083
    if (importance == ALERT)
2084
	beep();
2085
2086

    lastmessage = importance;
2087

2088
2089
2090
    /* Turn the cursor off while fiddling in the statusbar. */
    curs_set(0);

2091
2092
    blank_statusbar();

2093
2094
2095
2096
    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
2097
    free(bar);
2098
2099

#ifndef NANO_TINY
2100
2101
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2102
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2103
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2104

2105
    wmove(bottomwin, 0, start_x);
2106
2107
2108
    if (interface_color_pair[STATUS_BAR].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2109
2110
2111
2112
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
2113
2114
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2115

2116
    /* Push the message to the screen straightaway. */
2117
    wnoutrefresh(bottomwin);
2118
    doupdate();
2119

2120
    suppress_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2121

2122
    /* If we're doing quick statusbar blanking, blank it after just one
2123
2124
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2125
#ifndef NANO_TINY
2126
2127
2128
    if (ISSET(QUICK_BLANK))
	statusblank = 1;
    else
2129
#endif
2130
	statusblank = 26;
2131
2132
}

2133
2134
/* Display the shortcut list corresponding to menu on the last two rows
 * of the bottom portion of the window. */
2135
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2136
{
2137
    size_t i, colwidth, slen;
2138
2139
    subnfunc *f;
    const sc *s;
2140

2141
2142
2143
    /* Set the global variable to the given menu. */
    currmenu = menu;

Chris Allegretta's avatar
Chris Allegretta committed
2144
2145
2146
    if (ISSET(NO_HELP))
	return;

2147
    if (menu == MMAIN) {
2148
	slen = MAIN_VISIBLE;
2149

2150
	assert(slen <= length_of_list(menu));
2151
    } else {
2152
	slen = length_of_list(menu);
2153

2154
	/* Don't show any more shortcuts than the main list does. */
2155
2156
2157
2158
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2159
2160
2161
2162
    /* 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. */
2163
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2164

2165
    blank_bottombars();
2166

2167
2168
2169
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2170

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

2173
#ifdef DEBUG
2174
	fprintf(stderr, "Checking menu items....");
2175
#endif
2176
	if ((f->menus & menu) == 0)
2177
	    continue;
2178

2179
#ifdef DEBUG
2180
	fprintf(stderr, "found one! f->menus = %x, desc = \"%s\"\n", f->menus, f->desc);
2181
#endif
2182
2183
	s = first_sc_for(menu, f->scfunc);
	if (s == NULL) {
2184
2185
2186
#ifdef DEBUG
	    fprintf(stderr, "Whoops, guess not, no shortcut key found for func!\n");
#endif
2187
2188
	    continue;
	}
2189
	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2190
#ifdef DEBUG
2191
	fprintf(stderr, "Calling onekey with keystr \"%s\" and desc \"%s\"\n", s->keystr, f->desc);
2192
#endif
2193
	onekey(s->keystr, _(f->desc), colwidth + (COLS % colwidth));
2194
	i++;
Chris Allegretta's avatar
Chris Allegretta committed
2195
    }
2196

2197
2198
    wnoutrefresh(bottomwin);
    reset_cursor();
2199
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2200
2201
}

2202
2203
/* 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
2204
 * to write at most length characters, even if length is very small and
2205
2206
 * keystroke and desc are long.  Note that waddnstr(,,(size_t)-1) adds
 * the whole string!  We do not bother padding the entry with blanks. */
2207
void onekey(const char *keystroke, const char *desc, int length)
Chris Allegretta's avatar
Chris Allegretta committed
2208
{
2209
2210
    assert(keystroke != NULL && desc != NULL);

2211
2212
2213
    if (interface_color_pair[KEY_COMBO].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2214
    waddnstr(bottomwin, keystroke, actual_x(keystroke, length));
2215
2216
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2217

2218
    length -= strlenpt(keystroke) + 1;
2219

2220
    if (length > 0) {
2221
	waddch(bottomwin, ' ');
2222
2223
2224
	if (interface_color_pair[FUNCTION_TAG].bright)
	    wattron(bottomwin, A_BOLD);
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
2225
	waddnstr(bottomwin, desc, actual_x(desc, length));
2226
2227
	wattroff(bottomwin, A_BOLD);
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
Chris Allegretta's avatar
Chris Allegretta committed
2228
2229
2230
    }
}

2231
2232
/* Redetermine current_y from the position of current relative to edittop,
 * and put the cursor in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2233
2234
void reset_cursor(void)
{
2235
    size_t xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2236

2237
#ifndef NANO_TINY
2238
    if (ISSET(SOFTWRAP)) {
2239
	filestruct *line = openfile->edittop;
2240
2241
	openfile->current_y = 0;

2242
2243
2244
2245
	while (line != NULL && line != openfile->current) {
	    openfile->current_y += strlenpt(line->data) / COLS + 1;
	    line = line->next;
	}
2246
	openfile->current_y += xpt / COLS;
2247

2248
	if (openfile->current_y < editwinrows)
2249
	    wmove(edit, openfile->current_y, xpt % COLS);
2250
2251
2252
    } else
#endif
    {
2253
	openfile->current_y = openfile->current->lineno -
2254
				openfile->edittop->lineno;
2255
2256
2257
2258

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

2261
2262
2263
2264
2265
2266
2267
2268
/* 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. */
2269
void edit_draw(filestruct *fileptr, const char *converted, int
2270
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2271
{
2272
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2273
2274
2275
2276
2277
2278
2279
2280
2281
    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. */
2282
2283
#endif

2284
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2285
    assert(strlenpt(converted) <= COLS);
2286

2287
2288
2289
    /* 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);
2290

2291
#ifndef USE_SLANG
2292
2293
2294
    /* 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. */
2295
2296
    if (seen_wide)
	wredrawln(edit, line, 1);
2297
#endif
2298

2299
#ifndef DISABLE_COLOR
2300
2301
2302
    /* If color syntaxes are available and turned on, we need to display
     * them. */
    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
2303
	const colortype *varnish = openfile->colorstrings;
2304

2305
2306
2307
2308
	/* If there are multiline regexes, make sure there is a cache. */
	if (openfile->syntax->nmultis > 0)
	    alloc_multidata_if_needed(fileptr);

2309
	for (; varnish != NULL; varnish = varnish->next) {
2310
2311
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
2312
	    int paintlen = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2313
2314
		/* Number of chars to paint on this line.  There are
		 * COLS characters on a whole line. */
2315
	    size_t index;
2316
		/* Index in converted where we paint. */
2317
2318
2319
2320
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2321

2322
	    if (varnish->bright)
2323
		wattron(edit, A_BOLD);
2324
	    wattron(edit, COLOR_PAIR(varnish->pairnum));
2325
2326
2327
	    /* 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. */
2328

2329
2330
	    /* First case: varnish is a single-line expression. */
	    if (varnish->end == NULL) {
2331
2332
2333
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2334
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2335
2336
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2337
2338
2339
		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
2340
2341
2342
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2343
		    if (regexec(varnish->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2344
2345
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2346
			break;
2347
2348
		    /* Translate the match to the beginning of the
		     * line. */
2349
2350
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2351
2352
2353

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2354
			startmatch.rm_eo++;
2355
		    else if (startmatch.rm_so < endpos &&
2356
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2357
2358
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2359
				startmatch.rm_so) - start;
2360

2361
2362
2363
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2364
2365
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2366
2367
2368

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

2369
			mvwaddnstr(edit, line, x_start, converted +
2370
				index, paintlen);
2371
		    }
2372
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2373
		}
2374
	    } else {	/* Second case: varnish is a multiline expression. */
2375
		const filestruct *start_line = fileptr->prev;
2376
		    /* The first line before fileptr that matches 'start'. */
2377
		regoff_t start_col;
2378
		    /* Where the match starts in that line. */
2379
		const filestruct *end_line;
2380
		    /* The line that matches 'end'. */
2381

2382
		/* First see if the multidata was maybe already calculated. */
2383
		if (fileptr->multidata[varnish->id] == CNONE)
2384
		    goto tail_of_loop;
2385
		else if (fileptr->multidata[varnish->id] == CWHOLELINE) {
2386
		    mvwaddnstr(edit, line, 0, converted, -1);
2387
		    goto tail_of_loop;
2388
2389
		} else if (fileptr->multidata[varnish->id] == CBEGINBEFORE) {
		    regexec(varnish->end, fileptr->data, 1, &endmatch, 0);
2390
2391
		    /* If the coloured part is scrolled off, skip it. */
		    if (endmatch.rm_eo <= startpos)
2392
			goto tail_of_loop;
2393
2394
2395
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
			endmatch.rm_eo) - start);
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2396
		    goto tail_of_loop;
2397
		} if (fileptr->multidata[varnish->id] == -1)
2398
		    /* Assume this until proven otherwise below. */
2399
		    fileptr->multidata[varnish->id] = CNONE;
2400

2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
		/* 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. */

2411
		while (start_line != NULL && regexec(varnish->start,
2412
2413
2414
			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. */
2415
		    if (regexec(varnish->end, start_line->data, 0, NULL, 0) == 0)
2416
2417
2418
			goto step_two;
		    start_line = start_line->prev;
		}
2419

2420
2421
2422
2423
		/* If no start was found, skip to the next step. */
		if (start_line == NULL)
		    goto step_two;

2424
		/* If a found start has been qualified as an end earlier,
2425
		 * believe it and skip to the next step. */
2426
		if (start_line->multidata != NULL &&
2427
2428
			(start_line->multidata[varnish->id] == CBEGINBEFORE ||
			start_line->multidata[varnish->id] == CSTARTENDHERE))
2429
2430
		    goto step_two;

2431
		/* Skip over a zero-length regex match. */
2432
		if (startmatch.rm_so == startmatch.rm_eo)
2433
2434
		    goto tail_of_loop;

2435
2436
2437
2438
2439
2440
2441
		/* 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;
2442
		    if (regexec(varnish->end, start_line->data +
2443
2444
2445
				start_col + startmatch.rm_eo, 0, NULL,
				(start_col + startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH)
2446
2447
2448
			/* No end found after this start. */
			break;
		    start_col++;
2449
		    if (regexec(varnish->start, start_line->data +
2450
2451
				start_col, 1, &startmatch,
				REG_NOTBOL) == REG_NOMATCH)
2452
2453
2454
2455
2456
2457
2458
2459
2460
			/* 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;
2461
		while (end_line != NULL && regexec(varnish->end,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2462
			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2463
		    end_line = end_line->next;
2464

2465
2466
2467
2468
		/* 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) {
2469
		    fileptr->multidata[varnish->id] = CBEGINBEFORE;
2470
2471
		    goto step_two;
		}
2472

2473
2474
2475
2476
2477
2478
2479
		/* 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;
2480
		    fileptr->multidata[varnish->id] = CWHOLELINE;
2481
#ifdef DEBUG
2482
    fprintf(stderr, "  Marking for id %i  line %i as CWHOLELINE\n", varnish->id, line);
2483
#endif
2484
2485
2486
		} else {
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
						endmatch.rm_eo) - start);
2487
		    fileptr->multidata[varnish->id] = CBEGINBEFORE;
2488
#ifdef DEBUG
2489
    fprintf(stderr, "  Marking for id %i  line %i as CBEGINBEFORE\n", varnish->id, line);
2490
#endif
2491
2492
2493
2494
2495
2496
		}
		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;
2497
  step_two:
2498
2499
2500
2501
2502
		/* 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) {
2503
		    if (regexec(varnish->start, fileptr->data + start_col,
2504
2505
				1, &startmatch, (start_col == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH ||
2506
				start_col + startmatch.rm_so >= endpos)
2507
2508
			/* No more starts on this line. */
			break;
2509

2510
2511
2512
2513
2514
2515
2516
		    /* 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,
2517
				startmatch.rm_so) - start;
2518

2519
		    index = actual_x(converted, x_start);
2520

2521
		    if (regexec(varnish->end, fileptr->data +
2522
				startmatch.rm_eo, 1, &endmatch,
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
				(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 &&
2533
				endmatch.rm_eo > startmatch.rm_so) {
2534
			    paintlen = actual_x(converted + index,
2535
					strnlenpt(fileptr->data,
2536
					endmatch.rm_eo) - start - x_start);
2537

2538
			    assert(0 <= x_start && x_start < COLS);
2539

2540
			    mvwaddnstr(edit, line, x_start,
2541
					converted + index, paintlen);
2542
			    if (paintlen > 0) {
2543
				fileptr->multidata[varnish->id] = CSTARTENDHERE;
2544
#ifdef DEBUG
2545
    fprintf(stderr, "  Marking for id %i  line %i as CSTARTENDHERE\n", varnish->id, line);
2546
#endif
2547
			    }
2548
2549
			}
			start_col = endmatch.rm_eo;
2550
2551
2552
			/* Skip over a zero-length match. */
			if (endmatch.rm_so == endmatch.rm_eo)
			    start_col += 1;
2553
2554
2555
2556
		    } else {
			/* There is no end on this line.  But we haven't yet
			 * looked for one on later lines. */
			end_line = fileptr->next;
2557

2558
			while (end_line != NULL &&
2559
				regexec(varnish->end, end_line->data,
2560
				0, NULL, 0) == REG_NOMATCH)
2561
			    end_line = end_line->next;
2562

2563
2564
2565
			/* If there is no end, we're done on this line. */
			if (end_line == NULL)
			    break;
2566

2567
2568
2569
2570
			assert(0 <= x_start && x_start < COLS);

			/* Paint the rest of the line. */
			mvwaddnstr(edit, line, x_start, converted + index, -1);
2571
			fileptr->multidata[varnish->id] = CENDAFTER;
2572
#ifdef DEBUG
2573
    fprintf(stderr, "  Marking for id %i  line %i as CENDAFTER\n", varnish->id, line);
2574
#endif
2575
2576
2577
			/* We've painted to the end of the line, so don't
			 * bother checking for any more starts. */
			break;
2578
		    }
2579
2580
		}
	    }
2581
  tail_of_loop:
2582
	    wattroff(edit, A_BOLD);
2583
	    wattroff(edit, COLOR_PAIR(varnish->pairnum));
2584
	}
2585
    }
2586
#endif /* !DISABLE_COLOR */
2587

2588
#ifndef NANO_TINY
2589
    /* If the mark is on, we need to display it. */
2590
    if (openfile->mark_set && (fileptr->lineno <=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2591
	openfile->mark_begin->lineno || fileptr->lineno <=
2592
	openfile->current->lineno) && (fileptr->lineno >=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2593
	openfile->mark_begin->lineno || fileptr->lineno >=
2594
	openfile->current->lineno)) {
2595
	/* fileptr is at least partially selected. */
2596
	const filestruct *top;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2597
	    /* Either current or mark_begin, whichever is first. */
2598
	size_t top_x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2599
	    /* current_x or mark_begin_x, corresponding to top. */
2600
2601
	const filestruct *bot;
	size_t bot_x;
2602
	int x_start;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2603
	    /* Starting column for mvwaddnstr().  Zero-based. */
2604
	int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2605
2606
	    /* Number of characters to paint on this line.  There are
	     * COLS characters on a whole line. */
2607
	size_t index;
2608
	    /* Index in converted where we paint. */
2609

2610
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2611
2612
2613
2614
2615

	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
2616

2617
	/* The selected bit of fileptr is on this page. */
2618
2619
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2620
2621
2622
2623

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

2625
2626
2627
2628
2629
	    /* 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. */
2630
2631
2632
2633
2634
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
			start);
2635
2636
2637
2638
2639
2640
2641
2642

	    /* 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;
	    }
2643
2644
2645

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

2646
	    index = actual_x(converted, x_start);
2647

2648
2649
2650
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2651
	    wattron(edit, hilite_attribute);
2652
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2653
		paintlen);
2654
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2655
	}
2656
    }
2657
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2658
2659
}

2660
/* Just update one line in the edit buffer.  This is basically a wrapper
2661
 * for edit_draw().  The line will be displayed starting with
2662
 * fileptr->data[index].  Likely arguments are current_x or zero.
2663
 * Returns: Number of additional lines consumed (needed for SOFTWRAP). */
2664
int update_line(filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2665
{
2666
    int line = 0;
2667
	/* The line in the edit window that we want to update. */
2668
    int extralinesused = 0;
2669
2670
2671
2672
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2673

2674
    assert(fileptr != NULL);
2675

2676
#ifndef NANO_TINY
2677
    if (ISSET(SOFTWRAP)) {
2678
2679
	filestruct *tmp;

2680
2681
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2682
    } else
2683
#endif
2684
2685
	line = fileptr->lineno - openfile->edittop->lineno;

2686
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2687
	return 1;
2688

2689
    /* First, blank out the line. */
2690
    blank_line(edit, line, 0, COLS);
2691

2692
2693
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2694
#ifndef NANO_TINY
2695
2696
2697
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2698
#endif
2699
	index = strnlenpt(fileptr->data, index);
2700
    page_start = get_page_start(index);
2701

2702
2703
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2704
2705
2706
#ifdef NANO_TINY
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
#else
2707
2708
2709
    converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2710
	fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2711
#endif
2712
#endif /* !NANO_TINY */
2713

2714
    /* Paint the line. */
2715
    edit_draw(fileptr, converted, line, page_start);
2716
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2717

2718
#ifndef NANO_TINY
2719
    if (!ISSET(SOFTWRAP)) {
2720
#endif
2721
2722
2723
2724
	if (page_start > 0)
	    mvwaddch(edit, line, 0, '$');
	if (strlenpt(fileptr->data) > page_start + COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
2725
#ifndef NANO_TINY
2726
    } else {
2727
	size_t full_length = strlenpt(fileptr->data);
2728
	for (index += COLS; index <= full_length && line < editwinrows - 1; index += COLS) {
2729
2730
	    line++;
#ifdef DEBUG
2731
	    fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
2732
#endif
2733
	    blank_line(edit, line, 0, COLS);
2734
2735

	    /* Expand the line, replacing tabs with spaces, and control
2736
	     * characters with their displayed forms. */
2737
2738
2739
2740
2741
	    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
2742
2743
2744

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2745
	    free(converted);
2746
2747
2748
	    extralinesused++;
	}
    }
2749
#endif /* !NANO_TINY */
2750
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2751
2752
}

2753
2754
2755
2756
/* 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)
2757
2758
{
    return
2759
#ifndef NANO_TINY
2760
	openfile->mark_set ||
2761
#endif
2762
	get_page_start(pww_save) != get_page_start(openfile->placewewant);
2763
2764
}

2765
/* When edittop changes, try and figure out how many lines
2766
 * we really have to work with (i.e. set maxrows). */
2767
2768
2769
2770
2771
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2772
2773
2774
2775
2776
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2777
2778
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2779
	maxrows++;
2780
	n += strlenpt(foo->data) / COLS;
2781
2782
2783
	foo = foo->next;
    }

2784
2785
2786
    if (n < editwinrows)
	maxrows += editwinrows - n;

2787
#ifdef DEBUG
2788
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2789
2790
2791
#endif
}

2792
2793
/* 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
2794
2795
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
2796
2797
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is FALSE. */
2798
void edit_scroll(scroll_dir direction, ssize_t nlines)
2799
{
2800
    ssize_t i;
2801
    filestruct *foo;
2802

2803
    assert(nlines > 0);
2804

2805
2806
2807
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

2808
    /* Move the top line of the edit window up or down (depending on the
2809
2810
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
2811
    for (i = nlines; i > 0; i--) {
2812
	if (direction == UPWARD) {
2813
	    if (openfile->edittop == openfile->fileage)
2814
		break;
2815
	    openfile->edittop = openfile->edittop->prev;
2816
	} else {
2817
	    if (openfile->edittop == openfile->filebot)
2818
		break;
2819
	    openfile->edittop = openfile->edittop->next;
2820
	}
2821
2822

#ifndef NANO_TINY
2823
	/* Don't over-scroll on long lines. */
2824
	if (ISSET(SOFTWRAP) && direction == UPWARD) {
2825
	    ssize_t len = strlenpt(openfile->edittop->data) / COLS;
2826
	    i -= len;
2827
	    if (len > 0)
2828
		refresh_needed = TRUE;
2829
	}
2830
#endif
2831
2832
    }

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

2836
2837
2838
2839
    /* Don't bother scrolling zero lines, nor more than the window can hold. */
    if (nlines == 0)
	return;
    if (nlines >= editwinrows)
2840
	refresh_needed = TRUE;
2841

2842
    if (refresh_needed == TRUE)
2843
	return;
2844
2845
2846

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2847
    scrollok(edit, TRUE);
2848
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
2849
2850
    scrollok(edit, FALSE);

2851
2852
2853
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

2854
2855
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
2856
2857
    if ((direction == UPWARD && openfile->edittop ==
	openfile->fileage) || (direction == DOWNWARD &&
2858
2859
	openfile->edittop->lineno + editwinrows - 1 >=
	openfile->filebot->lineno))
2860
	nlines = editwinrows;
2861

2862
2863
2864
2865
2866
2867
    /* 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
2868

2869
2870
    if (nlines > editwinrows)
	nlines = editwinrows;
2871
2872
2873

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

2876
2877
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
2878
    if (direction == DOWNWARD) {
2879
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
2880
2881
2882
	    foo = foo->next;
    }

2883
2884
2885
2886
2887
2888
    /* 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--) {
2889
2890
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
2891
	    if (need_screen_update(0))
2892
2893
2894
2895
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
2896
		openfile->current_x : 0);
2897
	foo = foo->next;
2898
    }
2899
    compute_maxrows();
2900
2901
2902
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2903
 * updated.  Use this if we've moved without changing any text. */
2904
void edit_redraw(filestruct *old_current)
2905
{
2906
2907
2908
2909
    size_t was_pww = openfile->placewewant;

    openfile->placewewant = xplustabs();

2910
2911
    /* If the current line is offscreen, scroll until it's onscreen. */
    if (openfile->current->lineno >= openfile->edittop->lineno + maxrows ||
2912
		openfile->current->lineno < openfile->edittop->lineno) {
2913
	edit_update((focusing || !ISSET(SMOOTH_SCROLL)) ? CENTERING : FLOWING);
2914
	refresh_needed = TRUE;
2915
    }
2916

2917
#ifndef NANO_TINY
2918
2919
2920
    /* If the mark is on, update all lines between old_current and current. */
    if (openfile->mark_set) {
	filestruct *foo = old_current;
2921

2922
	while (foo != openfile->current) {
2923
	    update_line(foo, 0);
2924

2925
2926
2927
	    foo = (foo->lineno > openfile->current->lineno) ?
			foo->prev : foo->next;
	}
2928
2929
2930
2931
2932
2933
    } else
#endif
	/* Otherwise, update old_current only if it differs from current
	 * and was horizontally scrolled. */
	if (old_current != openfile->current && get_page_start(was_pww) > 0)
	    update_line(old_current, 0);
2934
2935
2936
2937
2938

    /* Update current if we've changed page, or if it differs from
     * old_current and needs to be horizontally scrolled. */
    if (need_screen_update(was_pww) || (old_current != openfile->current &&
			get_page_start(openfile->placewewant) > 0))
2939
	update_line(openfile->current, openfile->current_x);
2940
2941
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2942
2943
/* 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
2944
2945
void edit_refresh(void)
{
2946
    filestruct *foo;
2947
    int nlines;
2948

2949
    /* Figure out what maxrows should really be. */
2950
    compute_maxrows();
2951

2952
2953
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
2954
2955
	maxrows) {
#ifdef DEBUG
2956
2957
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
2958
2959
#endif

2960
	/* Make sure the current line is on the screen. */
2961
	edit_update((focusing || !ISSET(SMOOTH_SCROLL)) ? CENTERING : STATIONARY);
2962
    }
Chris Allegretta's avatar
Chris Allegretta committed
2963

2964
2965
    foo = openfile->edittop;

2966
#ifdef DEBUG
2967
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
2968
#endif
2969

2970
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
2971
	nlines += update_line(foo, (foo == openfile->current) ?
2972
		openfile->current_x : 0);
2973
2974
2975
	foo = foo->next;
    }

2976
    for (; nlines < editwinrows; nlines++)
2977
2978
2979
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
2980
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2981
2982
}

2983
2984
2985
2986
2987
2988
/* Move edittop so that current is on the screen.  manner says how it
 * should be moved: CENTERING means that current should end up in the
 * middle of the screen, STATIONARY means that it should stay at the
 * same vertical position, and FLOWING means that it should scroll no
 * more than needed to bring current into view. */
void edit_update(update_type manner)
Chris Allegretta's avatar
Chris Allegretta committed
2989
{
2990
    int goal = 0;
2991

2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
    /* If manner is CENTERING, move edittop half the number of window
     * lines back from current.  If manner is STATIONARY, move edittop
     * back current_y lines if current_y is in range of the screen,
     * 0 lines if current_y is below zero, or (editwinrows - 1) lines
     * if current_y is too big.  This puts current at the same place
     * on the screen as before, or at the top or bottom if current_y is
     * beyond either.  If manner is FLOWING, move edittop back 0 lines
     * or (editwinrows - 1) lines, depending or where current has moved.
     * This puts the cursor on the first or the last line. */
    if (manner == CENTERING)
3002
	goal = editwinrows / 2;
3003
    else if (manner == FLOWING) {
3004
	if (openfile->current->lineno >= openfile->edittop->lineno)
3005
3006
	    goal = editwinrows - 1;
    } else {
3007
	goal = openfile->current_y;
3008

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

3014
3015
3016
3017
3018
    openfile->edittop = openfile->current;

    while (goal > 0 && openfile->edittop->prev != NULL) {
	openfile->edittop = openfile->edittop->prev;
	goal --;
3019
#ifndef NANO_TINY
3020
3021
	if (ISSET(SOFTWRAP))
	    goal -= strlenpt(openfile->edittop->data) / COLS;
3022
#endif
3023
    }
3024
#ifdef DEBUG
3025
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
3026
#endif
3027
    compute_maxrows();
Chris Allegretta's avatar
Chris Allegretta committed
3028
3029
}

3030
/* Unconditionally redraw the entire screen. */
3031
void total_redraw(void)
3032
{
3033
3034
3035
3036
3037
3038
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
3039
    wrefresh(curscr);
3040
#endif
3041
3042
}

3043
3044
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
3045
3046
void total_refresh(void)
{
3047
    total_redraw();
3048
    titlebar(NULL);
3049
    edit_refresh();
3050
    bottombars(currmenu);
3051
3052
}

3053
3054
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
3055
3056
void display_main_list(void)
{
3057
#ifndef DISABLE_COLOR
3058
3059
    if (openfile->syntax &&
		(openfile->syntax->formatter || openfile->syntax->linter))
3060
	set_lint_or_format_shortcuts();
3061
3062
3063
3064
    else
	set_spell_shortcuts();
#endif

3065
    bottombars(MMAIN);
3066
3067
}

3068
/* If constant is TRUE, we display the current cursor position only if
3069
3070
 * suppress_cursorpos is FALSE.  If constant is FALSE, we display the
 * position always.  In any case we reset suppress_cursorpos to FALSE. */
3071
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3072
{
3073
    filestruct *f;
3074
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3075
    size_t i, cur_xpt = xplustabs() + 1;
3076
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3077
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3078

3079
    assert(openfile->fileage != NULL && openfile->current != NULL);
3080

3081
    /* Determine the size of the file up to the cursor. */
3082
    f = openfile->current->next;
3083
    c = openfile->current->data[openfile->current_x];
3084
3085

    openfile->current->next = NULL;
3086
    openfile->current->data[openfile->current_x] = '\0';
3087
3088
3089

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

3090
    openfile->current->data[openfile->current_x] = c;
3091
    openfile->current->next = f;
3092

3093
    /* If the position needs to be suppressed, don't suppress it next time. */
3094
    if (suppress_cursorpos && constant) {
3095
	suppress_cursorpos = FALSE;
3096
	return;
3097
    }
Chris Allegretta's avatar
Chris Allegretta committed
3098

3099
    /* Display the current cursor position on the statusbar. */
3100
    linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
3101
    colpct = 100 * cur_xpt / cur_lenpt;
3102
    charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;
3103

3104
    statusline(HUSH,
3105
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3106
	(long)openfile->current->lineno,
3107
	(long)openfile->filebot->lineno, linepct,
3108
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3109
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3110
3111
3112

    /* Displaying the cursor position should not suppress it next time. */
    suppress_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3113
3114
}

3115
/* Unconditionally display the current cursor position. */
3116
void do_cursorpos_void(void)
3117
{
3118
    do_cursorpos(FALSE);
3119
3120
}

3121
3122
void enable_nodelay(void)
{
3123
3124
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3125
3126
3127
3128
}

void disable_nodelay(void)
{
3129
3130
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3131
3132
}

3133
3134
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3135
void spotlight(bool active, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3136
{
3137
    size_t word_len = strlenpt(word), room;
Chris Allegretta's avatar
Chris Allegretta committed
3138

3139
    /* Compute the number of columns that are available for the word. */
3140
    room = COLS + get_page_start(xplustabs()) - xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
3141

3142
    assert(room > 0);
3143

3144
3145
    if (word_len > room)
	room--;
3146

Chris Allegretta's avatar
Chris Allegretta committed
3147
    reset_cursor();
3148
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3149

3150
    if (active)
3151
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3152

3153
    /* This is so we can show zero-length matches. */
3154
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3155
	waddch(edit, ' ');
3156
    else
3157
	waddnstr(edit, word, actual_x(word, room));
3158

3159
    if (word_len > room)
3160
	waddch(edit, '$');
Chris Allegretta's avatar
Chris Allegretta committed
3161

3162
    if (active)
3163
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3164
3165
}

3166
#ifndef DISABLE_EXTRA
3167
3168
#define CREDIT_LEN 54
#define XLCREDIT_LEN 9
3169

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3170
3171
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3172
3173
void do_credits(void)
{
3174
3175
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3176
    int kbinput = ERR, crpos = 0, xlpos = 0;
3177
3178
3179
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3180
3181
	VERSION,
	"",
3182
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3183
3184
3185
3186
3187
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3188
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3189
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3190
	"Mark Majeres",
3191
	"Mike Frysinger",
3192
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3203
	NULL,				/* "Special thanks to:" */
3204
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3205
3206
3207
3208
3209
3210
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3211
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3212
	"Linus Torvalds",
3213
	NULL,				/* "the many translators and the TP" */
3214
	NULL,				/* "For ncurses:" */
3215
3216
3217
3218
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3219
3220
3221
3222
3223
3224
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3225
	"(C) 1999 - 2016",
3226
	"Free Software Foundation, Inc.",
3227
3228
3229
3230
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3231
	"http://www.nano-editor.org/"
3232
3233
    };

3234
    const char *xlcredits[XLCREDIT_LEN] = {
3235
3236
3237
3238
3239
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
3240
	N_("the many translators and the TP"),
3241
3242
3243
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3244
    };
3245

3246
3247
3248
3249
3250
3251
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3252
3253
    curs_set(0);
    nodelay(edit, TRUE);
3254

3255
    blank_titlebar();
3256
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3257
    blank_edit();
3258
3259
    blank_statusbar();
    blank_bottombars();
3260

3261
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3262
    wrefresh(edit);
3263
    wrefresh(bottomwin);
3264
    napms(700);
3265

3266
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3267
	if ((kbinput = wgetch(edit)) != ERR)
3268
	    break;
3269

3270
	if (crpos < CREDIT_LEN) {
3271
	    const char *what;
3272
3273
	    size_t start_x;

3274
	    if (credits[crpos] == NULL) {
3275
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3276

3277
		what = _(xlcredits[xlpos]);
3278
		xlpos++;
3279
	    } else
3280
		what = credits[crpos];
3281

3282
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3283
3284
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3285
	}
3286

3287
3288
3289
3290
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3291
	napms(700);
3292

3293
	scrollok(edit, TRUE);
3294
	wscrl(edit, 1);
3295
	scrollok(edit, FALSE);
3296
	wrefresh(edit);
3297

3298
	if ((kbinput = wgetch(edit)) != ERR)
3299
	    break;
3300
	napms(700);
3301

3302
	scrollok(edit, TRUE);
3303
	wscrl(edit, 1);
3304
	scrollok(edit, FALSE);
3305
	wrefresh(edit);
3306
3307
    }

3308
3309
3310
    if (kbinput != ERR)
	ungetch(kbinput);

3311
    if (!old_more_space)
3312
	UNSET(MORE_SPACE);
3313
    if (!old_no_help)
3314
	UNSET(NO_HELP);
3315
    window_init();
3316

3317
    nodelay(edit, FALSE);
3318

3319
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3320
}
3321
#endif /* !DISABLE_EXTRA */