winio.c 97.2 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
#include "revision.h"
25

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

32
33
34
35
36
37
#ifdef REVISION
#define BRANDING REVISION
#else
#define BRANDING PACKAGE_STRING
#endif

38
static int *key_buffer = NULL;
39
40
	/* The keystroke buffer, containing all the keystrokes we
	 * haven't handled yet at a given point. */
41
static size_t key_buffer_len = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
42
	/* The length of the keystroke buffer. */
43
44
static bool solitary = FALSE;
	/* Whether an Esc arrived by itself -- not as leader of a sequence. */
45
static int statusblank = 0;
46
	/* The number of keystrokes left before we blank the statusbar. */
47
static bool suppress_cursorpos = FALSE;
48
	/* Should we skip constant position display for one keystroke? */
49
#ifdef USING_OLD_NCURSES
50
51
static bool seen_wide = FALSE;
	/* Whether we've seen a multicolumn character in the current line. */
52
#endif
53

54
#ifndef NANO_TINY
55
56
57
58
59
60
61
62
63
64
65
66
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;
}
67
#endif
68

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

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

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

135
136
137
138
    /* Just before reading in the first character, display any pending
     * screen updates. */
    doupdate();

139
    /* Read in the first character using whatever mode we're in. */
140
141
    input = wgetch(win);

142
#ifndef NANO_TINY
143
144
    if (the_window_resized()) {
	ungetch(input);
145
	input = KEY_WINCH;
146
    }
147
148
#endif

149
150
151
152
153
154
155
156
157
158
159
    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);

160
#ifndef NANO_TINY
161
	if (the_window_resized()) {
162
163
	    input = KEY_WINCH;
	    break;
164
	}
165
166
#endif
	input = wgetch(win);
167
    }
168

169
170
    /* Increment the length of the keystroke buffer, and save the value
     * of the keystroke at the end of it. */
171
    key_buffer_len++;
172
173
    key_buffer = (int *)nmalloc(sizeof(int));
    key_buffer[0] = input;
174

175
176
177
178
179
180
181
#ifndef NANO_TINY
    /* If we got SIGWINCH, get out immediately since the win argument is
     * no longer valid. */
    if (input == KEY_WINCH)
	return;
#endif

182
183
184
185
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
186
	input = wgetch(win);
187

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

192
193
	/* Otherwise, increment the length of the keystroke buffer, and
	 * save the value of the keystroke at the end of it. */
194
	key_buffer_len++;
195
196
197
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
	key_buffer[key_buffer_len - 1] = input;
198
199
    }

200
201
202
    /* Restore waiting mode if it was on. */
    if (!nodelay_mode)
	nodelay(win, FALSE);
203
204

#ifdef DEBUG
205
206
    {
	size_t i;
207
	fprintf(stderr, "\nget_key_buffer(): the sequence of hex codes:");
208
209
210
211
	for (i = 0; i < key_buffer_len; i++)
	    fprintf(stderr, " %3x", key_buffer[i]);
	fprintf(stderr, "\n");
    }
212
#endif
213
}
214

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
215
/* Return the length of the keystroke buffer. */
216
size_t get_key_buffer_len(void)
217
218
219
220
{
    return key_buffer_len;
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
221
/* Add the keystrokes in input to the keystroke buffer. */
222
void unget_input(int *input, size_t input_len)
223
224
{
    /* If input is empty, get out. */
225
    if (input_len == 0)
226
227
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
228
229
    /* If adding input would put the keystroke buffer beyond maximum
     * capacity, only add enough of input to put it at maximum
230
     * capacity. */
231
232
    if (key_buffer_len + input_len < key_buffer_len)
	input_len = (size_t)-1 - key_buffer_len;
233

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
234
235
236
    /* 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. */
237
238
239
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
240

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
241
242
    /* If the keystroke buffer wasn't empty before, move its beginning
     * forward far enough so that we can add input to its beginning. */
243
244
245
    if (key_buffer_len > input_len)
	memmove(key_buffer + input_len, key_buffer,
		(key_buffer_len - input_len) * sizeof(int));
246

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
247
    /* Copy input to the beginning of the keystroke buffer. */
248
    memcpy(key_buffer, input, input_len * sizeof(int));
249
250
}

251
252
253
/* Put the character given in kbinput back into the input stream.  If it
 * is a Meta key, also insert an Escape character in front of it. */
void unget_kbinput(int kbinput, bool metakey)
254
{
255
    unget_input(&kbinput, 1);
256

257
    if (metakey) {
258
259
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
260
261
262
    }
}

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

271
272
    if (key_buffer_len == 0 && win != NULL)
	get_key_buffer(win);
273

274
275
    if (key_buffer_len == 0)
	return NULL;
276

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

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

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

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

    return input;
307
308
}

