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

24
#include "proto.h"
25

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

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

46
47
static sig_atomic_t sigwinch_counter_save = 0;

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

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

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

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

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

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

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

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

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

    while (TRUE) {
168
	input = wgetch(win);
169

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

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

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

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

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
215
216
217
    /* 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. */
218
219
220
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
221

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

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

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

241
    unget_input(&kbinput, 1);
242

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

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

    if (key_buffer_len == 0) {
259
	if (win != NULL) {
260
	    get_key_buffer(win);
261

262
263
264
	    if (key_buffer_len == 0)
		return NULL;
	} else
265
266
267
	    return NULL;
    }

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

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

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

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

    return input;
298
299
}

300
301
/* Read in a single character.  If it's ignored, swallow it and go on.
 * Otherwise, try to translate it from ASCII, meta key sequences, escape
302
303
 * sequences, and/or extended keypad values.  Supported extended keypad
 * values consist of
304
305
306
 * [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace,
 * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown),
 * the function keypad (F1-F16), and the numeric keypad with NumLock
307
 * off. */
308
int get_kbinput(WINDOW *win)
309
310
311
312
313
{
    int kbinput;

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

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

322
323
324
325
326
327
    return kbinput;
}

/* Translate ASCII characters, extended keypad values, and escape
 * sequences into their corresponding key values.  Set meta_key to TRUE
 * when we get a meta key sequence, and set func_key to TRUE when we get
328
 * a function key. */
329
int parse_kbinput(WINDOW *win)
330
{
331
    static int escapes = 0, byte_digits = 0;
332
    int *kbinput, retval = ERR;
333

334
335
    meta_key = FALSE;
    func_key = FALSE;
336

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

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

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

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

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

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

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

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

				unget_input(seq, byte_mb_len);

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

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

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

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

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

661
662
    free(kbinput);

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

667
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
668
 * keypad values, into their corresponding key values.  These sequences
669
670
671
 * are generated when the keypad doesn't support the needed keys.
 * Assume that Escape has already been read in. */
int get_escape_seq_kbinput(const int *seq, size_t seq_len)
672
{
673
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
674

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

1194
#ifdef DEBUG
1195
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d\n", retval);
1196
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1197

1198
    return retval;
1199
1200
}

1201
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1202
 * letters A (up), B (down), C (right), and D (left).  These are common
1203
1204
1205
1206
1207
 * to many escape sequences. */
int get_escape_seq_abcd(int kbinput)
{
    switch (tolower(kbinput)) {
	case 'a':
1208
	    return sc_seq_or(do_up_void, 0);
1209
	case 'b':
1210
	    return sc_seq_or(do_down_void, 0);
1211
	case 'c':
1212
	    return sc_seq_or(do_right, 0);
1213
	case 'd':
1214
	    return sc_seq_or(do_left, 0);
1215
1216
1217
1218
1219
	default:
	    return ERR;
    }
}

1220
/* Interpret the escape sequence in the keystroke buffer, the first
1221
1222
1223
 * character of which is kbinput.  Assume that the keystroke buffer
 * isn't empty, and that the initial escape has already been read in. */
int parse_escape_seq_kbinput(WINDOW *win, int kbinput)
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
{
    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);
1234
    retval = get_escape_seq_kbinput(seq, seq_len);
1235
1236
1237

    free(seq);

1238
1239
    /* If we got an unrecognized escape sequence, throw it out. */
    if (retval == ERR) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1240
	if (win == edit) {
1241
1242
1243
1244
1245
	    statusbar(_("Unknown Command"));
	    beep();
	}
    }

1246
#ifdef DEBUG
1247
    fprintf(stderr, "parse_escape_seq_kbinput(): kbinput = %d, seq_len = %lu, retval = %d\n", kbinput, (unsigned long)seq_len, retval);
1248
1249
1250
1251
1252
#endif

    return retval;
}

1253
1254
/* Translate a byte sequence: turn a three-digit decimal number (from
 * 000 to 255) into its corresponding byte value. */
1255
int get_byte_kbinput(int kbinput)
1256
{
1257
    static int byte_digits = 0, byte = 0;
1258
    int retval = ERR;
1259

1260
1261
    /* Increment the byte digit counter. */
    byte_digits++;
1262

1263
    switch (byte_digits) {
1264
	case 1:
1265
1266
	    /* First digit: This must be from zero to two.  Put it in
	     * the 100's position of the byte sequence holder. */
1267
	    if ('0' <= kbinput && kbinput <= '2')
1268
		byte = (kbinput - '0') * 100;
1269
	    else
1270
1271
		/* This isn't the start of a byte sequence.  Return this
		 * character as the result. */
1272
1273
1274
		retval = kbinput;
	    break;
	case 2:
1275
1276
1277
1278
	    /* 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. */
1279
1280
1281
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1282
	    else
1283
1284
		/* This isn't the second digit of a byte sequence.
		 * Return this character as the result. */
1285
1286
1287
		retval = kbinput;
	    break;
	case 3:
1288
	    /* Third digit: This must be from zero to five if the first
1289
1290
1291
	     * 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. */
1292
1293
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
1294
		byte += kbinput - '0';
1295
		/* The byte sequence is complete. */
1296
		retval = byte;
1297
	    } else
1298
1299
		/* This isn't the third digit of a byte sequence.
		 * Return this character as the result. */
1300
1301
		retval = kbinput;
	    break;
1302
	default:
1303
1304
1305
	    /* If there are more than three digits, return this
	     * character as the result.  (Maybe we should produce an
	     * error instead?) */
1306
1307
1308
1309
1310
1311
1312
1313
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1314
	byte = 0;
1315
1316
1317
    }

#ifdef DEBUG
1318
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1319
1320
1321
1322
1323
#endif

    return retval;
}

1324
#ifdef ENABLE_UTF8
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
/* If the character in kbinput is a valid hexadecimal digit, multiply it
 * by factor and add the result to uni. */
long add_unicode_digit(int kbinput, long factor, long *uni)
{
    long retval = ERR;

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

    return retval;
}

1343
/* Translate a Unicode sequence: turn a six-digit hexadecimal number
1344
 * (from 000000 to 10FFFF, case-insensitive) into its corresponding
1345
 * multibyte value. */
1346
long get_unicode_kbinput(int kbinput)
1347
{
1348
1349
1350
    static int uni_digits = 0;
    static long uni = 0;
    long retval = ERR;
1351

1352
    /* Increment the Unicode digit counter. */
1353
    uni_digits++;
1354

1355
    switch (uni_digits) {
1356
	case 1:
1357
1358
	    /* First digit: This must be zero or one.  Put it in the
	     * 0x100000's position of the Unicode sequence holder. */
1359
	    if ('0' <= kbinput && kbinput <= '1')
1360
		uni = (kbinput - '0') * 0x100000;
1361
	    else
1362
1363
		/* This isn't the first digit of a Unicode sequence.
		 * Return this character as the result. */
1364
1365
1366
		retval = kbinput;
	    break;
	case 2:
1367
1368
1369
1370
1371
1372
	    /* Second digit: This must be zero if the first was one, and
	     * may be any hexadecimal value if the first was zero.  Put
	     * it in the 0x10000's position of the Unicode sequence
	     * holder. */
	    if (uni == 0 || '0' == kbinput)
		retval = add_unicode_digit(kbinput, 0x10000, &uni);
1373
	    else
1374
1375
		/* This isn't the second digit of a Unicode sequence.
		 * Return this character as the result. */
1376
1377
1378
		retval = kbinput;
	    break;
	case 3:
1379
1380
1381
1382
	    /* Third digit: This may be any hexadecimal value.  Put it
	     * in the 0x1000's position of the Unicode sequence
	     * holder. */
	    retval = add_unicode_digit(kbinput, 0x1000, &uni);
1383
	    break;
1384
	case 4:
1385
1386
1387
1388
	    /* Fourth digit: This may be any hexadecimal value.  Put it
	     * in the 0x100's position of the Unicode sequence
	     * holder. */
	    retval = add_unicode_digit(kbinput, 0x100, &uni);
1389
	    break;
1390
	case 5:
1391
1392
1393
	    /* Fifth digit: This may be any hexadecimal value.  Put it
	     * in the 0x10's position of the Unicode sequence holder. */
	    retval = add_unicode_digit(kbinput, 0x10, &uni);
1394
	    break;
1395
	case 6:
1396
1397
1398
1399
1400
1401
	    /* Sixth digit: This may be any hexadecimal value.  Put it
	     * in the 0x1's position of the Unicode sequence holder. */
	    retval = add_unicode_digit(kbinput, 0x1, &uni);
	    /* If this character is a valid hexadecimal value, then the
	     * Unicode sequence is complete. */
	    if (retval == ERR)
1402
		retval = uni;
1403
1404
	    break;
	default:
1405
1406
1407
	    /* If there are more than six digits, return this character
	     * as the result.  (Maybe we should produce an error
	     * instead?) */
1408
1409
1410
	    retval = kbinput;
	    break;
    }
1411

1412
1413
    /* If we have a result, reset the Unicode digit counter and the
     * Unicode sequence holder. */
1414
    if (retval != ERR) {
1415
1416
	uni_digits = 0;
	uni = 0;
1417
    }
1418

1419
#ifdef DEBUG
1420
    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n", kbinput, uni_digits, uni, retval);
1421
1422
#endif

1423
1424
    return retval;
}
1425
#endif /* ENABLE_UTF8 */
1426

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