309
/* Read in a single keystroke, ignoring any that are invalid. */
310
int get_kbinput(WINDOW *win)
311
312
313
{
    int kbinput;

314
    /* Extract one keystroke from the input stream. */
315
    while ((kbinput = parse_kbinput(win)) == ERR)
316
	;
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#ifdef DEBUG
    fprintf(stderr, "after parsing:  kbinput = %d, meta_key = %s\n",
	kbinput, meta_key ? "TRUE" : "FALSE");
#endif

#ifndef NANO_TINY
    if (kbinput == controlleft)
	kbinput = sc_seq_or(do_prev_word_void, 0);
    else if (kbinput == controlright)
	kbinput = sc_seq_or(do_next_word_void, 0);
    else if (kbinput == controlup)
	kbinput = sc_seq_or(do_prev_block, 0);
    else if (kbinput == controldown)
	kbinput = sc_seq_or(do_next_block, 0);
#endif

334
    /* If we read from the edit window, blank the statusbar if needed. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
335
    if (win == edit)
336
337
	check_statusblank();

338
339
340
    return kbinput;
}

341
342
/* Extract a single keystroke from the input stream.  Translate escape
 * sequences and extended keypad codes into their corresponding values.
343
 * Set meta_key to TRUE when appropriate.  Supported extended keypad values
344
345
346
 * are: [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace,
 * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown),
 * the function keys (F1-F16), and the numeric keypad with NumLock off. */
347
int parse_kbinput(WINDOW *win)
348
{
349
    static int escapes = 0, byte_digits = 0;
350
    static bool double_esc = FALSE;
351
    int *kbinput, keycode, retval = ERR;
352

353
    meta_key = FALSE;
354

355
    /* Read in a character. */
356
357
358
359
360
361
    kbinput = get_input(win, 1);

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

    while (kbinput == NULL)
362
	kbinput = get_input(win, 1);
363

364
365
366
    keycode = *kbinput;
    free(kbinput);

367
368
369
370
371
#ifdef DEBUG
    fprintf(stderr, "before parsing:  keycode = %d, escapes = %d, byte_digits = %d\n",
	keycode, escapes, byte_digits);
#endif

372
    if (keycode == NANO_CONTROL_3) {
373
374
	    /* Increment the escape counter. */
	    escapes++;
375
376
377
	    /* If there are four consecutive escapes, discard three of them. */
	    if (escapes > 3)
		escapes = 1;
378
	    solitary = (escapes == 1 && get_key_buffer_len() == 0);
379
    } else if (keycode != ERR) {
380
381
	    switch (escapes) {
		case 0:
382
		    /* One non-escape: normal input mode. */
383
		    retval = keycode;
384
385
		    break;
		case 1:
386
		    /* Reset the escape counter. */
387
		    escapes = 0;
388
		    if ((keycode != 'O' && keycode != 'o' && keycode != '[') ||
389
				get_key_buffer_len() == 0 || *key_buffer == 0x1B) {
390
391
			/* One escape followed by a single non-escape:
			 * meta key sequence mode. */
392
			if (!solitary || (keycode >= 0x20 && keycode < 0x7F))
393
			    meta_key = TRUE;
394
			retval = tolower(keycode);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
395
		    } else
396
397
			/* One escape followed by a non-escape, and there
			 * are more codes waiting: escape sequence mode. */
398
			retval = parse_escape_sequence(win, keycode);
399
400
		    break;
		case 2:
401
402
		    if (double_esc) {
			/* An "ESC ESC [ X" sequence from Option+arrow. */
403
			switch (keycode) {
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
			    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
			}
			double_esc = FALSE;
			escapes = 0;
		    } else if (get_key_buffer_len() == 0) {
422
423
424
			if (('0' <= keycode && keycode <= '2' &&
				byte_digits == 0) || ('0' <= keycode &&
				keycode <= '9' && byte_digits > 0)) {
425
426
427
428
429
430
			    /* 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. */
431
432
433
			    int byte;

			    byte_digits++;
434
			    byte = get_byte_kbinput(keycode);
435

436
437
438
439
			    /* 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. */
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
			    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. */
465
			    escapes = 0;
466
467
			    if (byte_digits == 0)
				/* Two escapes followed by a non-decimal
468
469
470
471
				 * 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. */
472
				retval = get_control_kbinput(keycode);
473
			    else {
474
475
476
				/* An invalid digit in the middle of a byte
				 * sequence: reset the byte sequence counter
				 * and save the code we got as the result. */
477
				byte_digits = 0;
478
				retval = keycode;
479
			    }
480
			}
481
		    } else if (keycode == '[' && key_buffer_len > 0 &&
482
				'A' <= *key_buffer && *key_buffer <= 'D') {
483
484
			/* This is an iTerm2 sequence: ^[ ^[ [ X. */
			double_esc = TRUE;
485
		    } else {
486
487
488
			/* Two escapes followed by a non-escape, and there
			 * are more codes waiting: combined meta and escape
			 * sequence mode. */
489
			escapes = 0;
490
			meta_key = TRUE;
491
			retval = parse_escape_sequence(win, keycode);
492
		    }
493
		    break;
494
495
496
497
		case 3:
		    /* Reset the escape counter. */
		    escapes = 0;
		    if (get_key_buffer_len() == 0)
498
499
			/* Three escapes followed by a non-escape, and no
			 * other codes are waiting: normal input mode. */
500
			retval = keycode;
501
		    else
502
503
504
505
			/* 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. */
506
			retval = get_control_kbinput(
507
				parse_escape_sequence(win, keycode));
508
		    break;
509
510
	    }
    }
511

512
    if (retval != ERR) {
513
	switch (retval) {
514
515
516
517
#ifdef KEY_SLEFT
	    /* Slang doesn't support KEY_SLEFT. */
	    case KEY_SLEFT:
#endif
518
	    case KEY_LEFT:
519
		retval = sc_seq_or(do_left, keycode);
520
		break;
521
522
523
524
#ifdef KEY_SRIGHT
	    /* Slang doesn't support KEY_SRIGHT. */
	    case KEY_SRIGHT:
#endif
525
	    case KEY_RIGHT:
526
		retval = sc_seq_or(do_right, keycode);
527
		break;
528
529
530
531
532
#ifdef KEY_SUP
	    /* ncurses and Slang don't support KEY_SUP. */
	    case KEY_SUP:
#endif
	    case KEY_UP:
533
		retval = sc_seq_or(do_up_void, keycode);
534
535
536
537
538
539
		break;
#ifdef KEY_SDOWN
	    /* ncurses and Slang don't support KEY_SDOWN. */
	    case KEY_SDOWN:
#endif
	    case KEY_DOWN:
540
		retval = sc_seq_or(do_down_void, keycode);
541
		break;
542
543
544
545
#ifdef KEY_SHOME
	    /* HP-UX 10-11 and Slang don't support KEY_SHOME. */
	    case KEY_SHOME:
#endif
546
#ifdef KEY_HOME
547
	    case KEY_HOME:
548
549
#endif
	    case KEY_A1:	/* Home (7) on keypad with NumLock off. */
550
		retval = sc_seq_or(do_home, keycode);
551
		break;
552
553
554
555
556
557
558
559
#ifdef KEY_SEND
	    /* HP-UX 10-11 and Slang don't support KEY_SEND. */
	    case KEY_SEND:
#endif
#ifdef KEY_END
	    case KEY_END:
#endif
	    case KEY_C1:	/* End (1) on keypad with NumLock off. */
560
		retval = sc_seq_or(do_end, keycode);
561
562
563
		break;
	    case KEY_PPAGE:
	    case KEY_A3:	/* PageUp (9) on keypad with NumLock off. */
564
		retval = sc_seq_or(do_page_up, keycode);
565
566
		break;
	    case KEY_NPAGE:
567
	    case KEY_C3:	/* PageDown (3) on keypad with NumLock off. */
568
		retval = sc_seq_or(do_page_down, keycode);
569
570
571
		break;

	    case KEY_ENTER:
572
		retval = sc_seq_or(do_enter, keycode);
573
		break;
574
	    case KEY_BACKSPACE:
575
		retval = sc_seq_or(do_backspace, keycode);
576
		break;
577
578
579
#ifdef KEY_SDC
	    /* Slang doesn't support KEY_SDC. */
	    case KEY_SDC:
580
581
#endif
	    case NANO_CONTROL_8:
582
		if (ISSET(REBIND_DELETE))
583
		    retval = sc_seq_or(do_delete, keycode);
584
		else
585
		    retval = sc_seq_or(do_backspace, keycode);
586
		break;
587
588
589
#ifdef KEY_SIC
	    /* Slang doesn't support KEY_SIC. */
	    case KEY_SIC:
590
		retval = sc_seq_or(do_insertfile_void, keycode);
591
		break;
592
#endif
593
594
595
596
#ifdef KEY_SBEG
	    /* Slang doesn't support KEY_SBEG. */
	    case KEY_SBEG:
#endif
597
598
#ifdef KEY_BEG
	    /* Slang doesn't support KEY_BEG. */
599
600
601
	    case KEY_BEG:
#endif
	    case KEY_B2:	/* Center (5) on keypad with NumLock off. */
602
603
		retval = ERR;
		break;
604
#ifdef KEY_CANCEL
605
606
607
#ifdef KEY_SCANCEL
	    /* Slang doesn't support KEY_SCANCEL. */
	    case KEY_SCANCEL:
608
#endif
609
610
	    /* Slang doesn't support KEY_CANCEL. */
	    case KEY_CANCEL:
611
		retval = first_sc_for(currmenu, do_cancel)->keycode;
612
613
		break;
#endif
614
#ifdef KEY_SUSPEND
615
616
617
618
619
620
#ifdef KEY_SSUSPEND
	    /* Slang doesn't support KEY_SSUSPEND. */
	    case KEY_SSUSPEND:
#endif
	    /* Slang doesn't support KEY_SUSPEND. */
	    case KEY_SUSPEND:
621
		retval = sc_seq_or(do_suspend_void, 0);
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
		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;
642
#endif
643
	}
644
    }
645

646
    /* Return the result. */
647
648
649
    return retval;
}

650
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
651
 * keypad values, into their corresponding key values.  These sequences
652
653
 * are generated when the keypad doesn't support the needed keys.
 * Assume that Escape has already been read in. */
654
int convert_sequence(const int *seq, size_t seq_len)
655
{
656
    if (seq_len > 1) {
657
	switch (seq[0]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
658
	    case 'O':
659
		switch (seq[1]) {
660
		    case '1':
661
662
			if (seq_len > 4  && seq[2] == ';') {

663
664
	switch (seq[3]) {
	    case '2':
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
		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. */
			return arrow_from_abcd(seq[4]);
		    case 'P': /* Esc O 1 ; 2 P == F13 on Terminal. */
			return KEY_F(13);
		    case 'Q': /* Esc O 1 ; 2 Q == F14 on Terminal. */
			return KEY_F(14);
		    case 'R': /* Esc O 1 ; 2 R == F15 on Terminal. */
			return KEY_F(15);
		    case 'S': /* Esc O 1 ; 2 S == F16 on Terminal. */
			return KEY_F(16);
		}
680
681
		break;
	    case '5':
682
683
684
685
686
687
688
689
690
691
		switch (seq[4]) {
		    case 'A': /* Esc O 1 ; 5 A == Ctrl-Up on Terminal. */
			return CONTROL_UP;
		    case 'B': /* Esc O 1 ; 5 B == Ctrl-Down on Terminal. */
			return CONTROL_DOWN;
		    case 'C': /* Esc O 1 ; 5 C == Ctrl-Right on Terminal. */
			return CONTROL_RIGHT;
		    case 'D': /* Esc O 1 ; 5 D == Ctrl-Left on Terminal. */
			return CONTROL_LEFT;
		}
692
693
		break;
	}
694

695
696
			}
			break;
697
		    case '2':
698
			if (seq_len >= 3) {
699
			    switch (seq[2]) {
700
				case 'P': /* Esc O 2 P == F13 on xterm. */
701
				    return KEY_F(13);
702
				case 'Q': /* Esc O 2 Q == F14 on xterm. */
703
				    return KEY_F(14);
704
				case 'R': /* Esc O 2 R == F15 on xterm. */
705
				    return KEY_F(15);
706
				case 'S': /* Esc O 2 S == F16 on xterm. */
707
				    return KEY_F(16);
708
709
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
710
			break;
711
		    case 'A': /* Esc O A == Up on VT100/VT320/xterm. */
712
713
714
		    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. */
715
			return arrow_from_abcd(seq[1]);
716
717
		    case 'E': /* Esc O E == Center (5) on numeric keypad
			       * with NumLock off on xterm. */
718
			return KEY_B2;
719
		    case 'F': /* Esc O F == End on xterm/Terminal. */
720
			return KEY_END;
721
		    case 'H': /* Esc O H == Home on xterm/Terminal. */
722
			return KEY_HOME;
723
		    case 'M': /* Esc O M == Enter on numeric keypad with
724
			       * NumLock off on VT100/VT220/VT320/xterm/
725
			       * rxvt/Eterm. */
726
			return KEY_ENTER;
727
		    case 'P': /* Esc O P == F1 on VT100/VT220/VT320/Mach
728
			       * console. */
729
			return KEY_F(1);
730
		    case 'Q': /* Esc O Q == F2 on VT100/VT220/VT320/Mach
731
			       * console. */
732
			return KEY_F(2);
733
		    case 'R': /* Esc O R == F3 on VT100/VT220/VT320/Mach
734
			       * console. */
735
			return KEY_F(3);
736
		    case 'S': /* Esc O S == F4 on VT100/VT220/VT320/Mach
737
			       * console. */
738
			return KEY_F(4);
739
		    case 'T': /* Esc O T == F5 on Mach console. */
740
			return KEY_F(5);
741
		    case 'U': /* Esc O U == F6 on Mach console. */
742
			return KEY_F(6);
743
		    case 'V': /* Esc O V == F7 on Mach console. */
744
			return KEY_F(7);
745
		    case 'W': /* Esc O W == F8 on Mach console. */
746
			return KEY_F(8);
747
		    case 'X': /* Esc O X == F9 on Mach console. */
748
			return KEY_F(9);
749
		    case 'Y': /* Esc O Y == F10 on Mach console. */
750
			return KEY_F(10);
751
		    case 'a': /* Esc O a == Ctrl-Up on rxvt. */
752
			return CONTROL_UP;
753
		    case 'b': /* Esc O b == Ctrl-Down on rxvt. */
754
			return CONTROL_DOWN;
755
		    case 'c': /* Esc O c == Ctrl-Right on rxvt. */
756
			return CONTROL_RIGHT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
757
		    case 'd': /* Esc O d == Ctrl-Left on rxvt. */
758
			return CONTROL_LEFT;
759
		    case 'j': /* Esc O j == '*' on numeric keypad with
760
			       * NumLock off on VT100/VT220/VT320/xterm/
761
			       * rxvt/Eterm/Terminal. */
762
			return '*';
763
		    case 'k': /* Esc O k == '+' on numeric keypad with
764
			       * NumLock off on VT100/VT220/VT320/xterm/
765
			       * rxvt/Eterm/Terminal. */
766
			return '+';
767
		    case 'l': /* Esc O l == ',' on numeric keypad with
768
			       * NumLock off on VT100/VT220/VT320/xterm/
769
			       * rxvt/Eterm/Terminal. */
770
			return ',';
771
		    case 'm': /* Esc O m == '-' on numeric keypad with
772
			       * NumLock off on VT100/VT220/VT320/xterm/
773
			       * rxvt/Eterm/Terminal. */
774
			return '-';
775
		    case 'n': /* Esc O n == Delete (.) on numeric keypad
776
			       * with NumLock off on VT100/VT220/VT320/
777
			       * xterm/rxvt/Eterm/Terminal. */
778
			return KEY_DC;
779
		    case 'o': /* Esc O o == '/' on numeric keypad with
780
			       * NumLock off on VT100/VT220/VT320/xterm/
781
			       * rxvt/Eterm/Terminal. */
782
			return '/';
783
		    case 'p': /* Esc O p == Insert (0) on numeric keypad
784
			       * with NumLock off on VT100/VT220/VT320/
785
			       * rxvt/Eterm/Terminal. */
786
			return KEY_IC;
787
		    case 'q': /* Esc O q == End (1) on numeric keypad
788
			       * with NumLock off on VT100/VT220/VT320/
789
			       * rxvt/Eterm/Terminal. */
790
			return KEY_END;
791
		    case 'r': /* Esc O r == Down (2) on numeric keypad
792
			       * with NumLock off on VT100/VT220/VT320/
793
			       * rxvt/Eterm/Terminal. */
794
			return KEY_DOWN;
795
		    case 's': /* Esc O s == PageDown (3) on numeric
796
			       * keypad with NumLock off on VT100/VT220/
797
			       * VT320/rxvt/Eterm/Terminal. */
798
			return KEY_NPAGE;
799
		    case 't': /* Esc O t == Left (4) on numeric keypad
800
			       * with NumLock off on VT100/VT220/VT320/
801
			       * rxvt/Eterm/Terminal. */
802
			return KEY_LEFT;
803
		    case 'u': /* Esc O u == Center (5) on numeric keypad
804
805
			       * with NumLock off on VT100/VT220/VT320/
			       * rxvt/Eterm. */
806
			return KEY_B2;
807
		    case 'v': /* Esc O v == Right (6) on numeric keypad
808
			       * with NumLock off on VT100/VT220/VT320/
809
			       * rxvt/Eterm/Terminal. */
810
			return KEY_RIGHT;
811
		    case 'w': /* Esc O w == Home (7) on numeric keypad
812
			       * with NumLock off on VT100/VT220/VT320/
813
			       * rxvt/Eterm/Terminal. */
814
			return KEY_HOME;
815
		    case 'x': /* Esc O x == Up (8) on numeric keypad
816
			       * with NumLock off on VT100/VT220/VT320/
817
			       * rxvt/Eterm/Terminal. */
818
			return KEY_UP;
819
		    case 'y': /* Esc O y == PageUp (9) on numeric keypad
820
			       * with NumLock off on VT100/VT220/VT320/
821
			       * rxvt/Eterm/Terminal. */
822
			return KEY_PPAGE;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
823
824
825
		}
		break;
	    case 'o':
826
		switch (seq[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
827
		    case 'a': /* Esc o a == Ctrl-Up on Eterm. */
828
			return CONTROL_UP;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
829
		    case 'b': /* Esc o b == Ctrl-Down on Eterm. */
830
			return CONTROL_DOWN;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
831
		    case 'c': /* Esc o c == Ctrl-Right on Eterm. */
832
			return CONTROL_RIGHT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
833
		    case 'd': /* Esc o d == Ctrl-Left on Eterm. */
834
			return CONTROL_LEFT;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
835
836
837
		}
		break;
	    case '[':
838
		switch (seq[1]) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
839
		    case '1':
840
			if (seq_len > 3 && seq[3] == '~') {
841
			    switch (seq[2]) {
842
				case '1': /* Esc [ 1 1 ~ == F1 on rxvt/Eterm. */
843
				    return KEY_F(1);
844
				case '2': /* Esc [ 1 2 ~ == F2 on rxvt/Eterm. */
845
				    return KEY_F(2);
846
				case '3': /* Esc [ 1 3 ~ == F3 on rxvt/Eterm. */
847
				    return KEY_F(3);
848
				case '4': /* Esc [ 1 4 ~ == F4 on rxvt/Eterm. */
849
				    return KEY_F(4);
850
851
				case '5': /* Esc [ 1 5 ~ == F5 on xterm/
					   * rxvt/Eterm. */
852
				    return KEY_F(5);
853
				case '7': /* Esc [ 1 7 ~ == F6 on
854
855
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
856
				    return KEY_F(6);
857
				case '8': /* Esc [ 1 8 ~ == F7 on
858
859
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
860
				    return KEY_F(7);
861
				case '9': /* Esc [ 1 9 ~ == F8 on
862
863
					   * VT220/VT320/Linux console/
					   * xterm/rxvt/Eterm. */
864
				    return KEY_F(8);
865
866
867
			    }
			} else if (seq_len > 4 && seq[2] == ';') {

868
	switch (seq[3]) {
869
	    case '2':
870
871
872
873
874
875
876
		switch (seq[4]) {
		    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. */
			return arrow_from_abcd(seq[4]);
		}
877
878
		break;
	    case '5':
879
880
881
882
883
884
885
886
887
888
		switch (seq[4]) {
		    case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on xterm. */
			return CONTROL_UP;
		    case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on xterm. */
			return CONTROL_DOWN;
		    case 'C': /* Esc [ 1 ; 5 C == Ctrl-Right on xterm. */
			return CONTROL_RIGHT;
		    case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on xterm. */
			return CONTROL_LEFT;
		}
889
890
		break;
	}
891
892
893
894

			} else if (seq_len > 2 && seq[2] == '~')
			    /* Esc [ 1 ~ == Home on VT320/Linux console. */
			    return KEY_HOME;
895
896
			break;
		    case '2':
897
			if (seq_len > 3 && seq[3] == '~') {
898
			    switch (seq[2]) {
899
900
				case '0': /* Esc [ 2 0 ~ == F9 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
901
				    return KEY_F(9);
902
903
				case '1': /* Esc [ 2 1 ~ == F10 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
904
				    return KEY_F(10);
905
906
				case '3': /* Esc [ 2 3 ~ == F11 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
907
				    return KEY_F(11);
908
909
				case '4': /* Esc [ 2 4 ~ == F12 on VT220/VT320/
					   * Linux console/xterm/rxvt/Eterm. */
910
				    return KEY_F(12);
911
912
				case '5': /* Esc [ 2 5 ~ == F13 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
913
				    return KEY_F(13);
914
915
				case '6': /* Esc [ 2 6 ~ == F14 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
916
				    return KEY_F(14);
917
918
				case '8': /* Esc [ 2 8 ~ == F15 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
919
				    return KEY_F(15);
920
921
				case '9': /* Esc [ 2 9 ~ == F16 on VT220/VT320/
					   * Linux console/rxvt/Eterm. */
922
				    return KEY_F(16);
923
			    }
924
925
926
927
			} else if (seq_len > 2 && seq[2] == '~')
			    /* Esc [ 2 ~ == Insert on VT220/VT320/
			     * Linux console/xterm/Terminal. */
			    return KEY_IC;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
928
			break;
929
		    case '3': /* Esc [ 3 ~ == Delete on VT220/VT320/
930
			       * Linux console/xterm/Terminal. */
931
			return KEY_DC;
932
		    case '4': /* Esc [ 4 ~ == End on VT220/VT320/Linux
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
933
			       * console/xterm. */
934
			return KEY_END;
935
		    case '5': /* Esc [ 5 ~ == PageUp on VT220/VT320/
936
937
			       * Linux console/xterm/Terminal;
			       * Esc [ 5 ^ == PageUp on Eterm. */
938
			return KEY_PPAGE;
939
		    case '6': /* Esc [ 6 ~ == PageDown on VT220/VT320/
940
			       * Linux console/xterm/Terminal;
941
			       * Esc [ 6 ^ == PageDown on Eterm. */
942
			return KEY_NPAGE;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
943
		    case '7': /* Esc [ 7 ~ == Home on rxvt. */
944
			return KEY_HOME;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
945
		    case '8': /* Esc [ 8 ~ == End on rxvt. */
946
			return KEY_END;
947
		    case '9': /* Esc [ 9 == Delete on Mach console. */
948
			return KEY_DC;
949
		    case '@': /* Esc [ @ == Insert on Mach console. */
950
			return KEY_IC;
951
		    case 'A': /* Esc [ A == Up on ANSI/VT220/Linux
952
			       * console/FreeBSD console/Mach console/
953
			       * rxvt/Eterm/Terminal. */
954
		    case 'B': /* Esc [ B == Down on ANSI/VT220/Linux
955
			       * console/FreeBSD console/Mach console/
956
			       * rxvt/Eterm/Terminal. */
957
		    case 'C': /* Esc [ C == Right on ANSI/VT220/Linux
958
			       * console/FreeBSD console/Mach console/
959
			       * rxvt/Eterm/Terminal. */
960
		    case 'D': /* Esc [ D == Left on ANSI/VT220/Linux
961
			       * console/FreeBSD console/Mach console/
962
			       * rxvt/Eterm/Terminal. */
963
			return arrow_from_abcd(seq[1]);
964
		    case 'E': /* Esc [ E == Center (5) on numeric keypad
965
966
			       * with NumLock off on FreeBSD console/
			       * Terminal. */
967
			return KEY_B2;
968
		    case 'F': /* Esc [ F == End on FreeBSD console/Eterm. */
969
			return KEY_END;
970
		    case 'G': /* Esc [ G == PageDown on FreeBSD console. */
971
			return KEY_NPAGE;
972
		    case 'H': /* Esc [ H == Home on ANSI/VT220/FreeBSD
973
			       * console/Mach console/Eterm. */
974
			return KEY_HOME;
975
		    case 'I': /* Esc [ I == PageUp on FreeBSD console. */
976
			return KEY_PPAGE;
977
		    case 'L': /* Esc [ L == Insert on ANSI/FreeBSD console. */
978
			return KEY_IC;
979
		    case 'M': /* Esc [ M == F1 on FreeBSD console. */
980
			return KEY_F(1);
981
		    case 'N': /* Esc [ N == F2 on FreeBSD console. */
982
			return KEY_F(2);
983
		    case 'O':
984
			if (seq_len > 2) {
985
			    switch (seq[2]) {
986
				case 'P': /* Esc [ O P == F1 on xterm. */
987
				    return KEY_F(1);
988
				case 'Q': /* Esc [ O Q == F2 on xterm. */
989
				    return KEY_F(2);
990
				case 'R': /* Esc [ O R == F3 on xterm. */
991
				    return KEY_F(3);
992
				case 'S': /* Esc [ O S == F4 on xterm. */
993
				    return KEY_F(4);
994
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
995
			} else
996
			    /* Esc [ O == F3 on FreeBSD console. */
997
			    return KEY_F(3);
998
999
			break;
		    case 'P': /* Esc [ P == F4 on FreeBSD console. */
1000
			return KEY_F(4);
1001
		    case 'Q': /* Esc [ Q == F5 on FreeBSD console. */
1002
			return KEY_F(5);
1003
		    case 'R': /* Esc [ R == F6 on FreeBSD console. */
1004
			return KEY_F(6);
1005
		    case 'S': /* Esc [ S == F7 on FreeBSD console. */
1006
			return KEY_F(7);
1007
		    case 'T': /* Esc [ T == F8 on FreeBSD console. */
1008
			return KEY_F(8);
1009
		    case 'U': /* Esc [ U == PageDown on Mach console. */
1010
			return KEY_NPAGE;
1011
		    case 'V': /* Esc [ V == PageUp on Mach console. */
1012
			return KEY_PPAGE;
1013
		    case 'W': /* Esc [ W == F11 on FreeBSD console. */
1014
			return KEY_F(11);
1015
		    case 'X': /* Esc [ X == F12 on FreeBSD console. */
1016
			return KEY_F(12);
1017
		    case 'Y': /* Esc [ Y == End on Mach console. */
1018
			return KEY_END;
1019
		    case 'Z': /* Esc [ Z == F14 on FreeBSD console. */
1020
			return KEY_F(14);
1021
		    case 'a': /* Esc [ a == Shift-Up on rxvt/Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1022
		    case 'b': /* Esc [ b == Shift-Down on rxvt/Eterm. */
1023
		    case 'c': /* Esc [ c == Shift-Right on rxvt/Eterm. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1024
		    case 'd': /* Esc [ d == Shift-Left on rxvt/Eterm. */
1025
			return arrow_from_abcd(seq[1]);
1026
		    case '[':
1027
			if (seq_len > 2 ) {
1028
			    switch (seq[2]) {
1029
1030
				case 'A': /* Esc [ [ A == F1 on Linux
					   * console. */
1031
				    return KEY_F(1);
1032
1033
				case 'B': /* Esc [ [ B == F2 on Linux
					   * console. */
1034
				    return KEY_F(2);
1035
1036
				case 'C': /* Esc [ [ C == F3 on Linux
					   * console. */
1037
				    return KEY_F(3);
1038
1039
				case 'D': /* Esc [ [ D == F4 on Linux
					   * console. */
1040
				    return KEY_F(4);
1041
1042
				case 'E': /* Esc [ [ E == F5 on Linux
					   * console. */
1043
				    return KEY_F(5);
1044
1045
			    }
			}
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1046
1047
1048
1049
			break;
		}
		break;
	}
1050
1051
    }

1052
    return ERR;
1053
1054
}

1055
1056
/* Return the equivalent arrow-key value for the first four letters
 * in the alphabet, common to many escape sequences. */
1057
int arrow_from_abcd(int kbinput)
1058
1059
1060
{
    switch (tolower(kbinput)) {
	case 'a':
1061
	    return KEY_UP;
1062
	case 'b':
1063
	    return KEY_DOWN;
1064
	case 'c':
1065
	    return KEY_RIGHT;
1066
	case 'd':
1067
	    return KEY_LEFT;
1068
1069
1070
1071
1072
	default:
	    return ERR;
    }
}

1073
/* Interpret the escape sequence in the keystroke buffer, the first
1074
1075
 * character of which is kbinput.  Assume that the keystroke buffer
 * isn't empty, and that the initial escape has already been read in. */
1076
int parse_escape_sequence(WINDOW *win, int kbinput)
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
{
    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);
1087
    retval = convert_sequence(seq, seq_len);
1088
1089
1090

    free(seq);

1091
    /* If we got an unrecognized escape sequence, notify the user. */
1092
    if (retval == ERR) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1093
	if (win == edit) {
1094
1095
	    /* TRANSLATORS: This refers to a sequence of escape codes
	     * (from the keyboard) that nano does not know about. */
1096
	    statusline(ALERT, _("Unknown sequence"));
1097
	    suppress_cursorpos = FALSE;
1098
	    lastmessage = HUSH;
1099
1100
1101
1102
	    if (currmenu == MMAIN) {
		reset_cursor();
		curs_set(1);
	    }
1103
1104
1105
	}
    }

1106
#ifdef DEBUG
1107
1108
    fprintf(stderr, "parse_escape_sequence(): kbinput = %d, seq_len = %lu, retval = %d\n",
		kbinput, (unsigned long)seq_len, retval);
1109
1110
1111
1112
1113
#endif

    return retval;
}

1114
1115
/* Translate a byte sequence: turn a three-digit decimal number (from
 * 000 to 255) into its corresponding byte value. */
1116
int get_byte_kbinput(int kbinput)
1117
{
1118
    static int byte_digits = 0, byte = 0;
1119
    int retval = ERR;
1120

1121
1122
    /* Increment the byte digit counter. */
    byte_digits++;
1123

1124
    switch (byte_digits) {
1125
	case 1:
1126
1127
	    /* First digit: This must be from zero to two.  Put it in
	     * the 100's position of the byte sequence holder. */
1128
	    if ('0' <= kbinput && kbinput <= '2')
1129
		byte = (kbinput - '0') * 100;
1130
	    else
1131
1132
		/* This isn't the start of a byte sequence.  Return this
		 * character as the result. */
1133
1134
1135
		retval = kbinput;
	    break;
	case 2:
1136
1137
1138
1139
	    /* 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. */
1140
1141
1142
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1143
	    else
1144
1145
		/* This isn't the second digit of a byte sequence.
		 * Return this character as the result. */
1146
1147
1148
		retval = kbinput;
	    break;
	case 3:
1149
	    /* Third digit: This must be from zero to five if the first
1150
1151
1152
	     * 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. */
1153
1154
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
1155
		byte += kbinput - '0';
1156
		/* The byte sequence is complete. */
1157
		retval = byte;
1158
	    } else
1159
1160
		/* This isn't the third digit of a byte sequence.
		 * Return this character as the result. */
1161
1162
		retval = kbinput;
	    break;
1163
	default:
1164
1165
1166
	    /* If there are more than three digits, return this
	     * character as the result.  (Maybe we should produce an
	     * error instead?) */
1167
1168
1169
1170
1171
1172
1173
1174
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1175
	byte = 0;
1176
1177
1178
    }

#ifdef DEBUG
1179
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1180
1181
1182
1183
1184
#endif

    return retval;
}