1433
     /* Ctrl-Space (Ctrl-2, Ctrl-@, Ctrl-`) */
1434
1435
    if (kbinput == ' ' || kbinput == '2')
	retval = NANO_CONTROL_SPACE;
1436
1437
    /* Ctrl-/ (Ctrl-7, Ctrl-_) */
    else if (kbinput == '/')
1438
	retval = NANO_CONTROL_7;
1439
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-/, Ctrl-_) */
1440
1441
1442
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
1443
1444
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
1445
1446
    /* Ctrl-@ (Ctrl-Space, Ctrl-2, Ctrl-`) to Ctrl-_ (Ctrl-/, Ctrl-7) */
    else if ('@' <= kbinput && kbinput <= '_')
1447
	retval = kbinput - '@';
1448
1449
    /* Ctrl-` (Ctrl-2, Ctrl-Space, Ctrl-@) to Ctrl-~ (Ctrl-6, Ctrl-^) */
    else if ('`' <= kbinput && kbinput <= '~')
1450
	retval = kbinput - '`';
1451
1452
1453
    else
	retval = kbinput;

1454
#ifdef DEBUG
1455
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1456
1457
#endif

1458
1459
    return retval;
}
1460

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1461
1462
/* Put the output-formatted characters in output back into the keystroke
 * buffer, so that they can be parsed and displayed as output again. */
1463
void unparse_kbinput(char *output, size_t output_len)
1464
{
1465
1466
    int *input;
    size_t i;
1467

1468
1469
1470
1471
    if (output_len == 0)
	return;

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

1473
1474
    for (i = 0; i < output_len; i++)
	input[i] = (int)output[i];
1475

1476
    unget_input(input, output_len);
1477

1478
    free(input);
1479
1480
}

1481
/* Read in a stream of characters verbatim, and return the length of the
1482
1483
1484
1485
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1486

1487
    /* Turn off flow control characters if necessary so that we can type
1488
1489
     * them in verbatim, and turn the keypad off if necessary so that we
     * don't get extended keypad values. */
1490
1491
    if (ISSET(PRESERVE))
	disable_flow_control();
1492
1493
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, FALSE);
1494
1495
1496

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

    /* Turn flow control characters back on if necessary and turn the
1499
     * keypad back on if necessary now that we're done. */
1500
1501
    if (ISSET(PRESERVE))
	enable_flow_control();
1502
1503
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, TRUE);
1504

1505
    return retval;
1506
1507
}

1508
1509
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
1510
 * the input into the corresponding multibyte value if possible.  After
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1511
 * that, leave the input as-is. */
1512
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1513
{
1514
    int *kbinput, *retval;
1515

1516
    /* Read in the first keystroke. */
1517
1518
    while ((kbinput = get_input(win, 1)) == NULL)
	;
1519

1520
#ifdef ENABLE_UTF8
1521
1522
1523
    if (using_utf8()) {
	/* Check whether the first keystroke is a valid hexadecimal
	 * digit. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1524
	long uni = get_unicode_kbinput(*kbinput);
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543

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

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

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

	    while (uni == ERR) {
1544
1545
		while ((kbinput = get_input(win, 1)) == NULL)
		    ;
1546
1547
		uni = get_unicode_kbinput(*kbinput);
	    }
1548

1549
1550
1551
	    /* Put back the multibyte equivalent of the Unicode
	     * value. */
	    uni_mb = make_mbchar(uni, &uni_mb_len);
1552

1553
	    seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1554

1555
1556
	    for (i = 0; i < uni_mb_len; i++)
		seq[i] = (unsigned char)uni_mb[i];
1557

1558
	    unget_input(seq, uni_mb_len);
1559

1560
1561
	    free(seq);
	    free(uni_mb);
1562
	}
1563
    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1564
1565
#endif /* ENABLE_UTF8 */

1566
1567
	/* Put back the first keystroke. */
	unget_input(kbinput, 1);
1568

1569
1570
    free(kbinput);

1571
    /* Get the complete sequence, and save the characters in it as the
1572
     * result. */
1573
    *kbinput_len = get_key_buffer_len();
1574
    retval = get_input(NULL, *kbinput_len);
1575
1576
1577
1578

    return retval;
}

1579
#ifndef DISABLE_MOUSE
1580
/* Handle any mouse event that may have occurred.  We currently handle
1581
1582
1583
1584
1585
1586
1587
 * 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
1588
1589
1590
1591
1592
1593
 * 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. */
1594
int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1595
1596
{
    MEVENT mevent;
1597
    bool in_bottomwin;
1598
    subnfunc *f;
1599
1600
1601
1602
1603
1604

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

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

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

1611
1612
    in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1613
    /* Handle releases/clicks of the first mouse button. */
1614
    if (mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) {
1615
1616
	/* If we're allowing shortcuts, the current shortcut list is
	 * being displayed on the last two lines of the screen, and the
1617
1618
1619
	 * 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. */
1620
	if (allow_shortcuts && !ISSET(NO_HELP) && in_bottomwin) {
1621
1622
1623
1624
	    int i;
		/* The width of all the shortcuts, except for the last
		 * two, in the shortcut list in bottomwin. */
	    int j;
1625
		/* The calculated index number of the clicked item. */
1626
1627
1628
1629
	    size_t currslen;
		/* The number of shortcuts in the current shortcut
		 * list. */

1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
	    /* 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;
	    }
1644

1645
	    /* Get the shortcut lists' length. */
1646
	    if (currmenu == MMAIN)
1647
		currslen = MAIN_VISIBLE;
1648
	    else {
1649
		currslen = length_of_list(currmenu);
1650

1651
1652
1653
1654
1655
		/* We don't show any more shortcuts than the main list
		 * does. */
		if (currslen > MAIN_VISIBLE)
		    currslen = MAIN_VISIBLE;
	    }
1656

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

1665
1666
	    /* Calculate the one-based index in the shortcut list. */
	    j = (*mouse_x / i) * 2 + *mouse_y;
1667

1668
1669
	    /* Adjust the index if we hit the last two wider ones. */
	    if ((j > currslen) && (*mouse_x % i < COLS % i))
1670
		j -= 2;
1671
1672
1673
#ifdef DEBUG
	    fprintf(stderr, "Calculated %i as index in shortcut list, currmenu = %x.\n", j, currmenu);
#endif
1674
1675
	    /* Ignore releases/clicks of the first mouse button beyond
	     * the last shortcut. */
1676
	    if (j > currslen)
1677
		return 2;
1678

1679
1680
	    /* Go through the list of functions to determine which
	     * shortcut in the current menu we released/clicked on. */
1681
	    for (f = allfuncs; f != NULL; f = f->next) {
1682
		if ((f->menus & currmenu) == 0)
1683
1684
		    continue;
		if (first_sc_for(currmenu, f->scfunc) == NULL)
1685
		    continue;
1686
1687
		/* Tick off an actually shown shortcut. */
		j -= 1;
1688
1689
		if (j == 0)
		    break;
1690
	    }
1691
#ifdef DEBUG
1692
	    fprintf(stderr, "Stopped on func %ld present in menus %x\n", (long)f->scfunc, f->menus);
1693
#endif
1694

1695
	    /* And put the corresponding key into the keyboard buffer. */
1696
	    if (f != NULL) {
1697
		const sc *s = first_sc_for(currmenu, f->scfunc);
1698
		unget_kbinput(s->seq, s->type == META, s->type == FKEY);
1699
	    }
1700
	    return 1;
1701
	} else
1702
1703
	    /* Handle releases/clicks of the first mouse button that
	     * aren't on the current shortcut list elsewhere. */
1704
	    return 0;
1705
    }
1706
1707
#if NCURSES_MOUSE_VERSION >= 2
    /* Handle presses of the fourth mouse button (upward rolls of the
1708
1709
1710
     * 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
1711
	bool in_edit = wenclose(edit, *mouse_y, *mouse_x);
1712

1713
1714
1715
1716
	if (in_bottomwin)
	    /* Translate the mouse event coordinates so that they're
	     * relative to bottomwin. */
	    wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);
1717

1718
	if (in_edit || (in_bottomwin && *mouse_y == 0)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1719
1720
	    int i;

1721
1722
1723
1724
1725
	    /* 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) ?
1726
			sc_seq_or(do_up_void, 0) : sc_seq_or(do_down_void, 0),
1727
			FALSE, FALSE);
1728
1729
1730
1731
1732
1733
1734

	    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;
1735
1736
    }
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1737
1738
1739

    /* Ignore all other mouse events. */
    return 2;
1740
}
1741
1742
#endif /* !DISABLE_MOUSE */

1743
1744
1745
1746
/* 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. */
1747
const sc *get_shortcut(int *kbinput)
1748
{
1749
    sc *s;
1750

1751
#ifdef DEBUG
1752
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %s -- ", *kbinput, meta_key ? "TRUE" : "FALSE");
1753
1754
#endif

1755
    for (s = sclist; s != NULL; s = s->next) {
1756
	if ((s->menus & currmenu) && *kbinput == s->seq
1757
		&& meta_key == (s->type == META)) {
1758
#ifdef DEBUG
1759
	    fprintf (stderr, "matched seq \"%s\", and btw meta was %d (menu is %x from %x)\n",
1760
			     s->keystr, meta_key, currmenu, s->menus);
1761
#endif
1762
	    return s;
1763
1764
	}
    }
1765
#ifdef DEBUG
1766
    fprintf (stderr, "matched nothing, btw meta was %d\n", meta_key);
1767
#endif
1768
1769
1770
1771

    return NULL;
}

1772
1773
1774
1775
1776
/* 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
1777

1778
1779
1780
1781
    for (; n > 0; n--)
	waddch(win, ' ');
}

1782
/* Blank the first line of the top portion of the window. */
1783
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1784
{
1785
    blank_line(topwin, 0, 0, COLS);
1786
1787
}

1788
1789
/* If the MORE_SPACE flag isn't set, blank the second line of the top
 * portion of the window. */
1790
1791
1792
void blank_topbar(void)
{
    if (!ISSET(MORE_SPACE))
1793
	blank_line(topwin, 1, 0, COLS);
1794
1795
}

1796
/* Blank all the lines of the middle portion of the window, i.e. the
1797
 * edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
1798
1799
void blank_edit(void)
{
1800
    int i;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1801

1802
    for (i = 0; i < editwinrows; i++)
1803
	blank_line(edit, i, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1804
1805
}

1806
/* Blank the first line of the bottom portion of the window. */
Chris Allegretta's avatar
Chris Allegretta committed
1807
1808
void blank_statusbar(void)
{
1809
    blank_line(bottomwin, 0, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1810
1811
}

1812
1813
/* If the NO_HELP flag isn't set, blank the last two lines of the bottom
 * portion of the window. */
1814
1815
1816
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
1817
1818
	blank_line(bottomwin, 1, 0, COLS);
	blank_line(bottomwin, 2, 0, COLS);
1819
1820
1821
    }
}

1822
1823
1824
/* Check if the number of keystrokes needed to blank the statusbar has
 * been pressed.  If so, blank the statusbar, unless constant cursor
 * position display is on. */
1825
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1826
{
1827
    if (statusblank > 0) {
1828
	statusblank--;
1829

1830
1831
1832
1833
1834
1835
	if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
	    blank_statusbar();
	    wnoutrefresh(bottomwin);
	    reset_cursor();
	    wnoutrefresh(edit);
	}
Chris Allegretta's avatar
Chris Allegretta committed
1836
1837
1838
    }
}

1839
1840
1841
1842
/* Convert buf into a string that can be displayed on screen.  The
 * caller wants to display buf starting with column start_col, and
 * extending for at most len columns.  start_col is zero-based.  len is
 * one-based, so len == 0 means you get "" returned.  The returned
1843
1844
1845
1846
1847
 * string is dynamically allocated, and should be freed.  If dollars is
 * TRUE, the caller might put "$" at the beginning or end of the line if
 * it's too long. */
char *display_string(const char *buf, size_t start_col, size_t len, bool
	dollars)
1848
1849
{
    size_t start_index;
1850
	/* Index in buf of the first character shown. */
1851
    size_t column;
1852
	/* Screen column that start_index corresponds to. */
1853
1854
1855
1856
1857
1858
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */
1859
    char *buf_mb;
1860
1861
    int buf_mb_len;

1862
1863
1864
1865
1866
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

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

1870
1871
    buf_mb = charalloc(mb_cur_max());

1872
1873
    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
1874

1875
    assert(column <= start_col);
1876

1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
    /* Make sure there's enough room for the initial character, whether
     * it's a multibyte control character, a non-control multibyte
     * character, a tab character, or a null terminator.  Rationale:
     *
     * multibyte control character followed by a null terminator:
     *     1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0')
     * multibyte non-control character followed by a null terminator:
     *     mb_cur_max() bytes + 1 byte ('\0')
     * tab character followed by a null terminator:
     *     mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0')
     *
     * Since tabsize has a minimum value of 1, it can substitute for 1
     * byte above. */
    alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
    converted = charalloc(alloc_len);
1892

1893
    index = 0;
1894
    seen_wide = FALSE;
1895

1896
1897
    if (buf[start_index] != '\0' && buf[start_index] != '\t' &&
	(column < start_col || (dollars && column > 0))) {
1898
1899
	/* We don't display all of buf[start_index] since it starts to
	 * the left of the screen. */
1900
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1901

1902
	if (is_cntrl_mbchar(buf_mb)) {
1903
	    if (column < start_col) {
1904
1905
		char *ctrl_buf_mb = charalloc(mb_cur_max());
		int ctrl_buf_mb_len, i;
1906

1907
1908
		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
			&ctrl_buf_mb_len);
1909

1910
1911
		for (i = 0; i < ctrl_buf_mb_len; i++)
		    converted[index++] = ctrl_buf_mb[i];
1912

1913
		start_col += mbwidth(ctrl_buf_mb);
1914

1915
		free(ctrl_buf_mb);
1916

1917
		start_index += buf_mb_len;
1918
	    }
1919
	}
1920
#ifdef ENABLE_UTF8
1921
1922
1923
1924
1925
1926
	else if (using_utf8() && mbwidth(buf_mb) == 2) {
	    if (column >= start_col) {
		converted[index++] = ' ';
		start_col++;
	    }

1927
	    converted[index++] = ' ';
1928
	    start_col++;
1929
1930

	    start_index += buf_mb_len;
1931
	}
1932
#endif
1933
1934
    }

1935
    while (buf[start_index] != '\0') {
1936
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1937

1938
1939
1940
	if (mbwidth(buf + start_index) > 1)
	    seen_wide = TRUE;

1941
1942
1943
1944
1945
1946
1947
1948
	/* Make sure there's enough room for the next character, whether
	 * it's a multibyte control character, a non-control multibyte
	 * character, a tab character, or a null terminator. */
	if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) {
	    alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
	    converted = charealloc(converted, alloc_len);
	}

1949
	/* If buf contains a tab character, interpret it. */
1950
	if (*buf_mb == '\t') {
1951
#ifndef NANO_TINY
1952
1953
1954
1955
1956
1957
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = 0; i < whitespace_len[0]; i++)
		    converted[index++] = whitespace[i];
	    } else
1958
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1959
		converted[index++] = ' ';
1960
	    start_col++;
1961
	    while (start_col % tabsize != 0) {
1962
		converted[index++] = ' ';
1963
1964
		start_col++;
	    }
1965
	/* If buf contains a control character, interpret it. */
1966
	} else if (is_cntrl_mbchar(buf_mb)) {
1967
1968
	    char *ctrl_buf_mb = charalloc(mb_cur_max());
	    int ctrl_buf_mb_len, i;
1969

1970
	    converted[index++] = '^';
1971
1972
	    start_col++;

1973
1974
	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
		&ctrl_buf_mb_len);
1975

1976
1977
	    for (i = 0; i < ctrl_buf_mb_len; i++)
		converted[index++] = ctrl_buf_mb[i];
1978

1979
	    start_col += mbwidth(ctrl_buf_mb);
1980

1981
	    free(ctrl_buf_mb);