1185
#ifdef ENABLE_UTF8
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1186
/* If the character in kbinput is a valid hexadecimal digit, multiply it
1187
 * by factor and add the result to uni, and return ERR to signify okay. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1188
1189
1190
1191
1192
1193
1194
long add_unicode_digit(int kbinput, long factor, long *uni)
{
    if ('0' <= kbinput && kbinput <= '9')
	*uni += (kbinput - '0') * factor;
    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
	*uni += (tolower(kbinput) - 'a' + 10) * factor;
    else
1195
1196
	/* The character isn't hexadecimal; give it as the result. */
	return (long)kbinput;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1197

1198
    return ERR;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1199
1200
}

1201
/* Translate a Unicode sequence: turn a six-digit hexadecimal number
1202
 * (from 000000 to 10FFFF, case-insensitive) into its corresponding
1203
 * multibyte value. */
1204
long get_unicode_kbinput(WINDOW *win, int kbinput)
1205
{
1206
1207
1208
    static int uni_digits = 0;
    static long uni = 0;
    long retval = ERR;
1209

1210
    /* Increment the Unicode digit counter. */
1211
    uni_digits++;
1212

1213
    switch (uni_digits) {
1214
	case 1:
1215
1216
1217
1218
	    /* The first digit must be zero or one.  Put it in the
	     * 0x100000's position of the Unicode sequence holder.
	     * Otherwise, return the character itself as the result. */
	    if (kbinput == '0' || kbinput == '1')
1219
		uni = (kbinput - '0') * 0x100000;
1220
1221
1222
1223
	    else
		retval = kbinput;
	    break;
	case 2:
1224
1225
1226
	    /* The second digit must be zero if the first was one, but
	     * may be any hexadecimal value if the first was zero. */
	    if (kbinput == '0' || uni == 0)
1227
		retval = add_unicode_digit(kbinput, 0x10000, &uni);
1228
1229
1230
1231
	    else
		retval = kbinput;
	    break;
	case 3:
1232
	    /* Later digits may be any hexadecimal value. */
1233
	    retval = add_unicode_digit(kbinput, 0x1000, &uni);
1234
	    break;
1235
	case 4:
1236
	    retval = add_unicode_digit(kbinput, 0x100, &uni);
1237
	    break;
1238
	case 5:
1239
	    retval = add_unicode_digit(kbinput, 0x10, &uni);
1240
	    break;
1241
	case 6:
1242
	    retval = add_unicode_digit(kbinput, 0x1, &uni);
1243
1244
	    /* If also the sixth digit was a valid hexadecimal value, then
	     * the Unicode sequence is complete, so return it. */
1245
	    if (retval == ERR)
1246
		retval = uni;
1247
1248
	    break;
    }
1249

1250
    /* Show feedback only when editing, not when at a prompt. */
1251
    if (retval == ERR && win == edit) {
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
	char partial[7] = "......";

	/* Construct the partial result, right-padding it with dots. */
	snprintf(partial, uni_digits + 1, "%06lX", uni);
	partial[uni_digits] = '.';

	/* TRANSLATORS: This is shown while a six-digit hexadecimal
	 * Unicode character code (%s) is being typed in. */
	statusline(HUSH, _("Unicode Input: %s"), partial);
    }
1262

1263
#ifdef DEBUG
1264
1265
    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n",
						kbinput, uni_digits, uni, retval);