1982
	/* If buf contains a space character, interpret it. */
1983
	} else if (*buf_mb == ' ') {
1984
#ifndef NANO_TINY
1985
1986
1987
1988
1989
1990
1991
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = whitespace_len[0]; i < whitespace_len[0] +
			whitespace_len[1]; i++)
		    converted[index++] = whitespace[i];
	    } else
1992
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1993
		converted[index++] = ' ';
1994
	    start_col++;
1995
1996
	/* If buf contains a non-control character, interpret it.  If buf
	 * contains an invalid multibyte sequence, display it as such. */
1997
	} else {
1998
1999
	    char *nctrl_buf_mb = charalloc(mb_cur_max());
	    int nctrl_buf_mb_len, i;
2000

2001
#ifdef ENABLE_UTF8
2002
2003
2004
	    /* Make sure an invalid sequence-starter byte is properly
	     * terminated, so that it doesn't pick up lingering bytes
	     * of any previous content. */
2005
2006
2007
	    if (using_utf8() && buf_mb_len == 1)
		buf_mb[1] = '\0';
#endif
2008

2009
2010
	    nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
		&nctrl_buf_mb_len);
2011

2012
2013
2014
2015
2016
2017
	    for (i = 0; i < nctrl_buf_mb_len; i++)
		converted[index++] = nctrl_buf_mb[i];

	    start_col += mbwidth(nctrl_buf_mb);

	    free(nctrl_buf_mb);
2018
2019
	}

2020
	start_index += buf_mb_len;
2021
2022
    }

2023
2024
    free(buf_mb);

2025
2026
    assert(alloc_len >= index + 1);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2027
    /* Null-terminate converted. */
2028
    converted[index] = '\0';
2029
2030
2031

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

2034
    return converted;
2035
2036
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2037
2038
2039
2040
2041
2042
/* 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. */
2043
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2044
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2045
    int space = COLS;
2046
	/* The space we have available for display. */
2047
    size_t verlen = strlenpt(PACKAGE_STRING) + 1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2048
2049
	/* The length of the version message in columns, plus one for
	 * padding. */
2050
    const char *prefix;
2051
	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
2052
    size_t prefixlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2053
	/* The length of the prefix in columns, plus one for padding. */
2054
    const char *state;
2055
2056
	/* "Modified", "View", or "".  Shows the state of this
	 * buffer. */
2057
    size_t statelen = 0;
2058
	/* The length of the state in columns, or the length of
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2059
2060
	 * "Modified" if the state is blank and we're not in the file
	 * browser. */
2061
    char *exppath = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2062
	/* The filename, expanded for display. */
2063
    bool newfie = FALSE;
2064
	/* Do we say "New Buffer"? */
2065
    bool dots = FALSE;
2066
2067
	/* Do we put an ellipsis before the path? */

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

2070
2071
2072
    if (interface_color_pair[TITLE_BAR].bright)
	wattron(topwin, A_BOLD);
    wattron(topwin, interface_color_pair[TITLE_BAR].pairnum);
2073

2074
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2075

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2076
2077
2078
2079
    /* space has to be at least 4: two spaces before the version message,
     * at least one character of the version message, and one space
     * after the version message. */
    if (space < 4)
2080
2081
	space = 0;
    else {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2082
2083
2084
2085
	/* Limit verlen to 1/3 the length of the screen in columns,
	 * minus three columns for spaces. */
	if (verlen > (COLS / 3) - 3)
	    verlen = (COLS / 3) - 3;
2086
    }