1266
1267
#endif

1268
1269
1270
1271
    /* If we have an end result, reset the Unicode digit counter. */
    if (retval != ERR)
	uni_digits = 0;

1272
1273
    return retval;
}
1274
#endif /* ENABLE_UTF8 */
1275

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

1282
    /* Ctrl-Space (Ctrl-2, Ctrl-@, Ctrl-`) */
1283
1284
    if (kbinput == ' ' || kbinput == '2')
	retval = NANO_CONTROL_SPACE;
1285
1286
    /* Ctrl-/ (Ctrl-7, Ctrl-_) */
    else if (kbinput == '/')
1287
	retval = NANO_CONTROL_7;
1288
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-/, Ctrl-_) */
1289
1290
1291
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
1292
1293
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
1294
1295
    /* Ctrl-@ (Ctrl-Space, Ctrl-2, Ctrl-`) to Ctrl-_ (Ctrl-/, Ctrl-7) */
    else if ('@' <= kbinput && kbinput <= '_')
1296
	retval = kbinput - '@';
1297
1298
    /* Ctrl-` (Ctrl-2, Ctrl-Space, Ctrl-@) to Ctrl-~ (Ctrl-6, Ctrl-^) */
    else if ('`' <= kbinput && kbinput <= '~')
1299
	retval = kbinput - '`';
1300
1301
1302
    else
	retval = kbinput;

1303
#ifdef DEBUG
1304
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1305
1306
#endif

1307
1308
    return retval;
}
1309

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1310
1311
/* Put the output-formatted characters in output back into the keystroke
 * buffer, so that they can be parsed and displayed as output again. */
1312
void unparse_kbinput(char *output, size_t output_len)
1313
{
1314
1315
    int *input;
    size_t i;
1316

1317
1318
1319
1320
    if (output_len == 0)
	return;

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

1322
1323
    for (i = 0; i < output_len; i++)
	input[i] = (int)output[i];
1324

1325
    unget_input(input, output_len);
1326

1327
    free(input);
1328
1329
}

1330
/* Read in a stream of characters verbatim, and return the length of the
1331
1332
1333
1334
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1335

1336
    /* Turn off flow control characters if necessary so that we can type
1337
1338
     * them in verbatim, and turn the keypad off if necessary so that we
     * don't get extended keypad values. */
1339
1340
    if (ISSET(PRESERVE))
	disable_flow_control();
1341
1342
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, FALSE);
1343
1344
1345

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

    /* Turn flow control characters back on if necessary and turn the
1348
     * keypad back on if necessary now that we're done. */
1349
1350
    if (ISSET(PRESERVE))
	enable_flow_control();
1351
1352
1353
1354
1355
1356
    /* Use the global window pointers, because a resize may have freed
     * the data that the win parameter points to. */
    if (!ISSET(REBIND_KEYPAD)) {
	keypad(edit, TRUE);
	keypad(bottomwin, TRUE);
    }
1357

1358
    return retval;
1359
1360
}

1361
1362
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
1363
 * the input into the corresponding multibyte value if possible.  After
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1364
 * that, leave the input as-is. */
1365
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1366
{
1367
    int *kbinput, *retval;
1368

1369
    /* Read in the first keystroke. */
1370
1371
    while ((kbinput = get_input(win, 1)) == NULL)
	;
1372

1373
#ifndef NANO_TINY
1374
1375
1376
1377
1378
1379
    /* When the window was resized, abort and return nothing. */
    if (*kbinput == KEY_WINCH) {
	*kbinput_len = 0;
	free(kbinput);
	return NULL;
    }
1380
#endif
1381

1382
#ifdef ENABLE_UTF8
1383
1384
1385
    if (using_utf8()) {
	/* Check whether the first keystroke is a valid hexadecimal
	 * digit. */
1386
	long uni = get_unicode_kbinput(win, *kbinput);
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400

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

	    while (uni == ERR) {
1401
		free(kbinput);
1402
1403
		while ((kbinput = get_input(win, 1)) == NULL)
		    ;
1404
		uni = get_unicode_kbinput(win, *kbinput);
1405
	    }
1406

1407
1408
1409
	    /* Put back the multibyte equivalent of the Unicode
	     * value. */
	    uni_mb = make_mbchar(uni, &uni_mb_len);
1410

1411
	    seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1412

1413
1414
	    for (i = 0; i < uni_mb_len; i++)
		seq[i] = (unsigned char)uni_mb[i];
1415

1416
	    unget_input(seq, uni_mb_len);
1417

1418
1419
	    free(seq);
	    free(uni_mb);
1420
	}
1421
    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1422
1423
#endif /* ENABLE_UTF8 */

1424
1425
	/* Put back the first keystroke. */
	unget_input(kbinput, 1);
1426

1427
1428
    free(kbinput);

1429
    /* Get the complete sequence, and save the characters in it as the
1430
     * result. */
1431
    *kbinput_len = get_key_buffer_len();
1432
    retval = get_input(NULL, *kbinput_len);
1433
1434
1435
1436

    return retval;
}