Chris Allegretta's avatar
Chris Allegretta committed
2087

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2088
    if (space >= 4) {
2089
2090
	/* Add a space after the version message, and account for both
	 * it and the two spaces before it. */
2091
2092
	mvwaddnstr(topwin, 0, 2, PACKAGE_STRING,
		actual_x(PACKAGE_STRING, verlen));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2093
2094
2095
2096
	verlen += 3;

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

2099
2100
2101
2102
2103
2104
2105
2106
2107
#ifndef DISABLE_BROWSER
    /* Don't display the state if we're in the file browser. */
    if (path != NULL)
	state = "";
    else
#endif
	state = openfile->modified ? _("Modified") : ISSET(VIEW_MODE) ?
		_("View") : "";

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2111
2112
    /* If possible, add a space before state. */
    if (space > 0 && statelen < space)
2113
	statelen++;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2114
    else
2115
2116
2117
	goto the_end;

#ifndef DISABLE_BROWSER
2118
    /* path should be a directory if we're in the file browser. */
2119
2120
2121
2122
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
2123
    if (openfile->filename[0] == '\0') {
2124
	prefix = _("New Buffer");
2125
	newfie = TRUE;
2126
2127
    } else
	prefix = _("File:");
2128

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2131
    /* If newfie is FALSE, add a space after prefix. */
2132
    if (!newfie && prefixlen + statelen < space)
2133
2134
	prefixlen++;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2135
    /* If we're not in the file browser, set path to the current
2136
     * filename. */
2137
    if (path == NULL)
2138
	path = openfile->filename;
2139

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2140
    /* Account for the full lengths of the prefix and the state. */
2141
2142
2143
2144
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2145
	/* space is now the room we have for the filename. */
2146

2147
    if (!newfie) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2148
	size_t lenpt = strlenpt(path), start_col;
2149

2150
2151
2152
	/* Don't set dots to TRUE if we have fewer than eight columns
	 * (i.e. one column for padding, plus seven columns for a
	 * filename). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2153
	dots = (space >= 8 && lenpt >= space);
2154
2155
2156
2157
2158
2159
2160
2161

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

	exppath = display_string(path, start_col, space, FALSE);
2162
2163
    }

2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
    /* If dots is TRUE, we will display something like "File:
     * ...ename". */
    if (dots) {
	mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix,
		prefixlen));
	if (space <= -3 || newfie)
	    goto the_end;
	waddch(topwin, ' ');
	waddnstr(topwin, "...", space + 3);
	if (space <= 0)
	    goto the_end;
	waddstr(topwin, exppath);
    } else {
2177
2178
2179
	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
	    /* The length of the expanded filename. */

2180
	/* There is room for the whole filename, so we center it. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2181
2182
	mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3),
		prefix, actual_x(prefix, prefixlen));
2183
	if (!newfie) {
2184
2185
2186
2187
2188
2189
2190
2191
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    }

  the_end:
    free(exppath);

2192
    if (state[0] != '\0') {
2193
	if (statelen >= COLS - 1)
2194
2195
	    mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
	else {
2196
	    assert(COLS - statelen - 1 >= 0);
2197

2198
	    mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2199
		actual_x(state, statelen));
2200
	}
2201
    }
2202

2203
2204
    wattroff(topwin, A_BOLD);
    wattroff(topwin, interface_color_pair[TITLE_BAR].pairnum);
2205

2206
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2207
    reset_cursor();
2208
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2209
2210
}

2211
2212
/* Mark the current file as modified if it isn't already, and then
 * update the titlebar to display the file's new status. */
2213
2214
void set_modified(void)
{
2215
2216
    if (!openfile->modified) {
	openfile->modified = TRUE;
2217
	titlebar(NULL);
2218
#ifndef NANO_TINY
2219
	if (ISSET(LOCKING)) {
2220
	    if (openfile->filename[0] == '\0')
2221
2222
		return;
	    else if (openfile->lock_filename == NULL) {
2223
2224
		/* TRANSLATORS: Try to keep this at most 76 characters. */
		statusbar(_("Warning: Modifying a file which is not locked, check directory permission?"));
2225
2226
	    } else {
		write_lockfile(openfile->lock_filename,
2227
2228
				get_full_path(openfile->filename), TRUE);
	    }
2229
	}
2230
#endif
2231
2232
2233
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2234
2235
2236
/* Display a message on the statusbar, and set disable_cursorpos to
 * TRUE, so that the message won't be immediately overwritten if
 * constant cursor position display is on. */
2237
2238
2239
void statusbar(const char *msg, ...)
{
    va_list ap;
2240
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
2241
    size_t start_x;
2242
2243
2244
2245
#ifndef NANO_TINY
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

    UNSET(WHITESPACE_DISPLAY);
2246
#endif
2247
2248
2249
2250
2251

    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(). */
2252
    if (isendwin()) {
2253
2254
2255
2256
2257
2258
2259
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

    blank_statusbar();

2260
2261
2262
2263
    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
2264
    free(bar);
2265
2266

#ifndef NANO_TINY
2267
2268
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2269
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2270
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2271

2272
    wmove(bottomwin, 0, start_x);
2273
2274
2275
    if (interface_color_pair[STATUS_BAR].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2276
2277
2278
2279
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
2280
2281
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2282
2283
2284
2285
2286
    wnoutrefresh(bottomwin);
    reset_cursor();
    wnoutrefresh(edit);
	/* Leave the cursor at its position in the edit window, not in
	 * the statusbar. */
2287

2288
    disable_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2289
2290
2291

    /* If we're doing quick statusbar blanking, and constant cursor
     * position display is off, blank the statusbar after only one
2292
2293
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2294
    statusblank =
2295
#ifndef NANO_TINY
2296
	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2297
#endif
2298
	26;
2299
2300
}

2301
2302
/* Display the shortcut list in s on the last two rows of the bottom
 * portion of the window. */
2303
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2304
{
2305
    size_t i, colwidth, slen;
2306
2307
    subnfunc *f;
    const sc *s;
2308

2309
2310
2311
    /* Set the global variable to the given menu. */
    currmenu = menu;

Chris Allegretta's avatar
Chris Allegretta committed
2312
2313
2314
    if (ISSET(NO_HELP))
	return;

2315
    if (menu == MMAIN) {
2316
	slen = MAIN_VISIBLE;
2317

2318
	assert(slen <= length_of_list(menu));
2319
    } else {
2320
	slen = length_of_list(menu);
2321

2322
	/* Don't show any more shortcuts than the main list does. */
2323
2324
2325
2326
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2327
2328
2329
2330
    /* 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. */
2331
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2332

2333
    blank_bottombars();
2334

2335
2336
2337
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2338

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

2341
#ifdef DEBUG
2342
	fprintf(stderr, "Checking menu items....");
2343
#endif
2344
	if ((f->menus & menu) == 0)
2345
	    continue;
2346

2347
#ifdef DEBUG
2348
	fprintf(stderr, "found one! f->menus = %x, desc = \"%s\"\n", f->menus, f->desc);
2349
#endif
2350
2351
	s = first_sc_for(menu, f->scfunc);
	if (s == NULL) {
2352
2353
2354
#ifdef DEBUG
	    fprintf(stderr, "Whoops, guess not, no shortcut key found for func!\n");
#endif
2355
2356
	    continue;
	}
2357
	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2358
#ifdef DEBUG
2359
	fprintf(stderr, "Calling onekey with keystr \"%s\" and desc \"%s\"\n", s->keystr, f->desc);
2360
#endif
2361
	onekey(s->keystr, _(f->desc), colwidth + (COLS % colwidth));
2362
	i++;
Chris Allegretta's avatar
Chris Allegretta committed
2363
    }
2364

2365
2366
    wnoutrefresh(bottomwin);
    reset_cursor();
2367
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2368
2369
}

2370
2371
2372
2373
2374
2375
/* Write a shortcut key to the help area at the bottom of the window.
 * keystroke is e.g. "^G" and desc is e.g. "Get Help".  We are careful
 * to write at most len characters, even if len is very small and
 * keystroke and desc are long.  Note that waddnstr(,,(size_t)-1) adds
 * the whole string!  We do not bother padding the entry with blanks. */
void onekey(const char *keystroke, const char *desc, size_t len)
Chris Allegretta's avatar
Chris Allegretta committed
2376
{
2377
2378
    size_t keystroke_len = strlenpt(keystroke) + 1;

2379
2380
    assert(keystroke != NULL && desc != NULL);

2381
2382
2383
    if (interface_color_pair[KEY_COMBO].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2384
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2385
2386
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2387
2388
2389
2390
2391
2392

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

2393
2394
    if (len > 0) {
	waddch(bottomwin, ' ');
2395
2396
2397
	if (interface_color_pair[FUNCTION_TAG].bright)
	    wattron(bottomwin, A_BOLD);
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
2398
	waddnstr(bottomwin, desc, actual_x(desc, len));
2399
2400
	wattroff(bottomwin, A_BOLD);
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
Chris Allegretta's avatar
Chris Allegretta committed
2401
2402
2403
    }
}

2404
2405
/* Reset current_y, based on the position of current, and put the cursor
 * in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2406
2407
void reset_cursor(void)
{
2408
    size_t xpt;
2409
2410
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
2411
    if (openfile == NULL) {
2412
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2413
	return;
2414
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2415

2416
    xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2417

2418
#ifndef NANO_TINY
2419
2420
    if (ISSET(SOFTWRAP)) {
	filestruct *tmp;
2421
2422
	openfile->current_y = 0;

2423
	for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next)
2424
	    openfile->current_y += (strlenpt(tmp->data) / COLS) + 1;
2425

2426
	openfile->current_y += xplustabs() / COLS;
2427
	if (openfile->current_y < editwinrows)
2428
	    wmove(edit, openfile->current_y, xpt % COLS);
2429
2430
2431
    } else
#endif
    {
2432
2433
2434
2435
2436
2437
	openfile->current_y = openfile->current->lineno -
	    openfile->edittop->lineno;

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

2440
2441
2442
2443
2444
2445
2446
2447
/* 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. */
2448
void edit_draw(filestruct *fileptr, const char *converted, int
2449
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2450
{
2451
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2452
2453
2454
2455
2456
2457
2458
2459
2460
    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. */
2461
2462
#endif

2463
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2464
    assert(strlenpt(converted) <= COLS);
2465

2466
2467
2468
    /* 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);
2469

2470
#ifndef USE_SLANG
2471
2472
2473
    /* 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. */
2474
2475
    if (seen_wide)
	wredrawln(edit, line, 1);
2476
#endif
2477

2478
#ifndef DISABLE_COLOR
2479
2480
2481
2482
    /* If color syntaxes are available and turned on, we need to display
     * them. */
    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
	const colortype *tmpcolor = openfile->colorstrings;
2483

2484
2485
2486
2487
	/* If there are multiline regexes, make sure there is a cache. */
	if (openfile->syntax->nmultis > 0)
	    alloc_multidata_if_needed(fileptr);

2488
2489
2490
	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
2491
	    int paintlen = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2492
2493
		/* Number of chars to paint on this line.  There are
		 * COLS characters on a whole line. */
2494
	    size_t index;
2495
		/* Index in converted where we paint. */
2496
2497
2498
2499
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2500
2501
2502
2503

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2504
2505
2506
	    /* 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. */
2507

2508
	    /* First case, tmpcolor is a single-line expression. */
2509
	    if (tmpcolor->end == NULL) {
2510
2511
2512
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2513
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2514
2515
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2516
2517
2518
		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
2519
2520
2521
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2522
		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2523
2524
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2525
			break;
2526
2527
		    /* Translate the match to the beginning of the
		     * line. */
2528
2529
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2530
2531
2532

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2533
			startmatch.rm_eo++;
2534
		    else if (startmatch.rm_so < endpos &&
2535
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2536
2537
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2538
				startmatch.rm_so) - start;
2539

2540
2541
2542
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2543
2544
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2545
2546
2547

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

2548
			mvwaddnstr(edit, line, x_start, converted +
2549
				index, paintlen);
2550
		    }
2551
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2552
		}
2553
	    } else {	/* This is a multiline expression. */
2554
		const filestruct *start_line = fileptr->prev;
2555
		    /* The first line before fileptr that matches 'start'. */
2556
		regoff_t start_col;
2557
		    /* Where the match starts in that line. */
2558
		const filestruct *end_line;
2559
		    /* The line that matches 'end'. */
2560

2561
2562
		/* First see if the multidata was maybe already calculated. */
		if (fileptr->multidata[tmpcolor->id] == CNONE)
2563
		    goto end_of_loop;
2564
		else if (fileptr->multidata[tmpcolor->id] == CWHOLELINE) {
2565
		    mvwaddnstr(edit, line, 0, converted, -1);
2566
		    goto end_of_loop;
2567
		} else if (fileptr->multidata[tmpcolor->id] == CBEGINBEFORE) {
2568
		    regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0);
2569
2570
2571
		    /* If the coloured part is scrolled off, skip it. */
		    if (endmatch.rm_eo <= startpos)
			goto end_of_loop;
2572
2573
2574
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
			endmatch.rm_eo) - start);
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2575
		    goto end_of_loop;
2576
		} if (fileptr->multidata[tmpcolor->id] == -1)
2577
2578
		    /* Assume this until proven otherwise below. */
		    fileptr->multidata[tmpcolor->id] = CNONE;
2579

2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
		/* 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. */

2590
		while (start_line != NULL && regexec(tmpcolor->start,
2591
2592
2593
2594
			start_line->data, 1, &startmatch, 0) == REG_NOMATCH) {
		    /* There is no start; but if there is an end on this line,
		     * there is no need to look for starts on earlier lines. */
		    if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) == 0)
2595
2596
2597
			goto step_two;
		    start_line = start_line->prev;
		}
2598

2599
2600
2601
2602
		/* If no start was found, skip to the next step. */
		if (start_line == NULL)
		    goto step_two;

2603
		/* If a found start has been qualified as an end earlier,
2604
		 * believe it and skip to the next step. */
2605
		if (start_line->multidata != NULL &&
2606
2607
			(start_line->multidata[tmpcolor->id] == CBEGINBEFORE ||
			start_line->multidata[tmpcolor->id] == CSTARTENDHERE))
2608
2609
		    goto step_two;

2610
		/* Skip over a zero-length regex match. */
2611
		if (startmatch.rm_so == startmatch.rm_eo)
2612
		    startmatch.rm_eo++;
2613
		else {
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
		    /* Now start_line is the first line before fileptr
		     * containing a start match.  Is there a start on
		     * this line not followed by an end on this line? */
		    start_col = 0;
		    while (TRUE) {
			start_col += startmatch.rm_so;
			startmatch.rm_eo -= startmatch.rm_so;
			if (regexec(tmpcolor->end, start_line->data +
				start_col + startmatch.rm_eo, 0, NULL,
				(start_col + startmatch.rm_eo == 0) ?
				0 : REG_NOTBOL) == REG_NOMATCH)
			    /* No end found after this start. */
			    break;
			start_col++;
			if (regexec(tmpcolor->start, start_line->data +
				start_col, 1, &startmatch,
				REG_NOTBOL) == REG_NOMATCH)
			    /* No later start on this line. */
			    goto step_two;
		    }
		    /* Indeed, there is a start not followed on this
		     * line by an end. */

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

Benno Schulenberg's avatar
Benno Schulenberg committed
2646
		    /* If no end was found, or it is too early, next step. */
2647
		    if (end_line == NULL)
2648
			goto step_two;
2649
2650
2651
2652
		    if (end_line == fileptr && endmatch.rm_eo <= startpos) {
			fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
			goto step_two;
		    }
2653

2654
2655
2656
2657
2658
2659
2660
		    /* Now paint the start of fileptr.  If the start of
		     * fileptr is on a different line from the end,
		     * paintlen is -1, meaning 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. */
2661
		    if (end_line != fileptr) {
2662
			paintlen = -1;
2663
			fileptr->multidata[tmpcolor->id] = CWHOLELINE;
2664
2665
2666
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CWHOLELINE\n", tmpcolor->id, line);
#endif
2667
		    } else {
2668
2669
2670
			paintlen = actual_x(converted,
				strnlenpt(fileptr->data,
				endmatch.rm_eo) - start);
2671
			fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
2672
2673
2674
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CBEGINBEFORE\n", tmpcolor->id, line);
#endif
2675
		    }
2676
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2677
2678
2679
2680
		    /* If the whole line has been painted, don't bother
		     * looking for any more starts. */
		    if (paintlen < 0)
			goto end_of_loop;
2681
  step_two:
2682
2683
2684
		    /* 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;
2685
2686
2687
2688
2689
2690
2691
2692
2693

		    while (start_col < endpos) {
			if (regexec(tmpcolor->start, fileptr->data +
				start_col, 1, &startmatch, (start_col ==
				0) ? 0 : REG_NOTBOL) == REG_NOMATCH ||
				start_col + startmatch.rm_so >= endpos)
			    /* No more starts on this line. */
			    break;
			/* Translate the match to be relative to the
2694
			 * beginning of the line. */
2695
2696
2697
2698
			startmatch.rm_so += start_col;
			startmatch.rm_eo += start_col;

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

2702
			index = actual_x(converted, x_start);
2703

2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
			if (regexec(tmpcolor->end, fileptr->data +
				startmatch.rm_eo, 1, &endmatch,
				(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 &&
				endmatch.rm_eo > startmatch.rm_so) {
				paintlen = actual_x(converted + index,
					strnlenpt(fileptr->data,
					endmatch.rm_eo) - start -
					x_start);
2721

2722
2723
2724
2725
				assert(0 <= x_start && x_start < COLS);

				mvwaddnstr(edit, line, x_start,
					converted + index, paintlen);
2726
				if (paintlen > 0) {
2727
				    fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
2728
2729
2730
2731
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CSTARTENDHERE\n", tmpcolor->id, line);
#endif
				}
2732
			    }
2733
			    start_col = endmatch.rm_eo;
2734
2735
2736
2737
2738
2739
2740
			} else {
			    /* There is no end on this line.  But we
			     * haven't yet looked for one on later
			     * lines. */
			    end_line = fileptr->next;

			    while (end_line != NULL &&
2741
2742
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
2743
				end_line = end_line->next;
2744

2745
2746
			    if (end_line != NULL) {
				assert(0 <= x_start && x_start < COLS);
2747

2748
2749
				mvwaddnstr(edit, line, x_start,
					converted + index, -1);
Benno Schulenberg's avatar
Benno Schulenberg committed
2750
				fileptr->multidata[tmpcolor->id] = CENDAFTER;
2751
2752
2753
#ifdef DEBUG
    fprintf(stderr, "  Marking for id %i  line %i as CENDAFTER\n", tmpcolor->id, line);
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2754
2755
				/* We painted to the end of the line, so
				 * don't bother checking any more starts. */
2756
2757
				break;
			    }
2758
			    start_col = startmatch.rm_so + 1;
2759
2760
			}
		    }
2761
2762
		}
	    }
2763
  end_of_loop:
2764
2765
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2766
	}
2767
    }
2768
#endif /* !DISABLE_COLOR */
2769

2770
#ifndef NANO_TINY
2771
    /* If the mark is on, we need to display it. */
2772
    if (openfile->mark_set && (fileptr->lineno <=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2773
	openfile->mark_begin->lineno || fileptr->lineno <=
2774
	openfile->current->lineno) && (fileptr->lineno >=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2775
	openfile->mark_begin->lineno || fileptr->lineno >=
2776
	openfile->current->lineno)) {
2777
	/* fileptr is at least partially selected. */
2778
	const filestruct *top;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2779
	    /* Either current or mark_begin, whichever is first. */
2780
	size_t top_x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2781
	    /* current_x or mark_begin_x, corresponding to top. */
2782
2783
	const filestruct *bot;
	size_t bot_x;
2784
	int x_start;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2785
	    /* Starting column for mvwaddnstr().  Zero-based. */
2786
	int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2787
2788
	    /* Number of characters to paint on this line.  There are
	     * COLS characters on a whole line. */
2789
	size_t index;
2790
	    /* Index in converted where we paint. */
2791

2792
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2793
2794
2795
2796
2797

	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
2798

2799
	/* The selected bit of fileptr is on this page. */
2800
2801
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2802
2803
2804
2805

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

2807
2808
2809
2810
2811
	    /* 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. */
2812
2813
2814
2815
2816
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
			start);
2817
2818
2819
2820
2821
2822
2823
2824

	    /* 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;
	    }
2825
2826
2827

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

2828
	    index = actual_x(converted, x_start);
2829

2830
2831
2832
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2833
	    wattron(edit, hilite_attribute);
2834
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2835
		paintlen);
2836
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2837
	}
2838
    }
2839
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2840
2841
}

2842
/* Just update one line in the edit buffer.  This is basically a wrapper
2843
 * for edit_draw().  The line will be displayed starting with
2844
 * fileptr->data[index].  Likely arguments are current_x or zero.
2845
 * Returns: Number of additional lines consumed (needed for SOFTWRAP). */
2846
int update_line(filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2847
{
2848
    int line = 0;
2849
	/* The line in the edit window that we want to update. */
2850
    int extralinesused = 0;
2851
2852
2853
2854
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2855

2856
    assert(fileptr != NULL);
2857

2858
#ifndef NANO_TINY
2859
    if (ISSET(SOFTWRAP)) {
2860
2861
	filestruct *tmp;

2862
2863
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2864
    } else
2865
#endif
2866
2867
	line = fileptr->lineno - openfile->edittop->lineno;

2868
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2869
	return 1;
2870

2871
    /* First, blank out the line. */
2872
    blank_line(edit, line, 0, COLS);
2873

2874
2875
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2876
#ifndef NANO_TINY
2877
2878
2879
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2880
#endif
2881
	index = strnlenpt(fileptr->data, index);
2882
    page_start = get_page_start(index);
2883

2884
2885
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2886
2887
2888
#ifdef NANO_TINY
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
#else
2889
2890
2891
    converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2892
	fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2893
#endif
2894
#endif /* !NANO_TINY */
2895

2896
    /* Paint the line. */
2897
    edit_draw(fileptr, converted, line, page_start);
2898
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2899

2900
#ifndef NANO_TINY
2901
    if (!ISSET(SOFTWRAP)) {
2902
#endif
2903
2904
2905
2906
	if (page_start > 0)
	    mvwaddch(edit, line, 0, '$');
	if (strlenpt(fileptr->data) > page_start + COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
2907
#ifndef NANO_TINY
2908
    } else {
2909
	size_t full_length = strlenpt(fileptr->data);
2910
	for (index += COLS; index <= full_length && line < editwinrows; index += COLS) {
2911
2912
	    line++;
#ifdef DEBUG
2913
	    fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
2914
#endif
2915
	    blank_line(edit, line, 0, COLS);
2916
2917

	    /* Expand the line, replacing tabs with spaces, and control
2918
	     * characters with their displayed forms. */
2919
2920
2921
2922
2923
	    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
2924
2925
2926

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2927
	    free(converted);
2928
2929
2930
	    extralinesused++;
	}
    }
2931
#endif /* !NANO_TINY */
2932
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2933
2934
}

2935
2936
2937
2938
/* 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)
2939
2940
{
    return
2941
#ifndef NANO_TINY
2942
	openfile->mark_set ||
2943
#endif
2944
	get_page_start(pww_save) !=
2945
	get_page_start(openfile->placewewant);
2946
2947
}

2948
/* When edittop changes, try and figure out how many lines
2949
 * we really have to work with (i.e. set maxrows). */
2950
2951
2952
2953
2954
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2955
2956
2957
2958
2959
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2960
2961
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2962
	maxrows++;
2963
	n += strlenpt(foo->data) / COLS;
2964
2965
2966
	foo = foo->next;
    }

2967
2968
2969
    if (n < editwinrows)
	maxrows += editwinrows - n;

2970
#ifdef DEBUG
2971
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2972
2973
2974
#endif
}

2975
2976
/* 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
2977
2978
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
2979
2980
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is FALSE. */
2981
void edit_scroll(scroll_dir direction, ssize_t nlines)
2982
{
2983
    ssize_t i;
2984
    filestruct *foo;
2985
    bool do_redraw = need_screen_update(0);
2986

2987
2988
    /* Don't bother scrolling less than one line. */
    if (nlines < 1)
2989
2990
	return;

2991
2992
2993
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

2994
    /* Move the top line of the edit window up or down (depending on the
2995
2996
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
2997
    for (i = nlines; i > 0; i--) {
2998
	if (direction == UPWARD) {
2999
	    if (openfile->edittop == openfile->fileage)
3000
		break;
3001
	    openfile->edittop = openfile->edittop->prev;
3002
	} else {
3003
	    if (openfile->edittop == openfile->filebot)
3004
		break;
3005
	    openfile->edittop = openfile->edittop->next;
3006
	}
3007
3008

#ifndef NANO_TINY
3009
	/* Don't over-scroll on long lines. */
3010
	if (ISSET(SOFTWRAP) && direction == UPWARD) {
3011
	    ssize_t len = strlenpt(openfile->edittop->data) / COLS;
3012
	    i -= len;
3013
3014
3015
	    if (len > 0)
		do_redraw = TRUE;
	}
3016
#endif
3017
3018
    }

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3022
3023
    /* Don't bother scrolling zero lines or more than the number of
     * lines in the edit window minus one; in both cases, get out, and
3024
     * call edit_refresh() beforehand if we need to. */
3025
    if (nlines == 0 || do_redraw || nlines >= editwinrows) {
3026
	if (do_redraw || nlines >= editwinrows)
3027
	    edit_refresh_needed = TRUE;
3028
3029
	return;
    }
3030
3031
3032

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
3033
    scrollok(edit, TRUE);
3034
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
3035
3036
    scrollok(edit, FALSE);

3037
3038
3039
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

3040
3041
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
3042
3043
    if ((direction == UPWARD && openfile->edittop ==
	openfile->fileage) || (direction == DOWNWARD &&
3044
3045
	openfile->edittop->lineno + editwinrows - 1 >=
	openfile->filebot->lineno))
3046
	nlines = editwinrows;
3047

3048
3049
3050
3051
3052
3053
    /* 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
3054

3055
3056
    if (nlines > editwinrows)
	nlines = editwinrows;
3057
3058
3059

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

3062
3063
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
3064
    if (direction == DOWNWARD) {
3065
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
3066
3067
3068
	    foo = foo->next;
    }

3069
3070
3071
3072
3073
3074
    /* 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--) {
3075
3076
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
3077
3078
3079
3080
3081
	    if (do_redraw)
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
3082
		openfile->current_x : 0);
3083
	foo = foo->next;
3084
    }
3085
    compute_maxrows();
3086
3087
3088
}

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

3095
3096
    /* If either old_current or current is offscreen, scroll the edit
     * window until it's onscreen and get out. */
3097
3098
    if (old_current->lineno < openfile->edittop->lineno ||
	old_current->lineno >= openfile->edittop->lineno +
3099
	maxrows || openfile->current->lineno <
3100
	openfile->edittop->lineno || openfile->current->lineno >=
3101
	openfile->edittop->lineno + maxrows) {
3102
#ifdef DEBUG
3103
3104
	fprintf(stderr, "edit_redraw(): line %ld was offscreen, oldcurrent = %ld edittop = %ld",
		(long)openfile->current->lineno, (long)old_current->lineno, (long)openfile->edittop->lineno);
3105
#endif
3106

3107
3108
#ifndef NANO_TINY
	/* If the mark is on, update all the lines between old_current
3109
3110
	 * and either the old first line or old last line (depending on
	 * whether we've scrolled up or down) of the edit window. */
3111
	if (openfile->mark_set) {
3112
	    ssize_t old_lineno;
3113
	    filestruct *old_edittop = openfile->edittop;
3114
3115
3116
3117

	    if (old_edittop->lineno < openfile->edittop->lineno)
		old_lineno = old_edittop->lineno;
	    else
3118
		old_lineno = (old_edittop->lineno + maxrows <=
3119
3120
3121
			openfile->filebot->lineno) ?
			old_edittop->lineno + editwinrows :
			openfile->filebot->lineno;
3122
3123
3124

	    foo = old_current;

3125
	    while (foo->lineno != old_lineno) {
3126
3127
		update_line(foo, 0);

3128
		foo = (foo->lineno > old_lineno) ? foo->prev :
3129
3130
3131
3132
3133
			foo->next;
	    }
	}
#endif /* !NANO_TINY */

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3137
3138
	/* Update old_current if we're not on the same page as
	 * before. */
3139
3140
3141
	if (do_redraw)
	    update_line(old_current, 0);

3142
#ifndef NANO_TINY
3143
3144
3145
	/* If the mark is on, update all the lines between the old first
	 * line or old last line of the edit window (depending on
	 * whether we've scrolled up or down) and current. */
3146
	if (openfile->mark_set) {
3147
	    while (foo->lineno != openfile->current->lineno) {
3148
3149
		update_line(foo, 0);

3150
		foo = (foo->lineno > openfile->current->lineno) ?
3151
3152
3153
3154
3155
			foo->prev : foo->next;
	    }
	}
#endif /* !NANO_TINY */

3156
3157
3158
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3159
3160
3161
    /* Update old_current and current if we're not on the same page as
     * before.  If the mark is on, update all the lines between
     * old_current and current too. */
3162
    foo = old_current;
3163

3164
    while (foo != openfile->current) {
3165
	if (do_redraw)
3166
	    update_line(foo, 0);
3167

3168
#ifndef NANO_TINY
3169
	if (!openfile->mark_set)
3170
3171
#endif
	    break;
3172

3173
#ifndef NANO_TINY
3174
3175
	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
		foo->next;
3176
#endif
3177
    }
3178

3179
    if (do_redraw)
3180
	update_line(openfile->current, openfile->current_x);
3181
3182
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3183
3184
/* 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
3185
3186
void edit_refresh(void)
{
3187
    filestruct *foo;
3188
    int nlines;
3189

3190
    /* Figure out what maxrows should really be. */
3191
    compute_maxrows();
3192

3193
3194
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
3195
3196
	maxrows) {
#ifdef DEBUG
3197
3198
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
3199
3200
#endif

3201
	/* Make sure the current line is on the screen. */
3202
	edit_update(ISSET(SMOOTH_SCROLL) ? NONE : CENTER);
3203
    }
Chris Allegretta's avatar
Chris Allegretta committed
3204

3205
3206
    foo = openfile->edittop;

3207
#ifdef DEBUG
3208
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3209
#endif
3210

3211
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3212
	nlines += update_line(foo, (foo == openfile->current) ?
3213
		openfile->current_x : 0);
3214
3215
3216
	foo = foo->next;
    }