1437
#ifndef DISABLE_MOUSE
1438
/* Handle any mouse event that may have occurred.  We currently handle
1439
1440
1441
1442
1443
1444
1445
 * 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
1446
1447
1448
1449
1450
1451
 * 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. */
1452
int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1453
1454
{
    MEVENT mevent;
1455
    bool in_bottomwin;
1456
    subnfunc *f;
1457
1458
1459
1460
1461
1462

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

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

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

1469
1470
    in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1471
    /* Handle releases/clicks of the first mouse button. */
1472
    if (mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) {
1473
1474
	/* If we're allowing shortcuts, the current shortcut list is
	 * being displayed on the last two lines of the screen, and the
1475
1476
1477
	 * 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. */
1478
	if (allow_shortcuts && !ISSET(NO_HELP) && in_bottomwin) {
1479
1480
1481
1482
	    int i;
		/* The width of all the shortcuts, except for the last
		 * two, in the shortcut list in bottomwin. */
	    int j;
1483
		/* The calculated index number of the clicked item. */
1484
1485
1486
1487
	    size_t currslen;
		/* The number of shortcuts in the current shortcut
		 * list. */

1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
	    /* 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;
	    }
1502

1503
	    /* Get the shortcut lists' length. */
1504
	    if (currmenu == MMAIN)
1505
		currslen = MAIN_VISIBLE;
1506
	    else {
1507
		currslen = length_of_list(currmenu);
1508

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

1515
1516
1517
1518
1519
1520
1521
1522
	    /* 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));

1523
1524
	    /* Calculate the one-based index in the shortcut list. */
	    j = (*mouse_x / i) * 2 + *mouse_y;
1525

1526
1527
	    /* Adjust the index if we hit the last two wider ones. */
	    if ((j > currslen) && (*mouse_x % i < COLS % i))
1528
		j -= 2;
1529
1530
1531
#ifdef DEBUG
	    fprintf(stderr, "Calculated %i as index in shortcut list, currmenu = %x.\n", j, currmenu);
#endif
1532
1533
	    /* Ignore releases/clicks of the first mouse button beyond
	     * the last shortcut. */
1534
	    if (j > currslen)
1535
		return 2;
1536

1537
1538
	    /* Go through the list of functions to determine which
	     * shortcut in the current menu we released/clicked on. */
1539
	    for (f = allfuncs; f != NULL; f = f->next) {
1540
		if ((f->menus & currmenu) == 0)
1541
1542
		    continue;
		if (first_sc_for(currmenu, f->scfunc) == NULL)
1543
		    continue;
1544
1545
		/* Tick off an actually shown shortcut. */
		j -= 1;
1546
1547
		if (j == 0)
		    break;
1548
	    }
1549
#ifdef DEBUG
1550
	    fprintf(stderr, "Stopped on func %ld present in menus %x\n", (long)f->scfunc, f->menus);
1551
#endif
1552

1553
	    /* And put the corresponding key into the keyboard buffer. */
1554
	    if (f != NULL) {
1555
		const sc *s = first_sc_for(currmenu, f->scfunc);
1556
		unget_kbinput(s->keycode, s->meta);
1557
	    }
1558
	    return 1;
1559
	} else
1560
1561
	    /* Handle releases/clicks of the first mouse button that
	     * aren't on the current shortcut list elsewhere. */
1562
	    return 0;
1563
    }
1564
1565
#if NCURSES_MOUSE_VERSION >= 2
    /* Handle presses of the fourth mouse button (upward rolls of the
1566
1567
1568
     * 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
1569
	bool in_edit = wenclose(edit, *mouse_y, *mouse_x);
1570

1571
1572
1573
1574
	if (in_bottomwin)
	    /* Translate the mouse event coordinates so that they're
	     * relative to bottomwin. */
	    wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);
1575

1576
	if (in_edit || (in_bottomwin && *mouse_y == 0)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1577
1578
	    int i;

1579
1580
1581
1582
1583
	    /* 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) ?
1584
				KEY_PPAGE : KEY_NPAGE, FALSE);
1585
1586
1587
1588
1589
1590
1591

	    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;
1592
1593
    }
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1594
1595
1596

    /* Ignore all other mouse events. */
    return 2;
1597
}
1598
1599
#endif /* !DISABLE_MOUSE */

1600
1601
1602
1603
/* 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. */
1604
const sc *get_shortcut(int *kbinput)
1605
{
1606
    sc *s;
1607

1608
#ifdef DEBUG
1609
1610
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %s -- ",
				*kbinput, meta_key ? "TRUE" : "FALSE");
1611
1612
#endif

1613
    for (s = sclist; s != NULL; s = s->next) {
1614
	if ((s->menus & currmenu) && *kbinput == s->keycode &&
1615
					meta_key == s->meta) {
1616
#ifdef DEBUG
1617
1618
	    fprintf (stderr, "matched seq '%s'  (menu is %x from %x)\n",
				s->keystr, currmenu, s->menus);
1619
#endif
1620
	    return s;
1621
1622
	}
    }
1623
#ifdef DEBUG
1624
    fprintf (stderr, "matched nothing\n");
1625
#endif
1626
1627
1628
1629

    return NULL;
}

1630
1631
1632
1633
1634
/* 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
1635

1636
1637
1638
1639
    for (; n > 0; n--)
	waddch(win, ' ');
}

1640
/* Blank the first line of the top portion of the window. */
1641
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1642
{
1643
    blank_line(topwin, 0, 0, COLS);
1644
1645
}

1646
/* Blank all the lines of the middle portion of the window, i.e. the
1647
 * edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
1648
1649
void blank_edit(void)
{
1650
    int i;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1651

1652
    for (i = 0; i < editwinrows; i++)
1653
	blank_line(edit, i, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1654
1655
}

1656
/* Blank the first line of the bottom portion of the window. */
Chris Allegretta's avatar
Chris Allegretta committed
1657
1658
void blank_statusbar(void)
{
1659
    blank_line(bottomwin, 0, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1660
1661
}

1662
1663
/* If the NO_HELP flag isn't set, blank the last two lines of the bottom
 * portion of the window. */
1664
1665
1666
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
1667
1668
	blank_line(bottomwin, 1, 0, COLS);
	blank_line(bottomwin, 2, 0, COLS);
1669
1670
1671
    }
}

1672
1673
/* Check if the number of keystrokes needed to blank the statusbar has
 * been pressed.  If so, blank the statusbar, unless constant cursor
1674
 * position display is on and we are in the editing screen. */
1675
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1676
{
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
    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
1691
1692
1693
    }
}

1694
1695
/* Convert buf into a string that can be displayed on screen.  The
 * caller wants to display buf starting with column start_col, and
1696
1697
 * extending for at most span columns.  start_col is zero-based.  span
 * is one-based, so span == 0 means you get "" returned.  The returned
1698
1699
1700
 * 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. */
1701
1702
char *display_string(const char *buf, size_t start_col, size_t span,
	bool dollars)
1703
1704
{
    size_t start_index;
1705
	/* Index in buf of the first character shown. */
1706
    size_t column;
1707
	/* Screen column that start_index corresponds to. */
1708
1709
1710
1711
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */
1712

1713
1714
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
1715
1716
    if (dollars && span > 0 && strlenpt(buf) > start_col + span)
	span--;
1717

1718
    if (span == 0)
1719
1720
1721
1722
	return mallocstrcpy(NULL, "");

    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
1723

1724
    assert(column <= start_col);
1725

1726
1727
    /* Allocate enough space to hold the entire converted buffer. */
    converted = charalloc(strlen(buf) * (mb_cur_max() + tabsize) + 1);
1728

1729
    index = 0;
1730
#ifdef USING_OLD_NCURSES
1731
    seen_wide = FALSE;
1732
#endif
1733
    buf += start_index;
1734

1735
    if (*buf != '\0' && *buf != '\t' &&
1736
	(column < start_col || (dollars && column > 0))) {
1737
	/* We don't display the complete first character as it starts to
1738
	 * the left of the screen. */
1739
	if (is_cntrl_mbchar(buf)) {
1740
	    if (column < start_col) {
1741
		converted[index++] = control_mbrep(buf);
1742
		start_col++;
1743
		buf += parse_mbchar(buf, NULL, NULL);
1744
	    }
1745
	}
1746
#ifdef ENABLE_UTF8
1747
	else if (using_utf8() && mbwidth(buf) == 2) {
1748
1749
1750
1751
1752
	    if (column >= start_col) {
		converted[index++] = ' ';
		start_col++;
	    }

1753
	    converted[index++] = ' ';
1754
	    start_col++;
1755

1756
	    buf += parse_mbchar(buf, NULL, NULL);
1757
	}
1758
#endif
1759
1760
    }

1761
    while (*buf != '\0') {
1762
	int charlength, charwidth = 1;
1763

1764
	if (*buf == ' ') {
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
	    /* 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++;
1776
1777
	    buf++;
	    continue;
1778
	} else if (*buf == '\t') {
1779
	    /* Show a tab as a visible character, or as as a space. */
1780
#ifndef NANO_TINY
1781
	    if (ISSET(WHITESPACE_DISPLAY)) {
1782
		int i = 0;
1783

1784
1785
		while (i < whitespace_len[0])
		    converted[index++] = whitespace[i++];
1786
	    } else
1787
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1788
		converted[index++] = ' ';
1789
	    start_col++;
1790
	    /* Fill the tab up with the required number of spaces. */
1791
	    while (start_col % tabsize != 0) {
1792
		converted[index++] = ' ';
1793
1794
		start_col++;
	    }
1795
1796
1797
1798
	    buf++;
	    continue;
	}

1799
	charlength = length_of_char(buf, &charwidth);
1800

1801
	/* If buf contains a control character, represent it. */
1802
	if (is_cntrl_mbchar(buf)) {
1803
	    converted[index++] = '^';
1804
	    converted[index++] = control_mbrep(buf);
1805
	    start_col += 2;
1806
1807
1808
	    buf += charlength;
	    continue;
	}
1809

1810
1811
1812
1813
	/* If buf contains a valid non-control character, simply copy it. */
	if (charlength > 0) {
	    for (; charlength > 0; charlength--)
		converted[index++] = *(buf++);
1814

1815
	    start_col += charwidth;
1816
#ifdef USING_OLD_NCURSES
1817
	    if (charwidth > 1)
1818
		seen_wide = TRUE;
1819
#endif
1820
	    continue;
1821
1822
	}

1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
	/* Represent an invalid sequence with the Replacement Character. */
	converted[index++] = '\xEF';
	converted[index++] = '\xBF';
	converted[index++] = '\xBD';

	start_col += 1;
	buf++;

	/* For invalid codepoints, skip extra bytes. */
	if (charlength < -1)
	   buf += charlength + 7;
1834
1835
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1836
    /* Null-terminate converted. */
1837
    converted[index] = '\0';
1838

1839
1840
    /* Make sure converted takes up no more than span columns. */
    index = actual_x(converted, span);
1841
    null_at(&converted, index);
1842

1843
    return converted;
1844
1845
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1846
1847
1848
1849
1850
1851
/* 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. */
1852
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
1853
{
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
    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. */
1866

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

1869
    wattron(topwin, interface_color_pair[TITLE_BAR]);
1870

1871
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
1872

1873
1874
1875
    /* 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
1876

1877
    /* Figure out the path, prefix and state strings. */
1878
#ifndef DISABLE_BROWSER
1879
1880
1881
1882
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
1883
1884
1885
1886
1887
1888
1889
    {
	if (openfile->filename[0] == '\0')
	    path = _("New Buffer");
	else {
	    path = openfile->filename;
	    prefix = _("File:");
	}
1890

1891
1892
1893
1894
	if (openfile->modified)
	    state = _("Modified");
	else if (ISSET(VIEW_MODE))
	    state = _("View");
1895

1896
1897
	pluglen = strlenpt(_("Modified")) + 1;
    }
1898

1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
    /* 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;
1909
1910
    }

1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
    /* 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;
1923
1924
1925
	}
    }

1926
1927
1928
1929
    /* If we have side spaces left, center the path name. */
    if (verlen > 0)
	offset = verlen + (COLS - (verlen + pluglen + statelen) -
					(prefixlen + pathlen)) / 2;
1930

1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
    /* 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);
1948
    }
1949

1950
1951
1952
1953
1954
1955
    /* 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));

1956
    wattroff(topwin, interface_color_pair[TITLE_BAR]);
1957

1958
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
1959
    reset_cursor();
1960
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1961
1962
}

1963
1964
1965
1966
1967
1968
/* Display a normal message on the statusbar, quietly. */
void statusbar(const char *msg)
{
    statusline(HUSH, msg);
}

1969
/* Display a message on the statusbar, and set suppress_cursorpos to
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1970
1971
 * TRUE, so that the message won't be immediately overwritten if
 * constant cursor position display is on. */
1972
void statusline(message_type importance, const char *msg, ...)
1973
1974
{
    va_list ap;
1975
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
1976
    size_t start_x;
1977
1978
1979
1980
#ifndef NANO_TINY
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

    UNSET(WHITESPACE_DISPLAY);
1981
#endif
1982
1983
1984
1985
1986

    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(). */
1987
    if (isendwin()) {
1988
1989
1990
1991
1992
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

1993
1994
1995
1996
1997
1998
1999
    /* 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)
2000
2001
	napms(1200);

2002
    if (importance == ALERT)
2003
	beep();
2004
2005

    lastmessage = importance;
2006

2007
2008
2009
    /* Turn the cursor off while fiddling in the statusbar. */
    curs_set(0);

2010
2011
    blank_statusbar();

2012
2013
2014
2015
    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
2016
    free(bar);
2017
2018

#ifndef NANO_TINY
2019
2020
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2021
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2022
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2023

2024
    wmove(bottomwin, 0, start_x);
2025
    wattron(bottomwin, interface_color_pair[STATUS_BAR]);
2026
2027
2028
2029
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
2030
    wattroff(bottomwin, interface_color_pair[STATUS_BAR]);
2031

2032
    /* Push the message to the screen straightaway. */
2033
    wnoutrefresh(bottomwin);
2034
    doupdate();
2035

2036
    suppress_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2037

2038
    /* If we're doing quick statusbar blanking, blank it after just one
2039
2040
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2041
#ifndef NANO_TINY
2042
2043
2044
    if (ISSET(QUICK_BLANK))
	statusblank = 1;
    else
2045
#endif
2046
	statusblank = 26;
2047
2048
}

2049
2050
/* Display the shortcut list corresponding to menu on the last two rows
 * of the bottom portion of the window. */
2051
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2052
{
2053
    size_t i, colwidth, slen;
2054
2055
    subnfunc *f;
    const sc *s;
2056

2057
2058
2059
    /* Set the global variable to the given menu. */
    currmenu = menu;

Chris Allegretta's avatar
Chris Allegretta committed
2060
2061
2062
    if (ISSET(NO_HELP))
	return;

2063
    if (menu == MMAIN) {
2064
	slen = MAIN_VISIBLE;
2065

2066
	assert(slen <= length_of_list(menu));
2067
    } else {
2068
	slen = length_of_list(menu);
2069

2070
	/* Don't show any more shortcuts than the main list does. */
2071
2072
2073
2074
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2075
2076
2077
2078
    /* 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. */
2079
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2080

2081
    blank_bottombars();
2082

2083
2084
2085
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2086

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

2089
#ifdef DEBUG
2090
	fprintf(stderr, "Checking menu items....");
2091
#endif
2092
	if ((f->menus & menu) == 0)
2093
	    continue;
2094

2095
#ifdef DEBUG
2096
	fprintf(stderr, "found one! f->menus = %x, desc = \"%s\"\n", f->menus, f->desc);
2097
#endif
2098
2099
	s = first_sc_for(menu, f->scfunc);
	if (s == NULL) {
2100
2101
2102
#ifdef DEBUG
	    fprintf(stderr, "Whoops, guess not, no shortcut key found for func!\n");
#endif
2103
2104
	    continue;
	}
2105
	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2106
#ifdef DEBUG
2107
	fprintf(stderr, "Calling onekey with keystr \"%s\" and desc \"%s\"\n", s->keystr, f->desc);
2108
#endif
2109
	onekey(s->keystr, _(f->desc), colwidth + (COLS % colwidth));
2110
	i++;
Chris Allegretta's avatar
Chris Allegretta committed
2111
    }
2112

2113
2114
    wnoutrefresh(bottomwin);
    reset_cursor();
2115
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2116
2117
}

2118
2119
/* 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
2120
 * to write at most length characters, even if length is very small and
2121
2122
 * keystroke and desc are long.  Note that waddnstr(,,(size_t)-1) adds
 * the whole string!  We do not bother padding the entry with blanks. */
2123
void onekey(const char *keystroke, const char *desc, int length)
Chris Allegretta's avatar
Chris Allegretta committed
2124
{
2125
2126
    assert(keystroke != NULL && desc != NULL);

2127
    wattron(bottomwin, interface_color_pair[KEY_COMBO]);
2128
    waddnstr(bottomwin, keystroke, actual_x(keystroke, length));
2129
    wattroff(bottomwin, interface_color_pair[KEY_COMBO]);
2130

2131
    length -= strlenpt(keystroke) + 1;
2132

2133
    if (length > 0) {
2134
	waddch(bottomwin, ' ');
2135
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG]);
2136
	waddnstr(bottomwin, desc, actual_x(desc, length));
2137
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG]);
Chris Allegretta's avatar
Chris Allegretta committed
2138
2139
2140
    }
}

2141
2142
/* 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
2143
2144
void reset_cursor(void)
{
2145
    size_t xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2146

2147
#ifndef NANO_TINY
2148
    if (ISSET(SOFTWRAP)) {
2149
	filestruct *line = openfile->edittop;
2150
2151
	openfile->current_y = 0;

2152
2153
2154
2155
	while (line != NULL && line != openfile->current) {
	    openfile->current_y += strlenpt(line->data) / COLS + 1;
	    line = line->next;
	}
2156
	openfile->current_y += xpt / COLS;
2157

2158
	if (openfile->current_y < editwinrows)
2159
	    wmove(edit, openfile->current_y, xpt % COLS);
2160
2161
2162
    } else
#endif
    {
2163
	openfile->current_y = openfile->current->lineno -
2164
				openfile->edittop->lineno;
2165
2166
2167
2168

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

2171
2172
2173
2174
2175
2176
2177
2178
/* 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. */
2179
void edit_draw(filestruct *fileptr, const char *converted, int
2180
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2181
{
2182
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2183
2184
2185
2186
2187
2188
2189
2190
2191
    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. */
2192
2193
#endif

2194
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2195
    assert(strlenpt(converted) <= COLS);
2196

2197
2198
2199
    /* 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);
2200

2201
#ifdef USING_OLD_NCURSES
2202
2203
2204
    /* 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. */
2205
2206
    if (seen_wide)
	wredrawln(edit, line, 1);
2207
#endif
2208

2209
#ifndef DISABLE_COLOR
2210
2211
2212
    /* If color syntaxes are available and turned on, we need to display
     * them. */
    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
2213
	const colortype *varnish = openfile->colorstrings;
2214

2215
2216
2217
2218
	/* If there are multiline regexes, make sure there is a cache. */
	if (openfile->syntax->nmultis > 0)
	    alloc_multidata_if_needed(fileptr);

2219
	for (; varnish != NULL; varnish = varnish->next) {
2220
2221
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
2222
	    int paintlen = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2223
2224
		/* Number of chars to paint on this line.  There are
		 * COLS characters on a whole line. */
2225
	    size_t index;
2226
		/* Index in converted where we paint. */
2227
2228
2229
2230
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2231

2232
	    wattron(edit, varnish->attributes);
2233
2234
2235
	    /* 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. */
2236

2237
2238
	    /* First case: varnish is a single-line expression. */
	    if (varnish->end == NULL) {
2239
2240
2241
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2242
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2243
2244
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2245
2246
2247
		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
2248
2249
2250
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2251
		    if (regexec(varnish->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2252
2253
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2254
			break;
2255
2256
		    /* Translate the match to the beginning of the
		     * line. */
2257
2258
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2259
2260
2261

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2262
			startmatch.rm_eo++;
2263
		    else if (startmatch.rm_so < endpos &&
2264
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2265
2266
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2267
				startmatch.rm_so) - start;
2268

2269
2270
2271
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2272
2273
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2274
2275
2276

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

2277
			mvwaddnstr(edit, line, x_start, converted +
2278
				index, paintlen);
2279
		    }
2280
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2281
		}
2282
	    } else {	/* Second case: varnish is a multiline expression. */
2283
		const filestruct *start_line = fileptr->prev;
2284
		    /* The first line before fileptr that matches 'start'. */
2285
		size_t start_col;
2286
		    /* Where the match starts in that line. */
2287
		const filestruct *end_line;
2288
		    /* The line that matches 'end'. */
2289

2290
		/* First see if the multidata was maybe already calculated. */
2291
		if (fileptr->multidata[varnish->id] == CNONE)
2292
		    goto tail_of_loop;
2293
		else if (fileptr->multidata[varnish->id] == CWHOLELINE) {
2294
		    mvwaddnstr(edit, line, 0, converted, -1);
2295
		    goto tail_of_loop;
2296
2297
		} else if (fileptr->multidata[varnish->id] == CBEGINBEFORE) {
		    regexec(varnish->end, fileptr->data, 1, &endmatch, 0);
2298
2299
		    /* If the coloured part is scrolled off, skip it. */
		    if (endmatch.rm_eo <= startpos)
2300
			goto tail_of_loop;
2301
2302
2303
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
			endmatch.rm_eo) - start);
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2304
		    goto tail_of_loop;
2305
		} if (fileptr->multidata[varnish->id] == -1)
2306
		    /* Assume this until proven otherwise below. */
2307
		    fileptr->multidata[varnish->id] = CNONE;
2308

2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
		/* 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. */

2319
		while (start_line != NULL && regexec(varnish->start,
2320
2321
2322
			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. */
2323
		    if (regexec(varnish->end, start_line->data, 0, NULL, 0) == 0)
2324
2325
2326
			goto step_two;
		    start_line = start_line->prev;
		}
2327

2328
2329
2330
2331
		/* If no start was found, skip to the next step. */
		if (start_line == NULL)
		    goto step_two;

2332
		/* If a found start has been qualified as an end earlier,
2333
		 * believe it and skip to the next step. */
2334
		if (start_line->multidata != NULL &&
2335
2336
			(start_line->multidata[varnish->id] == CBEGINBEFORE ||
			start_line->multidata[varnish->id] == CSTARTENDHERE))
2337
2338
		    goto step_two;

2339
		/* Skip over a zero-length regex match. */
2340
		if (startmatch.rm_so == startmatch.rm_eo)
2341
2342
		    goto tail_of_loop;

2343
2344
2345
2346
2347
2348
2349
		/* 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;
2350
		    if (regexec(varnish->end, start_line->data +
2351
2352
2353
				start_col + startmatch.rm_eo, 0, NULL,
				(start_col + startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH)
2354
2355
2356
			/* No end found after this start. */
			break;
		    start_col++;
2357
2358
		    if (regexec(varnish->start, start_line->data + start_col,
				1, &startmatch, REG_NOTBOL) == REG_NOMATCH)
2359
2360
2361
2362
2363
2364
2365
2366
2367
			/* 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;
2368
		while (end_line != NULL && regexec(varnish->end,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2369
			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2370
		    end_line = end_line->next;
2371

2372
2373
2374
2375
		/* 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) {
2376
		    fileptr->multidata[varnish->id] = CBEGINBEFORE;
2377
2378
		    goto step_two;
		}
2379

2380
2381
2382
2383
2384
2385
2386
		/* 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;
2387
		    fileptr->multidata[varnish->id] = CWHOLELINE;
2388
#ifdef DEBUG
2389
    fprintf(stderr, "  Marking for id %i  line %i as CWHOLELINE\n", varnish->id, line);
2390
#endif
2391
2392
2393
		} else {
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
						endmatch.rm_eo) - start);
2394
		    fileptr->multidata[varnish->id] = CBEGINBEFORE;
2395
#ifdef DEBUG
2396
    fprintf(stderr, "  Marking for id %i  line %i as CBEGINBEFORE\n", varnish->id, line);
2397
#endif
2398
2399
2400
2401
2402
2403
		}
		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;
2404
  step_two:
2405
2406
2407
2408
2409
		/* 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) {
2410
		    if (regexec(varnish->start, fileptr->data + start_col,
2411
2412
				1, &startmatch, (start_col == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH ||
2413
				start_col + startmatch.rm_so >= endpos)
2414
2415
			/* No more starts on this line. */
			break;
2416

2417
2418
2419
2420
2421
2422
2423
		    /* 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,
2424
				startmatch.rm_so) - start;
2425

2426
		    index = actual_x(converted, x_start);
2427

2428
		    if (regexec(varnish->end, fileptr->data +
2429
				startmatch.rm_eo, 1, &endmatch,
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
				(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 &&
2440
				endmatch.rm_eo > startmatch.rm_so) {
2441
			    paintlen = actual_x(converted + index,
2442
					strnlenpt(fileptr->data,
2443
					endmatch.rm_eo) - start - x_start);
2444

2445
			    assert(0 <= x_start && x_start < COLS);
2446

2447
			    mvwaddnstr(edit, line, x_start,
2448
					converted + index, paintlen);
2449
			    if (paintlen > 0) {
2450
				fileptr->multidata[varnish->id] = CSTARTENDHERE;
2451
#ifdef DEBUG
2452
    fprintf(stderr, "  Marking for id %i  line %i as CSTARTENDHERE\n", varnish->id, line);
2453
#endif
2454
			    }
2455
2456
			}
			start_col = endmatch.rm_eo;
2457
2458
2459
			/* Skip over a zero-length match. */
			if (endmatch.rm_so == endmatch.rm_eo)
			    start_col += 1;
2460
2461
2462
2463
		    } else {
			/* There is no end on this line.  But we haven't yet
			 * looked for one on later lines. */
			end_line = fileptr->next;
2464

2465
			while (end_line != NULL &&
2466
				regexec(varnish->end, end_line->data,
2467
				0, NULL, 0) == REG_NOMATCH)
2468
			    end_line = end_line->next;
2469

2470
2471
2472
			/* If there is no end, we're done on this line. */
			if (end_line == NULL)
			    break;
2473

2474
2475
2476
2477
			assert(0 <= x_start && x_start < COLS);

			/* Paint the rest of the line. */
			mvwaddnstr(edit, line, x_start, converted + index, -1);
2478
			fileptr->multidata[varnish->id] = CENDAFTER;
2479
#ifdef DEBUG
2480
    fprintf(stderr, "  Marking for id %i  line %i as CENDAFTER\n", varnish->id, line);
2481
#endif
2482
2483
2484
			/* We've painted to the end of the line, so don't
			 * bother checking for any more starts. */
			break;
2485
		    }
2486
2487
		}
	    }
2488
  tail_of_loop:
2489
	    wattroff(edit, varnish->attributes);
2490
	}