3217
    for (; nlines < editwinrows; nlines++)
3218
3219
3220
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
3221
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3222
3223
}

3224
3225
3226
3227
/* Move edittop to put it in range of current, keeping current in the
 * same place.  location determines how we move it: if it's CENTER, we
 * center current, and if it's NONE, we put current current_y lines
 * below edittop. */
3228
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
3229
{
3230
    filestruct *foo = openfile->current;
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
    int goal;

    /* If location is CENTER, we move edittop up (editwinrows / 2)
     * lines.  This puts current at the center of the screen.  If
     * location is NONE, we move edittop up current_y lines if current_y
     * is in range of the screen, 0 lines if current_y is less than 0,
     * or (editwinrows - 1) lines if current_y is greater than
     * (editwinrows - 1).  This puts current at the same place on the
     * screen as before, or at the top or bottom of the screen if
     * edittop is beyond either. */
    if (location == CENTER)
3242
	goal = editwinrows / 2;
3243
3244
    else {
	goal = openfile->current_y;
3245

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

3251
    for (; goal > 0 && foo->prev != NULL; goal--) {
3252
	foo = foo->prev;
3253
#ifndef NANO_TINY
3254
3255
	if (ISSET(SOFTWRAP) && foo)
	    goal -= strlenpt(foo->data) / COLS;
3256
#endif
3257
    }
3258
    openfile->edittop = foo;
3259
#ifdef DEBUG
3260
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
3261
#endif
3262
    compute_maxrows();
3263
    edit_refresh_needed = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
3264
3265
}

3266
/* Unconditionally redraw the entire screen. */
3267
void total_redraw(void)
3268
{
3269
3270
3271
3272
3273
3274
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
3275
    wrefresh(curscr);
3276
#endif
3277
3278
}

3279
3280
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
3281
3282
void total_refresh(void)
{
3283
    total_redraw();
3284
    titlebar(NULL);
3285
    edit_refresh();
3286
    bottombars(currmenu);
3287
3288
}

3289
3290
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
3291
3292
void display_main_list(void)
{
3293
#ifndef DISABLE_COLOR
3294
3295
3296
    if (openfile->syntax
	  && (openfile->syntax->formatter || openfile->syntax->linter))
	set_lint_or_format_shortcuts();
3297
3298
3299
3300
    else
	set_spell_shortcuts();
#endif

3301
    bottombars(MMAIN);
3302
3303
}

3304
3305
3306
3307
3308
3309
/* If constant is TRUE, we display the current cursor position only if
 * disable_cursorpos is FALSE.  Otherwise, we display it
 * unconditionally and set disable_cursorpos to FALSE.  If constant is
 * TRUE and disable_cursorpos is TRUE, we also set disable_cursorpos to
 * FALSE, so that we leave the current statusbar alone this time, and
 * display the current cursor position next time. */
3310
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3311
{
3312
    filestruct *f;
3313
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3314
    size_t i, cur_xpt = xplustabs() + 1;
3315
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3316
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3317

3318
    assert(openfile->fileage != NULL && openfile->current != NULL);
3319

3320
    f = openfile->current->next;
3321
    c = openfile->current->data[openfile->current_x];
3322
3323

    openfile->current->next = NULL;
3324
    openfile->current->data[openfile->current_x] = '\0';
3325
3326
3327

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

3328
    openfile->current->data[openfile->current_x] = c;
3329
    openfile->current->next = f;
3330

3331
3332
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
3333
	return;
3334
    }
Chris Allegretta's avatar
Chris Allegretta committed
3335

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3336
    /* Display the current cursor position on the statusbar, and set
3337
     * disable_cursorpos to FALSE. */
3338
    linepct = 100 * openfile->current->lineno / openfile->filebot->lineno;
3339
    colpct = 100 * cur_xpt / cur_lenpt;
3340
    charpct = (openfile->totsize == 0) ? 0 : 100 * i / openfile->totsize;
3341
3342

    statusbar(
3343
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3344
	(long)openfile->current->lineno,
3345
	(long)openfile->filebot->lineno, linepct,
3346
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3347
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3348

3349
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3350
3351
}

3352
/* Unconditionally display the current cursor position. */
3353
void do_cursorpos_void(void)
3354
{
3355
    do_cursorpos(FALSE);
3356
3357
}

3358
3359
void enable_nodelay(void)
{
3360
3361
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3362
3363
3364
3365
}

void disable_nodelay(void)
{
3366
3367
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3368
3369
}

3370
3371
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3372
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3373
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3374
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3375

3376
    y = get_page_start(y) + COLS - y;
3377
	/* Now y is the number of columns that we can display on this
3378
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3379

3380
3381
3382
3383
3384
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3385
    reset_cursor();
3386
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3387

3388
    if (highlight)
3389
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3390

3391
    /* This is so we can show zero-length matches. */
3392
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3393
	waddch(edit, ' ');
3394
    else
3395
	waddnstr(edit, word, actual_x(word, y));
3396
3397
3398

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

3400
    if (highlight)
3401
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3402
3403
}

3404
#ifndef DISABLE_EXTRA
3405
3406
#define CREDIT_LEN 54
#define XLCREDIT_LEN 9
3407

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3408
3409
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3410
3411
void do_credits(void)
{
3412
3413
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3414
    int kbinput = ERR, crpos = 0, xlpos = 0;
3415
3416
3417
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3418
3419
	VERSION,
	"",
3420
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3421
3422
3423
3424
3425
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3426
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3427
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3428
	"Mark Majeres",
3429
	"Mike Frysinger",
3430
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3441
	NULL,				/* "Special thanks to:" */
3442
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3443
3444
3445
3446
3447
3448
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3449
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3450
	"Linus Torvalds",
3451
	NULL,				/* "the many translators and the TP" */
3452
	NULL,				/* "For ncurses:" */
3453
3454
3455
3456
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3457
3458
3459
3460
3461
3462
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3463
	"(C) 1999 - 2015",
3464
	"Free Software Foundation, Inc.",
3465
3466
3467
3468
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3469
	"http://www.nano-editor.org/"
3470
3471
    };

3472
    const char *xlcredits[XLCREDIT_LEN] = {
3473
3474
3475
3476
3477
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
3478
	N_("the many translators and the TP"),
3479
3480
3481
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3482
    };
3483

3484
3485
3486
3487
3488
3489
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3490
3491
    curs_set(0);
    nodelay(edit, TRUE);