2491
    }
2492
#endif /* !DISABLE_COLOR */
2493

2494
#ifndef NANO_TINY
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
    /* If the mark is on, and fileptr is at least partially selected, we
     * need to paint it. */
    if (openfile->mark_set &&
		(fileptr->lineno <= openfile->mark_begin->lineno ||
		fileptr->lineno <= openfile->current->lineno) &&
		(fileptr->lineno >= openfile->mark_begin->lineno ||
		fileptr->lineno >= openfile->current->lineno)) {
	const filestruct *top, *bot;
	    /* The lines where the marked region begins and ends. */
	size_t top_x, bot_x;
	    /* The x positions where the marked region begins and ends. */
2506
	int x_start;
2507
	    /* The starting column for mvwaddnstr().  Zero-based. */
2508
	int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2509
2510
	    /* Number of characters to paint on this line.  There are
	     * COLS characters on a whole line. */
2511
	size_t index;
2512
	    /* Index in converted where we paint. */
2513

2514
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2515
2516
2517
2518
2519

	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
2520

2521
	/* Only paint if the marked bit of fileptr is on this page. */
2522
2523
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2524
2525
2526
2527

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

2529
2530
2531
2532
2533
	    /* 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. */
2534
2535
2536
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
2537
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start + start);
2538
2539
2540
2541
2542
2543
2544
2545

	    /* 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;
	    }
2546
2547
2548

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

2549
	    index = actual_x(converted, x_start);
2550

2551
2552
2553
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2554
	    wattron(edit, hilite_attribute);
2555
	    mvwaddnstr(edit, line, x_start, converted + index, paintlen);
2556
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2557
	}
2558
    }
2559
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2560
2561
}

2562
/* Just update one line in the edit buffer.  This is basically a wrapper
2563
 * for edit_draw().  The line will be displayed starting with
2564
 * fileptr->data[index].  Likely arguments are current_x or zero.
2565
 * Returns: Number of additional lines consumed (needed for SOFTWRAP). */
2566
int update_line(filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2567
{
2568
    int line = 0;
2569
	/* The line in the edit window that we want to update. */
2570
    int extralinesused = 0;
2571
2572
2573
2574
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2575

2576
    assert(fileptr != NULL);
2577

2578
#ifndef NANO_TINY
2579
    if (ISSET(SOFTWRAP)) {
2580
2581
	filestruct *tmp;

2582
2583
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2584
    } else
2585
#endif
2586
2587
	line = fileptr->lineno - openfile->edittop->lineno;

2588
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2589
	return 1;
2590

2591
    /* First, blank out the line. */
2592
    blank_line(edit, line, 0, COLS);
2593

2594
2595
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2596
#ifndef NANO_TINY
2597
2598
2599
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2600
#endif
2601
	index = strnlenpt(fileptr->data, index);
2602
    page_start = get_page_start(index);
2603

2604
2605
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2606
2607
2608
#ifdef NANO_TINY
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
#else
2609
2610
2611
    converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2612
	fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2613
#endif
2614
#endif /* !NANO_TINY */
2615

2616
    /* Paint the line. */
2617
    edit_draw(fileptr, converted, line, page_start);
2618
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2619

2620
#ifndef NANO_TINY
2621
    if (!ISSET(SOFTWRAP)) {
2622
#endif
2623
2624
2625
2626
	if (page_start > 0)
	    mvwaddch(edit, line, 0, '$');
	if (strlenpt(fileptr->data) > page_start + COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
2627
#ifndef NANO_TINY
2628
    } else {
2629
	size_t full_length = strlenpt(fileptr->data);
2630
	for (index += COLS; index <= full_length && line < editwinrows - 1; index += COLS) {
2631
2632
	    line++;
#ifdef DEBUG
2633
	    fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
2634
#endif
2635
	    blank_line(edit, line, 0, COLS);
2636
2637

	    /* Expand the line, replacing tabs with spaces, and control
2638
	     * characters with their displayed forms. */
2639
2640
2641
2642
2643
	    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
2644
2645
2646

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2647
	    free(converted);
2648
2649
2650
	    extralinesused++;
	}
    }
2651
#endif /* !NANO_TINY */
2652
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2653
2654
}

2655
2656
2657
2658
/* 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)
2659
2660
{
    return
2661
#ifndef NANO_TINY
2662
	openfile->mark_set ||
2663
#endif
2664
	get_page_start(pww_save) != get_page_start(openfile->placewewant);
2665
2666
}

2667
/* When edittop changes, try and figure out how many lines
2668
 * we really have to work with (i.e. set maxrows). */
2669
2670
2671
2672
2673
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2674
2675
2676
2677
2678
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2679
2680
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2681
	maxrows++;
2682
	n += strlenpt(foo->data) / COLS;
2683
2684
2685
	foo = foo->next;
    }

2686
2687
2688
    if (n < editwinrows)
	maxrows += editwinrows - n;

2689
#ifdef DEBUG
2690
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2691
2692
2693
#endif
}

2694
2695
/* 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
2696
2697
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
2698
2699
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is FALSE. */
2700
void edit_scroll(scroll_dir direction, ssize_t nlines)
2701
{
2702
    ssize_t i;
2703
    filestruct *foo;
2704

2705
    assert(nlines > 0);
2706

2707
2708
2709
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

2710
    /* Move the top line of the edit window up or down (depending on the
2711
2712
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
2713
    for (i = nlines; i > 0; i--) {
2714
	if (direction == UPWARD) {
2715
	    if (openfile->edittop == openfile->fileage)
2716
		break;
2717
	    openfile->edittop = openfile->edittop->prev;
2718
	} else {
2719
	    if (openfile->edittop == openfile->filebot)
2720
		break;
2721
	    openfile->edittop = openfile->edittop->next;
2722
	}
2723
2724

#ifndef NANO_TINY
2725
	/* Don't over-scroll on long lines. */
2726
	if (ISSET(SOFTWRAP) && direction == UPWARD) {
2727
	    ssize_t len = strlenpt(openfile->edittop->data) / COLS;
2728
	    i -= len;
2729
	    if (len > 0)
2730
		refresh_needed = TRUE;
2731
	}
2732
#endif
2733
2734
    }

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