3492

3493
    blank_titlebar();
3494
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3495
    blank_edit();
3496
3497
    blank_statusbar();
    blank_bottombars();
3498

3499
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3500
    wrefresh(edit);
3501
    wrefresh(bottomwin);
3502
    napms(700);
3503

3504
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3505
	if ((kbinput = wgetch(edit)) != ERR)
3506
	    break;
3507

3508
	if (crpos < CREDIT_LEN) {
3509
	    const char *what;
3510
3511
	    size_t start_x;

3512
	    if (credits[crpos] == NULL) {
3513
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3514

3515
		what = _(xlcredits[xlpos]);
3516
		xlpos++;
3517
	    } else
3518
		what = credits[crpos];
3519

3520
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3521
3522
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3523
	}
3524

3525
3526
3527
3528
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3529
	napms(700);
3530

3531
	scrollok(edit, TRUE);
3532
	wscrl(edit, 1);
3533
	scrollok(edit, FALSE);
3534
	wrefresh(edit);
3535

3536
	if ((kbinput = wgetch(edit)) != ERR)
3537
	    break;
3538
	napms(700);
3539

3540
	scrollok(edit, TRUE);
3541
	wscrl(edit, 1);
3542
	scrollok(edit, FALSE);
3543
	wrefresh(edit);
3544
3545
    }

3546
3547
3548
    if (kbinput != ERR)
	ungetch(kbinput);

3549
3550
3551
3552
3553
3554
    if (!old_more_space || !old_no_help) {
	UNSET(MORE_SPACE);
	UNSET(NO_HELP);
	window_init();
    }

3555
    curs_set(1);
3556
    nodelay(edit, FALSE);
3557

3558
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3559
}
3560
#endif /* !DISABLE_EXTRA */