2738
2739
2740
2741
    /* Don't bother scrolling zero lines, nor more than the window can hold. */
    if (nlines == 0)
	return;
    if (nlines >= editwinrows)
2742
	refresh_needed = TRUE;
2743

2744
    if (refresh_needed == TRUE)
2745
	return;
2746
2747
2748

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2749
    scrollok(edit, TRUE);
2750
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
2751
2752
    scrollok(edit, FALSE);

2753
2754
2755
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

2756
2757
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
2758
2759
    if ((direction == UPWARD && openfile->edittop ==
	openfile->fileage) || (direction == DOWNWARD &&
2760
2761
	openfile->edittop->lineno + editwinrows - 1 >=
	openfile->filebot->lineno))
2762
	nlines = editwinrows;
2763

2764
2765
2766
2767
2768
2769
    /* 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
2770

2771
2772
    if (nlines > editwinrows)
	nlines = editwinrows;
2773
2774
2775

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

2778
2779
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
2780
    if (direction == DOWNWARD) {
2781
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
2782
2783
2784
	    foo = foo->next;
    }

2785
2786
2787
2788
2789
2790
    /* 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--) {
2791
2792
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
2793
	    if (need_screen_update(0))
2794
2795
2796
2797
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
2798
		openfile->current_x : 0);
2799
	foo = foo->next;
2800
    }
2801
    compute_maxrows();
2802
2803
2804
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2805
 * updated.  Use this if we've moved without changing any text. */
2806
void edit_redraw(filestruct *old_current)
2807
{
2808
2809
2810
2811
    size_t was_pww = openfile->placewewant;

    openfile->placewewant = xplustabs();

2812
2813
    /* If the current line is offscreen, scroll until it's onscreen. */
    if (openfile->current->lineno >= openfile->edittop->lineno + maxrows ||
2814
		openfile->current->lineno < openfile->edittop->lineno) {
2815
	edit_update((focusing || !ISSET(SMOOTH_SCROLL)) ? CENTERING : FLOWING);
2816
	refresh_needed = TRUE;
2817
    }
2818

2819
#ifndef NANO_TINY
2820
2821
2822
    /* If the mark is on, update all lines between old_current and current. */
    if (openfile->mark_set) {
	filestruct *foo = old_current;
2823

2824
	while (foo != openfile->current) {
2825
	    update_line(foo, 0);
2826

2827
2828
2829
	    foo = (foo->lineno > openfile->current->lineno) ?
			foo->prev : foo->next;
	}
2830
2831
2832
2833
2834
2835
    } 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);
2836
2837
2838
2839
2840

    /* 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))
2841
	update_line(openfile->current, openfile->current_x);
2842
2843
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2844
2845
/* 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
2846
2847
void edit_refresh(void)
{
2848
    filestruct *foo;
2849
    int nlines;
2850

2851
    /* Figure out what maxrows should really be. */
2852
    compute_maxrows();
2853

2854
2855
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
2856
2857
	maxrows) {
#ifdef DEBUG
2858
2859
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
2860
2861
#endif

2862
	/* Make sure the current line is on the screen. */
2863
	edit_update((focusing || !ISSET(SMOOTH_SCROLL)) ? CENTERING : STATIONARY);
2864
    }
Chris Allegretta's avatar
Chris Allegretta committed
2865

2866
2867
    foo = openfile->edittop;

2868
#ifdef DEBUG
2869
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
2870
#endif
2871

2872
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
2873
	nlines += update_line(foo, (foo == openfile->current) ?
2874
		openfile->current_x : 0);
2875
2876
2877
	foo = foo->next;
    }

2878
    for (; nlines < editwinrows; nlines++)
2879
2880
2881
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
2882
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2883
2884
}

2885
2886
2887
2888
2889
2890
/* 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
2891
{
2892
    int goal = 0;
2893

2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
    /* 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)
2904
	goal = editwinrows / 2;
2905
    else if (manner == FLOWING) {
2906
	if (openfile->current->lineno >= openfile->edittop->lineno)
2907
2908
	    goal = editwinrows - 1;
    } else {
2909
	goal = openfile->current_y;
2910

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

2916
2917
2918
2919
2920
    openfile->edittop = openfile->current;

    while (goal > 0 && openfile->edittop->prev != NULL) {
	openfile->edittop = openfile->edittop->prev;
	goal --;
2921
#ifndef NANO_TINY
2922
2923
	if (ISSET(SOFTWRAP))
	    goal -= strlenpt(openfile->edittop->data) / COLS;
2924
#endif
2925
    }
2926
#ifdef DEBUG
2927
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
2928
#endif
2929
    compute_maxrows();
Chris Allegretta's avatar
Chris Allegretta committed
2930
2931
}

2932
/* Unconditionally redraw the entire screen. */
2933
void total_redraw(void)
2934
{
2935
2936
2937
2938
2939
2940
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
2941
    wrefresh(curscr);
2942
#endif
2943
2944
}

2945
2946
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
2947
2948
void total_refresh(void)
{
2949
    total_redraw();
2950
    titlebar(NULL);
2951
    edit_refresh();
2952
    bottombars(currmenu);
2953
2954
}

2955
2956
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
2957
2958
void display_main_list(void)
{
2959
#ifndef DISABLE_COLOR
2960
2961
    if (openfile->syntax &&
		(openfile->syntax->formatter || openfile->syntax->linter))
2962
	set_lint_or_format_shortcuts();
2963
2964
2965
2966
    else
	set_spell_shortcuts();
#endif

2967
    bottombars(MMAIN);
2968
2969
}

2970
/* If constant is TRUE, we display the current cursor position only if
2971
2972
 * suppress_cursorpos is FALSE.  If constant is FALSE, we display the
 * position always.  In any case we reset suppress_cursorpos to FALSE. */
2973
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
2974
{
2975
    filestruct *f;
2976
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2977
    size_t i, cur_xpt = xplustabs() + 1;
2978
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
2979
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
2980

2981
    assert(openfile->fileage != NULL && openfile->current != NULL);
2982

2983
    /* Determine the size of the file up to the cursor. */
2984
    f = openfile->current->next;
2985
    c = openfile->current->data[openfile->current_x];
2986
2987

    openfile->current->next = NULL;
2988
    openfile->current->data[openfile->current_x] = '\0';
2989
2990
2991

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

2992
    openfile->current->data[openfile->current_x] = c;
2993
    openfile->current->next = f;
2994

2995
    /* If the position needs to be suppressed, don't suppress it next time. */
2996
    if (suppress_cursorpos && constant) {
2997
	suppress_cursorpos = FALSE;
2998
	return;
2999
    }
Chris Allegretta's avatar
Chris Allegretta committed
3000

3001
    /* Display the current cursor position on the statusbar. */
3002
    linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
3003
    colpct = 100 * cur_xpt / cur_lenpt;
3004
    charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;
3005

3006
    statusline(HUSH,
3007
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3008
	(long)openfile->current->lineno,
3009
	(long)openfile->filebot->lineno, linepct,
3010
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3011
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3012
3013
3014

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

3017
/* Unconditionally display the current cursor position. */
3018
void do_cursorpos_void(void)
3019
{
3020
    do_cursorpos(FALSE);
3021
3022
}

3023
3024
void enable_nodelay(void)
{
3025
3026
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3027
3028
3029
3030
}

void disable_nodelay(void)
{
3031
3032
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3033
3034
}

3035
3036
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3037
void spotlight(bool active, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3038
{
3039
    size_t word_len = strlenpt(word), room;
Chris Allegretta's avatar
Chris Allegretta committed
3040

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

3044
    assert(room > 0);
3045

3046
3047
    if (word_len > room)
	room--;
3048

Chris Allegretta's avatar
Chris Allegretta committed
3049
    reset_cursor();
3050
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3051

3052
    if (active)
3053
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3054

3055
    /* This is so we can show zero-length matches. */
3056
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3057
	waddch(edit, ' ');
3058
    else
3059
	waddnstr(edit, word, actual_x(word, room));
3060

3061
    if (word_len > room)
3062
	waddch(edit, '$');
Chris Allegretta's avatar
Chris Allegretta committed
3063

3064
    if (active)
3065
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3066
3067
}

3068
#ifndef DISABLE_EXTRA
3069
3070
#define CREDIT_LEN 54
#define XLCREDIT_LEN 9
3071

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3072
3073
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3074
3075
void do_credits(void)
{
3076
3077
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3078
    int kbinput = ERR, crpos = 0, xlpos = 0;
3079
3080
3081
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3082
3083
	VERSION,
	"",
3084
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3085
3086
3087
3088
3089
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3090
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3091
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3092
	"Mark Majeres",
3093
	"Mike Frysinger",
3094
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3105
	NULL,				/* "Special thanks to:" */
3106
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3107
3108
3109
3110
3111
3112
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3113
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3114
	"Linus Torvalds",
3115
	NULL,				/* "the many translators and the TP" */
3116
	NULL,				/* "For ncurses:" */
3117
3118
3119
3120
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3121
3122
3123
3124
3125
3126
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3127
	"(C) 1999 - 2016",
3128
	"Free Software Foundation, Inc.",
3129
3130
3131
3132
	"",
	"",
	"",
	"",
3133
	"https://nano-editor.org/"
3134
3135
    };

3136
    const char *xlcredits[XLCREDIT_LEN] = {
3137
3138
3139
3140
3141
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
3142
	N_("the many translators and the TP"),
3143
3144
3145
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3146
    };
3147

3148
3149
3150
3151
3152
3153
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3154
3155
    curs_set(0);
    nodelay(edit, TRUE);
3156

3157
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
3158
    blank_edit();
3159
    blank_statusbar();
3160

3161
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3162
    wrefresh(edit);
3163
    wrefresh(bottomwin);
3164
    napms(700);
3165

3166
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3167
	if ((kbinput = wgetch(edit)) != ERR)
3168
	    break;
3169

3170
	if (crpos < CREDIT_LEN) {
3171
	    const char *what;
3172
3173
	    size_t start_x;

3174
	    if (credits[crpos] == NULL) {
3175
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3176

3177
		what = _(xlcredits[xlpos]);
3178
		xlpos++;
3179
	    } else
3180
		what = credits[crpos];
3181

3182
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3183
3184
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3185
	}
3186

3187
3188
3189
3190
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3191
	napms(700);
3192

3193
	scrollok(edit, TRUE);
3194
	wscrl(edit, 1);
3195
	scrollok(edit, FALSE);
3196
	wrefresh(edit);
3197

3198
	if ((kbinput = wgetch(edit)) != ERR)
3199
	    break;
3200
	napms(700);
3201

3202
	scrollok(edit, TRUE);
3203
	wscrl(edit, 1);
3204
	scrollok(edit, FALSE);
3205
	wrefresh(edit);
3206
3207
    }

3208
3209
3210
    if (kbinput != ERR)
	ungetch(kbinput);

3211
    if (!old_more_space)
3212
	UNSET(MORE_SPACE);
3213
    if (!old_no_help)
3214
	UNSET(NO_HELP);
3215
    window_init();
3216

3217
    nodelay(edit, FALSE);
3218

3219
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3220
}
3221
#endif /* !DISABLE_EXTRA */