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

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

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

115
#ifndef NANO_TINY
116
117
    allow_pending_sigwinch(TRUE);
#endif
118

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
129
130
131
132
133
134
135
136
137
138
139
    } else
	while ((input = wgetch(win)) == ERR) {
	    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);
	}
140

141
#ifndef NANO_TINY
142
143
    allow_pending_sigwinch(FALSE);
#endif
144

145
146
    /* Increment the length of the keystroke buffer, and save the value
     * of the keystroke at the end of it. */
147
    key_buffer_len++;
148
149
    key_buffer = (int *)nmalloc(sizeof(int));
    key_buffer[0] = input;
150

151
152
153
154
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
155
#ifndef NANO_TINY
156
	allow_pending_sigwinch(TRUE);
157
#endif
158

159
	input = wgetch(win);
160

161
	/* If there aren't any more characters, stop reading. */
162
	if (input == ERR)
163
164
	    break;

165
166
	/* Otherwise, increment the length of the keystroke buffer, and
	 * save the value of the keystroke at the end of it. */
167
	key_buffer_len++;
168
169
170
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
	key_buffer[key_buffer_len - 1] = input;
171

172
#ifndef NANO_TINY
173
174
	allow_pending_sigwinch(FALSE);
#endif
175
176
    }

177
    /* Switch back to waiting mode for input. */
178
    nodelay(win, FALSE);
179
180

#ifdef DEBUG
181
    fprintf(stderr, "get_key_buffer(): key_buffer_len = %lu\n", (unsigned long)key_buffer_len);
182
#endif
183
}
184

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
185
/* Return the length of the keystroke buffer. */
186
size_t get_key_buffer_len(void)
187
188
189
190
{
    return key_buffer_len;
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
191
/* Add the keystrokes in input to the keystroke buffer. */
192
void unget_input(int *input, size_t input_len)
193
{
194
#ifndef NANO_TINY
195
196
    allow_pending_sigwinch(TRUE);
    allow_pending_sigwinch(FALSE);
197
#endif
198

199
    /* If input is empty, get out. */
200
    if (input_len == 0)
201
202
	return;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
203
204
    /* If adding input would put the keystroke buffer beyond maximum
     * capacity, only add enough of input to put it at maximum
205
     * capacity. */
206
207
    if (key_buffer_len + input_len < key_buffer_len)
	input_len = (size_t)-1 - key_buffer_len;
208

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
209
210
211
    /* 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. */
212
213
214
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
215

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
216
217
    /* If the keystroke buffer wasn't empty before, move its beginning
     * forward far enough so that we can add input to its beginning. */
218
219
220
    if (key_buffer_len > input_len)
	memmove(key_buffer + input_len, key_buffer,
		(key_buffer_len - input_len) * sizeof(int));
221

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
222
    /* Copy input to the beginning of the keystroke buffer. */
223
    memcpy(key_buffer, input, input_len * sizeof(int));
224
225
}

226
/* Put back the character stored in kbinput, putting it in byte range
227
228
 * beforehand.  If metakey is TRUE, put back the Escape character after
 * putting back kbinput.  If funckey is TRUE, put back the function key
229
 * (a value outside byte range) without putting it in byte range. */
230
void unget_kbinput(int kbinput, bool metakey, bool funckey)
231
{
232
    if (!funckey)
233
	kbinput = (char)kbinput;
234

235
    unget_input(&kbinput, 1);
236

237
    if (metakey) {
238
239
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
240
241
242
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
243
244
245
246
247
/* 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. */
248
int *get_input(WINDOW *win, size_t input_len)
249
{
250
    int *input;
251

252
#ifndef NANO_TINY
253
    allow_pending_sigwinch(TRUE);
254
255
256
    allow_pending_sigwinch(FALSE);
#endif

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

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

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

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

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

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

    return input;
297
298
}

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

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

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

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

333
334
    meta_key = FALSE;
    func_key = FALSE;
335

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

345
346
347
348
349
350
351
352
353
354
355
    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. */
356
357
		case 3:
		    /* Three escapes: wait for more input. */
358
359
		    break;
		default:
360
361
362
		    /* More than three escapes: limit the escape counter
		     * to no more than two, and wait for more input. */
		    escapes %= 3;
363
364
365
366
367
	    }
	    break;
	default:
	    switch (escapes) {
		case 0:
368
369
370
		    /* One non-escape: normal input mode.  Save the
		     * non-escape character as the result. */
		    retval = *kbinput;
371
372
		    break;
		case 1:
373
		    /* Reset the escape counter. */
374
		    escapes = 0;
375
		    if (get_key_buffer_len() == 0) {
376
			/* One escape followed by a non-escape, and
377
378
379
380
			 * 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. */
381
			meta_key = TRUE;
382
			retval = tolower(*kbinput);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
383
		    } else
384
			/* One escape followed by a non-escape, and
385
386
387
			 * there are other keystrokes waiting: escape
			 * sequence mode.  Interpret the escape
			 * sequence. */
388
389
			retval = parse_escape_seq_kbinput(win,
				*kbinput);
390
391
		    break;
		case 2:
392
393
394
395
396
397
		    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
398
399
400
401
402
403
404
405
406
			     * 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. */
407
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
			    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. */
442
			    escapes = 0;
443
444
445
446
447
448
			    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
449
450
451
452
453
				 * other keystrokes waiting: control
				 * character sequence mode.  Interpret
				 * the control sequence and save the
				 * corresponding control character as
				 * the result. */
454
455
456
457
458
459
460
461
462
				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;
			    }
463
			}
464
		    } else {
465
			/* Two escapes followed by a non-escape, and
466
467
468
469
			 * 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. */
470
			escapes = 0;
471
			meta_key = TRUE;
472
473
			retval = parse_escape_seq_kbinput(win,
				*kbinput);
474
		    }
475
		    break;
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
		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));
494
		    break;
495
496
	    }
    }
497

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

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

#ifdef DEBUG
655
    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);
656
657
#endif

658
659
    free(kbinput);

660
    /* Return the result. */
661
662
663
    return retval;
}

664
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
665
 * keypad values, into their corresponding key values.  These sequences
666
667
668
 * 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)
669
{
670
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
671

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

1236
#ifdef DEBUG
1237
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d\n", retval);
1238
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1239

1240
    return retval;
1241
1242
}

1243
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1244
 * letters A (up), B (down), C (right), and D (left).  These are common
1245
1246
1247
1248
1249
 * to many escape sequences. */
int get_escape_seq_abcd(int kbinput)
{
    switch (tolower(kbinput)) {
	case 'a':
1250
	    return sc_seq_or(do_up_void, 0);
1251
	case 'b':
1252
	    return sc_seq_or(do_down_void, 0);
1253
	case 'c':
1254
	    return sc_seq_or(do_right, 0);
1255
	case 'd':
1256
	    return sc_seq_or(do_left, 0);
1257
1258
1259
1260
1261
	default:
	    return ERR;
    }
}

1262
/* Interpret the escape sequence in the keystroke buffer, the first
1263
1264
1265
 * 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)
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
{
    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);
1276
    retval = get_escape_seq_kbinput(seq, seq_len);
1277
1278
1279

    free(seq);

1280
1281
    /* If we got an unrecognized escape sequence, throw it out. */
    if (retval == ERR) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1282
	if (win == edit) {
1283
1284
1285
1286
1287
	    statusbar(_("Unknown Command"));
	    beep();
	}
    }

1288
#ifdef DEBUG
1289
    fprintf(stderr, "parse_escape_seq_kbinput(): kbinput = %d, seq_len = %lu, retval = %d\n", kbinput, (unsigned long)seq_len, retval);
1290
1291
1292
1293
1294
#endif

    return retval;
}

1295
1296
/* Translate a byte sequence: turn a three-digit decimal number (from
 * 000 to 255) into its corresponding byte value. */
1297
int get_byte_kbinput(int kbinput)
1298
{
1299
    static int byte_digits = 0, byte = 0;
1300
    int retval = ERR;
1301

1302
1303
    /* Increment the byte digit counter. */
    byte_digits++;
1304

1305
    switch (byte_digits) {
1306
	case 1:
1307
1308
	    /* First digit: This must be from zero to two.  Put it in
	     * the 100's position of the byte sequence holder. */
1309
	    if ('0' <= kbinput && kbinput <= '2')
1310
		byte = (kbinput - '0') * 100;
1311
	    else
1312
1313
		/* This isn't the start of a byte sequence.  Return this
		 * character as the result. */
1314
1315
1316
		retval = kbinput;
	    break;
	case 2:
1317
1318
1319
1320
	    /* 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. */
1321
1322
1323
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1324
	    else
1325
1326
		/* This isn't the second digit of a byte sequence.
		 * Return this character as the result. */
1327
1328
1329
		retval = kbinput;
	    break;
	case 3:
1330
	    /* Third digit: This must be from zero to five if the first
1331
1332
1333
	     * 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. */
1334
1335
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
1336
		byte += kbinput - '0';
1337
		/* The byte sequence is complete. */
1338
		retval = byte;
1339
	    } else
1340
1341
		/* This isn't the third digit of a byte sequence.
		 * Return this character as the result. */
1342
1343
		retval = kbinput;
	    break;
1344
	default:
1345
1346
1347
	    /* If there are more than three digits, return this
	     * character as the result.  (Maybe we should produce an
	     * error instead?) */
1348
1349
1350
1351
1352
1353
1354
1355
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1356
	byte = 0;
1357
1358
1359
    }

#ifdef DEBUG
1360
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1361
1362
1363
1364
1365
#endif

    return retval;
}

1366
#ifdef ENABLE_UTF8
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
/* 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;
}

1385
/* Translate a Unicode sequence: turn a six-digit hexadecimal number
1386
 * (from 000000 to 10FFFF, case-insensitive) into its corresponding
1387
 * multibyte value. */
1388
long get_unicode_kbinput(int kbinput)
1389
{
1390
1391
1392
    static int uni_digits = 0;
    static long uni = 0;
    long retval = ERR;
1393

1394
    /* Increment the Unicode digit counter. */
1395
    uni_digits++;
1396

1397
    switch (uni_digits) {
1398
	case 1:
1399
1400
	    /* First digit: This must be zero or one.  Put it in the
	     * 0x100000's position of the Unicode sequence holder. */
1401
	    if ('0' <= kbinput && kbinput <= '1')
1402
		uni = (kbinput - '0') * 0x100000;
1403
	    else
1404
1405
		/* This isn't the first digit of a Unicode sequence.
		 * Return this character as the result. */
1406
1407
1408
		retval = kbinput;
	    break;
	case 2:
1409
1410
1411
1412
1413
1414
	    /* 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);
1415
	    else
1416
1417
		/* This isn't the second digit of a Unicode sequence.
		 * Return this character as the result. */
1418
1419
1420
		retval = kbinput;
	    break;
	case 3:
1421
1422
1423
1424
	    /* 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);
1425
	    break;
1426
	case 4:
1427
1428
1429
1430
	    /* 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);
1431
	    break;
1432
	case 5:
1433
1434
1435
	    /* 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);
1436
	    break;
1437
	case 6:
1438
1439
1440
1441
1442
1443
	    /* 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)
1444
		retval = uni;
1445
1446
	    break;
	default:
1447
1448
1449
	    /* If there are more than six digits, return this character
	     * as the result.  (Maybe we should produce an error
	     * instead?) */
1450
1451
1452
	    retval = kbinput;
	    break;
    }
1453

1454
1455
    /* If we have a result, reset the Unicode digit counter and the
     * Unicode sequence holder. */
1456
    if (retval != ERR) {
1457
1458
	uni_digits = 0;
	uni = 0;
1459
    }
1460

1461
#ifdef DEBUG
1462
    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n", kbinput, uni_digits, uni, retval);
1463
1464
#endif

1465
1466
    return retval;
}
1467
#endif /* ENABLE_UTF8 */
1468

1469
1470
1471
1472
1473
1474
/* Translate a control character sequence: turn an ASCII non-control
 * character into its corresponding control character. */
int get_control_kbinput(int kbinput)
{
    int retval;

1475
     /* Ctrl-Space (Ctrl-2, Ctrl-@, Ctrl-`) */
1476
1477
    if (kbinput == ' ' || kbinput == '2')
	retval = NANO_CONTROL_SPACE;
1478
1479
    /* Ctrl-/ (Ctrl-7, Ctrl-_) */
    else if (kbinput == '/')
1480
	retval = NANO_CONTROL_7;
1481
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-/, Ctrl-_) */
1482
1483
1484
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
1485
1486
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
1487
1488
    /* Ctrl-@ (Ctrl-Space, Ctrl-2, Ctrl-`) to Ctrl-_ (Ctrl-/, Ctrl-7) */
    else if ('@' <= kbinput && kbinput <= '_')
1489
	retval = kbinput - '@';
1490
1491
    /* Ctrl-` (Ctrl-2, Ctrl-Space, Ctrl-@) to Ctrl-~ (Ctrl-6, Ctrl-^) */
    else if ('`' <= kbinput && kbinput <= '~')
1492
	retval = kbinput - '`';
1493
1494
1495
    else
	retval = kbinput;

1496
#ifdef DEBUG
1497
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1498
1499
#endif

1500
1501
    return retval;
}
1502

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1503
1504
/* Put the output-formatted characters in output back into the keystroke
 * buffer, so that they can be parsed and displayed as output again. */
1505
void unparse_kbinput(char *output, size_t output_len)
1506
{
1507
1508
    int *input;
    size_t i;
1509

1510
1511
1512
1513
    if (output_len == 0)
	return;

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

1515
1516
    for (i = 0; i < output_len; i++)
	input[i] = (int)output[i];
1517

1518
    unget_input(input, output_len);
1519

1520
    free(input);
1521
1522
}

1523
/* Read in a stream of characters verbatim, and return the length of the
1524
1525
1526
1527
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1528

1529
    /* Turn off flow control characters if necessary so that we can type
1530
1531
     * them in verbatim, and turn the keypad off if necessary so that we
     * don't get extended keypad values. */
1532
1533
    if (ISSET(PRESERVE))
	disable_flow_control();
1534
1535
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, FALSE);
1536
1537
1538

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

    /* Turn flow control characters back on if necessary and turn the
1541
     * keypad back on if necessary now that we're done. */
1542
1543
    if (ISSET(PRESERVE))
	enable_flow_control();
1544
1545
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, TRUE);
1546

1547
    return retval;
1548
1549
}

1550
1551
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
1552
 * the input into the corresponding multibyte value if possible.  After
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1553
 * that, leave the input as-is. */
1554
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1555
{
1556
    int *kbinput, *retval;
1557

1558
    /* Read in the first keystroke. */
1559
1560
    while ((kbinput = get_input(win, 1)) == NULL)
	;
1561

1562
#ifdef ENABLE_UTF8
1563
1564
1565
    if (using_utf8()) {
	/* Check whether the first keystroke is a valid hexadecimal
	 * digit. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1566
	long uni = get_unicode_kbinput(*kbinput);
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585

	/* 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) {
1586
1587
		while ((kbinput = get_input(win, 1)) == NULL)
		    ;
1588
1589
		uni = get_unicode_kbinput(*kbinput);
	    }
1590

1591
1592
1593
	    /* Put back the multibyte equivalent of the Unicode
	     * value. */
	    uni_mb = make_mbchar(uni, &uni_mb_len);
1594

1595
	    seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1596

1597
1598
	    for (i = 0; i < uni_mb_len; i++)
		seq[i] = (unsigned char)uni_mb[i];
1599

1600
	    unget_input(seq, uni_mb_len);
1601

1602
1603
	    free(seq);
	    free(uni_mb);
1604
	}
1605
    } else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1606
1607
#endif /* ENABLE_UTF8 */

1608
1609
	/* Put back the first keystroke. */
	unget_input(kbinput, 1);
1610

1611
1612
    free(kbinput);

1613
    /* Get the complete sequence, and save the characters in it as the
1614
     * result. */
1615
    *kbinput_len = get_key_buffer_len();
1616
    retval = get_input(NULL, *kbinput_len);
1617
1618
1619
1620

    return retval;
}

1621
#ifndef DISABLE_MOUSE
1622
/* Handle any mouse event that may have occurred.  We currently handle
1623
1624
1625
1626
1627
1628
1629
 * 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
1630
1631
1632
1633
1634
1635
 * 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. */
1636
int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1637
1638
{
    MEVENT mevent;
1639
    bool in_bottomwin;
1640
    subnfunc *f;
1641
1642
1643
1644
1645
1646

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

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

1649
1650
1651
    /* Save the screen coordinates where the mouse event took place. */
    *mouse_x = mevent.x;
    *mouse_y = mevent.y;
1652

1653
1654
    in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1655
    /* Handle releases/clicks of the first mouse button. */
1656
    if (mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) {
1657
1658
	/* If we're allowing shortcuts, the current shortcut list is
	 * being displayed on the last two lines of the screen, and the
1659
1660
1661
	 * 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. */
1662
	if (allow_shortcuts && !ISSET(NO_HELP) && in_bottomwin) {
1663
1664
1665
1666
	    int i;
		/* The width of all the shortcuts, except for the last
		 * two, in the shortcut list in bottomwin. */
	    int j;
1667
		/* The calculated index number of the clicked item. */
1668
1669
1670
1671
	    size_t currslen;
		/* The number of shortcuts in the current shortcut
		 * list. */

1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
	    /* 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;
	    }
1686

1687
	    /* Get the shortcut lists' length. */
1688
	    if (currmenu == MMAIN)
1689
		currslen = MAIN_VISIBLE;
1690
	    else {
1691
		currslen = length_of_list(currmenu);
1692

1693
1694
1695
1696
1697
		/* We don't show any more shortcuts than the main list
		 * does. */
		if (currslen > MAIN_VISIBLE)
		    currslen = MAIN_VISIBLE;
	    }
1698

1699
1700
1701
1702
1703
1704
1705
1706
	    /* 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));

1707
1708
	    /* Calculate the one-based index in the shortcut list. */
	    j = (*mouse_x / i) * 2 + *mouse_y;
1709

1710
1711
	    /* Adjust the index if we hit the last two wider ones. */
	    if ((j > currslen) && (*mouse_x % i < COLS % i))
1712
		j -= 2;
1713
1714
1715
#ifdef DEBUG
	    fprintf(stderr, "Calculated %i as index in shortcut list, currmenu = %x.\n", j, currmenu);
#endif
1716
1717
	    /* Ignore releases/clicks of the first mouse button beyond
	     * the last shortcut. */
1718
	    if (j > currslen)
1719
		return 2;
1720

1721
1722
	    /* Go through the list of functions to determine which
	     * shortcut in the current menu we released/clicked on. */
1723
	    for (f = allfuncs; f != NULL; f = f->next) {
1724
		if ((f->menus & currmenu) == 0)
1725
		    continue;
1726
#ifndef DISABLE_HELP
1727
1728
		if (!f->help || strlen(f->help) == 0)
		    continue;
1729
#endif
1730
		if (first_sc_for(currmenu, f->scfunc) == NULL)
1731
		    continue;
1732
1733
		/* Tick off an actually shown shortcut. */
		j -= 1;
1734
1735
		if (j == 0)
		    break;
1736
	    }
1737
#ifdef DEBUG
1738
	    fprintf(stderr, "Stopped on func %ld present in menus %x\n", (long)f->scfunc, f->menus);
1739
#endif
1740

1741
	    /* And put the corresponding key into the keyboard buffer. */
1742
	    if (f != NULL) {
1743
		const sc *s = first_sc_for(currmenu, f->scfunc);
1744
		unget_kbinput(s->seq, s->type == META, s->type == FKEY);
1745
	    }
1746
	    return 1;
1747
	} else
1748
1749
	    /* Handle releases/clicks of the first mouse button that
	     * aren't on the current shortcut list elsewhere. */
1750
	    return 0;
1751
    }
1752
1753
#if NCURSES_MOUSE_VERSION >= 2
    /* Handle presses of the fourth mouse button (upward rolls of the
1754
1755
1756
     * 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
1757
	bool in_edit = wenclose(edit, *mouse_y, *mouse_x);
1758

1759
1760
1761
1762
	if (in_bottomwin)
	    /* Translate the mouse event coordinates so that they're
	     * relative to bottomwin. */
	    wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);
1763

1764
	if (in_edit || (in_bottomwin && *mouse_y == 0)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1765
1766
	    int i;

1767
1768
1769
1770
1771
	    /* 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) ?
1772
			sc_seq_or(do_up_void, 0) : sc_seq_or(do_down_void, 0),
1773
			FALSE, FALSE);
1774
1775
1776
1777
1778
1779
1780

	    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;
1781
1782
    }
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1783
1784
1785

    /* Ignore all other mouse events. */
    return 2;
1786
}
1787
1788
#endif /* !DISABLE_MOUSE */

1789
1790
1791
1792
/* 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. */
1793
const sc *get_shortcut(int *kbinput)
1794
{
1795
    sc *s;
1796

1797
#ifdef DEBUG
1798
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %s -- ", *kbinput, meta_key ? "TRUE" : "FALSE");
1799
1800
#endif

1801
    for (s = sclist; s != NULL; s = s->next) {
1802
	if ((currmenu & s->menu) && *kbinput == s->seq
1803
		&& meta_key == (s->type == META)) {
1804
#ifdef DEBUG
1805
	    fprintf (stderr, "matched seq \"%s\", and btw meta was %d (menu is %x from %x)\n",
1806
			     s->keystr, meta_key, currmenu, s->menu);
1807
#endif
1808
	    return s;
1809
1810
	}
    }
1811
#ifdef DEBUG
1812
    fprintf (stderr, "matched nothing, btw meta was %d\n", meta_key);
1813
#endif
1814
1815
1816
1817

    return NULL;
}

1818
1819
1820
1821
1822
/* 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
1823

1824
1825
1826
1827
    for (; n > 0; n--)
	waddch(win, ' ');
}

1828
/* Blank the first line of the top portion of the window. */
1829
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1830
{
1831
    blank_line(topwin, 0, 0, COLS);
1832
1833
}

1834
1835
/* If the MORE_SPACE flag isn't set, blank the second line of the top
 * portion of the window. */
1836
1837
1838
void blank_topbar(void)
{
    if (!ISSET(MORE_SPACE))
1839
	blank_line(topwin, 1, 0, COLS);
1840
1841
}

1842
/* Blank all the lines of the middle portion of the window, i.e. the
1843
 * edit window. */
Chris Allegretta's avatar
Chris Allegretta committed
1844
1845
void blank_edit(void)
{
1846
    int i;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1847

1848
    for (i = 0; i < editwinrows; i++)
1849
	blank_line(edit, i, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1850
1851
}

1852
/* Blank the first line of the bottom portion of the window. */
Chris Allegretta's avatar
Chris Allegretta committed
1853
1854
void blank_statusbar(void)
{
1855
    blank_line(bottomwin, 0, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1856
1857
}

1858
1859
/* If the NO_HELP flag isn't set, blank the last two lines of the bottom
 * portion of the window. */
1860
1861
1862
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
1863
1864
	blank_line(bottomwin, 1, 0, COLS);
	blank_line(bottomwin, 2, 0, COLS);
1865
1866
1867
    }
}

1868
1869
1870
/* 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. */
1871
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1872
{
1873
    if (statusblank > 0) {
1874
	statusblank--;
1875

1876
1877
1878
1879
1880
1881
	if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
	    blank_statusbar();
	    wnoutrefresh(bottomwin);
	    reset_cursor();
	    wnoutrefresh(edit);
	}
Chris Allegretta's avatar
Chris Allegretta committed
1882
1883
1884
    }
}

1885
1886
1887
1888
/* 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
1889
1890
1891
1892
1893
 * 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)
1894
1895
{
    size_t start_index;
1896
	/* Index in buf of the first character shown. */
1897
    size_t column;
1898
	/* Screen column that start_index corresponds to. */
1899
1900
1901
1902
1903
1904
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */
1905
    char *buf_mb;
1906
1907
    int buf_mb_len;

1908
1909
1910
1911
1912
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

1913
1914
1915
    if (len == 0)
	return mallocstrcpy(NULL, "");

1916
1917
    buf_mb = charalloc(mb_cur_max());

1918
1919
    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
1920

1921
    assert(column <= start_col);
1922

1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
    /* 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);
1938

1939
1940
    index = 0;

1941
1942
    if (buf[start_index] != '\0' && buf[start_index] != '\t' &&
	(column < start_col || (dollars && column > 0))) {
1943
1944
	/* We don't display all of buf[start_index] since it starts to
	 * the left of the screen. */
1945
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1946

1947
	if (is_cntrl_mbchar(buf_mb)) {
1948
	    if (column < start_col) {
1949
1950
		char *ctrl_buf_mb = charalloc(mb_cur_max());
		int ctrl_buf_mb_len, i;
1951

1952
1953
		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
			&ctrl_buf_mb_len);
1954

1955
1956
		for (i = 0; i < ctrl_buf_mb_len; i++)
		    converted[index++] = ctrl_buf_mb[i];
1957

1958
		start_col += mbwidth(ctrl_buf_mb);
1959

1960
		free(ctrl_buf_mb);
1961

1962
		start_index += buf_mb_len;
1963
	    }
1964
	}
1965
#ifdef ENABLE_UTF8
1966
1967
1968
1969
1970
1971
	else if (using_utf8() && mbwidth(buf_mb) == 2) {
	    if (column >= start_col) {
		converted[index++] = ' ';
		start_col++;
	    }

1972
	    converted[index++] = ' ';
1973
	    start_col++;
1974
1975

	    start_index += buf_mb_len;
1976
	}
1977
#endif
1978
1979
    }

1980
    while (buf[start_index] != '\0') {
1981
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1982

1983
1984
1985
1986
1987
1988
1989
1990
	/* 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);
	}

1991
	/* If buf contains a tab character, interpret it. */
1992
	if (*buf_mb == '\t') {
1993
#if !defined(NANO_TINY) && !defined(DISABLE_NANORC)
1994
1995
1996
1997
1998
1999
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = 0; i < whitespace_len[0]; i++)
		    converted[index++] = whitespace[i];
	    } else
2000
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2001
		converted[index++] = ' ';
2002
	    start_col++;
2003
	    while (start_col % tabsize != 0) {
2004
		converted[index++] = ' ';
2005
2006
		start_col++;
	    }
2007
	/* If buf contains a control character, interpret it.  If buf
2008
	 * contains an invalid multibyte control character, display it
Benno Schulenberg's avatar
Benno Schulenberg committed
2009
	 * as such. */
2010
	} else if (is_cntrl_mbchar(buf_mb)) {
2011
2012
	    char *ctrl_buf_mb = charalloc(mb_cur_max());
	    int ctrl_buf_mb_len, i;
2013

2014
	    converted[index++] = '^';
2015
2016
	    start_col++;

2017
2018
	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
		&ctrl_buf_mb_len);
2019

2020
2021
	    for (i = 0; i < ctrl_buf_mb_len; i++)
		converted[index++] = ctrl_buf_mb[i];
2022

2023
	    start_col += mbwidth(ctrl_buf_mb);
2024

2025
	    free(ctrl_buf_mb);
2026
	/* If buf contains a space character, interpret it. */
2027
	} else if (*buf_mb == ' ') {
2028
#if !defined(NANO_TINY) && !defined(DISABLE_NANORC)
2029
2030
2031
2032
2033
2034
2035
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = whitespace_len[0]; i < whitespace_len[0] +
			whitespace_len[1]; i++)
		    converted[index++] = whitespace[i];
	    } else
2036
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2037
		converted[index++] = ' ';
2038
	    start_col++;
2039
2040
2041
	/* If buf contains a non-control character, interpret it.  If
	 * buf contains an invalid multibyte non-control character,
	 * display it as such. */
2042
	} else {
2043
2044
	    char *nctrl_buf_mb = charalloc(mb_cur_max());
	    int nctrl_buf_mb_len, i;
2045

2046
2047
	    nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
		&nctrl_buf_mb_len);
2048

2049
2050
2051
2052
2053
2054
	    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);
2055
2056
	}

2057
	start_index += buf_mb_len;
2058
2059
    }

2060
2061
    free(buf_mb);

2062
2063
    assert(alloc_len >= index + 1);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2064
    /* Null-terminate converted. */
2065
    converted[index] = '\0';
2066
2067
2068

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

2071
    return converted;
2072
2073
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2074
2075
2076
2077
2078
2079
/* 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. */
2080
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2081
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2082
    int space = COLS;
2083
	/* The space we have available for display. */
2084
    size_t verlen = strlenpt(PACKAGE_STRING) + 1;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2085
2086
	/* The length of the version message in columns, plus one for
	 * padding. */
2087
    const char *prefix;
2088
	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
2089
    size_t prefixlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2090
	/* The length of the prefix in columns, plus one for padding. */
2091
    const char *state;
2092
2093
	/* "Modified", "View", or "".  Shows the state of this
	 * buffer. */
2094
    size_t statelen = 0;
2095
	/* The length of the state in columns, or the length of
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2096
2097
	 * "Modified" if the state is blank and we're not in the file
	 * browser. */
2098
    char *exppath = NULL;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2099
	/* The filename, expanded for display. */
2100
    bool newfie = FALSE;
2101
	/* Do we say "New Buffer"? */
2102
    bool dots = FALSE;
2103
2104
	/* Do we put an ellipsis before the path? */

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

2107
2108
2109
    if (interface_color_pair[TITLE_BAR].bright)
	wattron(topwin, A_BOLD);
    wattron(topwin, interface_color_pair[TITLE_BAR].pairnum);
2110

2111
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2112

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2113
2114
2115
2116
    /* 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)
2117
2118
	space = 0;
    else {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2119
2120
2121
2122
	/* 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;
2123
    }
Chris Allegretta's avatar
Chris Allegretta committed
2124

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2125
    if (space >= 4) {
2126
2127
	/* Add a space after the version message, and account for both
	 * it and the two spaces before it. */
2128
2129
	mvwaddnstr(topwin, 0, 2, PACKAGE_STRING,
		actual_x(PACKAGE_STRING, verlen));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2130
2131
2132
2133
	verlen += 3;

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

2136
2137
2138
2139
2140
2141
2142
2143
2144
#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
2145
    statelen = strlenpt((*state == '\0' && path == NULL) ?
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2146
	_("Modified") : state);
2147

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2148
2149
    /* If possible, add a space before state. */
    if (space > 0 && statelen < space)
2150
	statelen++;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2151
    else
2152
2153
2154
	goto the_end;

#ifndef DISABLE_BROWSER
2155
    /* path should be a directory if we're in the file browser. */
2156
2157
2158
2159
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
2160
    if (openfile->filename[0] == '\0') {
2161
	prefix = _("New Buffer");
2162
	newfie = TRUE;
2163
2164
    } else
	prefix = _("File:");
2165

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2168
    /* If newfie is FALSE, add a space after prefix. */
2169
    if (!newfie && prefixlen + statelen < space)
2170
2171
	prefixlen++;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2172
    /* If we're not in the file browser, set path to the current
2173
     * filename. */
2174
    if (path == NULL)
2175
	path = openfile->filename;
2176

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2177
    /* Account for the full lengths of the prefix and the state. */
2178
2179
2180
2181
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2182
	/* space is now the room we have for the filename. */
2183

2184
    if (!newfie) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2185
	size_t lenpt = strlenpt(path), start_col;
2186

2187
2188
2189
	/* 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
2190
	dots = (space >= 8 && lenpt >= space);
2191
2192
2193
2194
2195
2196
2197
2198

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

	exppath = display_string(path, start_col, space, FALSE);
2199
2200
    }

2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
    /* 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 {
2214
2215
2216
	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
	    /* The length of the expanded filename. */

2217
	/* There is room for the whole filename, so we center it. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2218
2219
	mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3),
		prefix, actual_x(prefix, prefixlen));
2220
	if (!newfie) {
2221
2222
2223
2224
2225
2226
2227
2228
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    }

  the_end:
    free(exppath);

2229
    if (state[0] != '\0') {
2230
	if (statelen >= COLS - 1)
2231
2232
	    mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
	else {
2233
	    assert(COLS - statelen - 1 >= 0);
2234

2235
	    mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2236
		actual_x(state, statelen));
2237
	}
2238
    }
2239

2240
2241
    wattroff(topwin, A_BOLD);
    wattroff(topwin, interface_color_pair[TITLE_BAR].pairnum);
2242

2243
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2244
    reset_cursor();
2245
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2246
2247
}

2248
2249
/* Mark the current file as modified if it isn't already, and then
 * update the titlebar to display the file's new status. */
2250
2251
void set_modified(void)
{
2252
2253
    if (!openfile->modified) {
	openfile->modified = TRUE;
2254
	titlebar(NULL);
2255
#ifndef NANO_TINY
2256
	if (ISSET(LOCKING)) {
2257
	    if (openfile->filename[0] == '\0')
2258
2259
		return;
	    else if (openfile->lock_filename == NULL) {
2260
2261
		/* TRANSLATORS: Try to keep this at most 76 characters. */
		statusbar(_("Warning: Modifying a file which is not locked, check directory permission?"));
2262
2263
	    } else {
		write_lockfile(openfile->lock_filename,
2264
2265
				get_full_path(openfile->filename), TRUE);
	    }
2266
	}
2267
#endif
2268
2269
2270
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2271
2272
2273
/* 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. */
2274
2275
2276
void statusbar(const char *msg, ...)
{
    va_list ap;
2277
    char *bar, *foo;
Benno Schulenberg's avatar
Benno Schulenberg committed
2278
    size_t start_x;
2279
#if !defined(NANO_TINY) && !defined(DISABLE_NANORC)
2280
2281
    bool old_whitespace;
#endif
2282
2283
2284
2285
2286

    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(). */
2287
    if (isendwin()) {
2288
2289
2290
2291
2292
2293
2294
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

    blank_statusbar();

2295
#if !defined(NANO_TINY) && !defined(DISABLE_NANORC)
2296
2297
    old_whitespace = ISSET(WHITESPACE_DISPLAY);
    UNSET(WHITESPACE_DISPLAY);
2298
#endif
2299
2300
2301
2302
    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
2303
    free(bar);
2304
#if !defined(NANO_TINY) && !defined(DISABLE_NANORC)
2305
2306
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2307
#endif
Benno Schulenberg's avatar
Benno Schulenberg committed
2308
    start_x = (COLS - strlenpt(foo) - 4) / 2;
2309

2310
    wmove(bottomwin, 0, start_x);
2311
2312
2313
    if (interface_color_pair[STATUS_BAR].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2314
2315
2316
2317
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
2318
2319
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[STATUS_BAR].pairnum);
2320
2321
2322
2323
2324
    wnoutrefresh(bottomwin);
    reset_cursor();
    wnoutrefresh(edit);
	/* Leave the cursor at its position in the edit window, not in
	 * the statusbar. */
2325

2326
    disable_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2327
2328
2329

    /* If we're doing quick statusbar blanking, and constant cursor
     * position display is off, blank the statusbar after only one
2330
2331
     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
     * Pico does. */
2332
    statusblank =
2333
#ifndef NANO_TINY
2334
	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2335
#endif
2336
	26;
2337
2338
}

2339
2340
/* Display the shortcut list in s on the last two rows of the bottom
 * portion of the window. */
2341
void bottombars(int menu)
Chris Allegretta's avatar
Chris Allegretta committed
2342
{
2343
    size_t i, colwidth, slen;
2344
2345
    subnfunc *f;
    const sc *s;
2346

Chris Allegretta's avatar
Chris Allegretta committed
2347
2348
2349
    if (ISSET(NO_HELP))
	return;

2350
    if (menu == MMAIN) {
2351
	slen = MAIN_VISIBLE;
2352

2353
	assert(slen <= length_of_list(menu));
2354
    } else {
2355
	slen = length_of_list(menu);
2356

2357
	/* Don't show any more shortcuts than the main list does. */
2358
2359
2360
2361
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2362
2363
2364
2365
    /* 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. */
2366
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2367

2368
    blank_bottombars();
2369

2370
2371
2372
#ifdef DEBUG
    fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
#endif
2373

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

2376
#ifdef DEBUG
2377
	fprintf(stderr, "Checking menu items....");
2378
#endif
2379
	if ((f->menus & menu) == 0)
2380
	    continue;
2381

2382
#ifdef DEBUG
2383
	fprintf(stderr, "found one! f->menus = %x, desc = \"%s\"\n", f->menus, f->desc);
2384
#endif
2385
2386
	s = first_sc_for(menu, f->scfunc);
	if (s == NULL) {
2387
2388
2389
#ifdef DEBUG
	    fprintf(stderr, "Whoops, guess not, no shortcut key found for func!\n");
#endif
2390
2391
	    continue;
	}
2392
	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2393
#ifdef DEBUG
2394
	fprintf(stderr, "Calling onekey with keystr \"%s\" and desc \"%s\"\n", s->keystr, f->desc);
2395
#endif
2396
	onekey(s->keystr, _(f->desc), colwidth + (COLS % colwidth));
2397
	i++;
Chris Allegretta's avatar
Chris Allegretta committed
2398
    }
2399

2400
2401
    wnoutrefresh(bottomwin);
    reset_cursor();
2402
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2403
2404
}

2405
2406
2407
2408
2409
2410
/* 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
2411
{
2412
2413
    size_t keystroke_len = strlenpt(keystroke) + 1;

2414
2415
    assert(keystroke != NULL && desc != NULL);

2416
2417
2418
    if (interface_color_pair[KEY_COMBO].bright)
	wattron(bottomwin, A_BOLD);
    wattron(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2419
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2420
2421
    wattroff(bottomwin, A_BOLD);
    wattroff(bottomwin, interface_color_pair[KEY_COMBO].pairnum);
2422
2423
2424
2425
2426
2427

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

2428
2429
    if (len > 0) {
	waddch(bottomwin, ' ');
2430
2431
2432
	if (interface_color_pair[FUNCTION_TAG].bright)
	    wattron(bottomwin, A_BOLD);
	wattron(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
2433
	waddnstr(bottomwin, desc, actual_x(desc, len));
2434
2435
	wattroff(bottomwin, A_BOLD);
	wattroff(bottomwin, interface_color_pair[FUNCTION_TAG].pairnum);
Chris Allegretta's avatar
Chris Allegretta committed
2436
2437
2438
    }
}

2439
2440
/* 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
2441
2442
void reset_cursor(void)
{
2443
    size_t xpt;
2444
2445
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
2446
    if (openfile == NULL) {
2447
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2448
	return;
2449
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2450

2451
    xpt = xplustabs();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2452

2453
#ifndef NANO_TINY
2454
2455
    if (ISSET(SOFTWRAP)) {
	filestruct *tmp;
2456
2457
	openfile->current_y = 0;

2458
	for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next)
2459
	    openfile->current_y += (strlenpt(tmp->data) / COLS) + 1;
2460

2461
	openfile->current_y += xplustabs() / COLS;
2462
	if (openfile->current_y < editwinrows)
2463
	    wmove(edit, openfile->current_y, xpt % COLS);
2464
2465
2466
    } else
#endif
    {
2467
2468
2469
2470
2471
2472
	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
2473
}
Chris Allegretta's avatar
Chris Allegretta committed
2474

2475
2476
2477
2478
2479
2480
2481
2482
/* 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. */
2483
void edit_draw(filestruct *fileptr, const char *converted, int
2484
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2485
{
2486
#if !defined(NANO_TINY) || !defined(DISABLE_COLOR)
2487
2488
2489
2490
2491
2492
2493
2494
2495
    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. */
2496
2497
#endif

2498
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2499
    assert(strlenpt(converted) <= COLS);
2500

2501
2502
2503
    /* 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);
2504

2505
#ifndef USE_SLANG
2506
2507
2508
    /* 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. */
2509
    wredrawln(edit, line, 1);
2510
#endif
2511

2512
#ifndef DISABLE_COLOR
2513
2514
2515
2516
    /* 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;
2517

2518
2519
2520
	/* Set up multi-line color data for this line if it's not yet
	 * calculated. */
	if (fileptr->multidata == NULL && openfile->syntax
2521
		&& openfile->syntax->nmultis > 0) {
2522
	    int i;
2523
2524
2525
2526
	    fileptr->multidata = (short *)nmalloc(openfile->syntax->nmultis * sizeof(short));
	    for (i = 0; i < openfile->syntax->nmultis; i++)
		/* Assume this applies until we know otherwise. */
		fileptr->multidata[i] = -1;
2527
	}
2528
2529
2530
2531
	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2532
2533
		/* Number of chars to paint on this line.  There are
		 * COLS characters on a whole line. */
2534
	    size_t index;
2535
		/* Index in converted where we paint. */
2536
2537
2538
2539
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2540
2541
2542
2543

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2544
2545
2546
	    /* 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. */
2547

2548
	    /* First case, tmpcolor is a single-line expression. */
2549
	    if (tmpcolor->end == NULL) {
2550
2551
2552
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2553
		 * last match.  Even though two matches may overlap, we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2554
2555
		 * want to ignore them, so that we can highlight e.g. C
		 * strings correctly. */
2556
2557
2558
		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
2559
2560
2561
		     * unless k is zero.  If regexec() returns
		     * REG_NOMATCH, there are no more matches in the
		     * line. */
2562
		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2563
2564
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2565
			break;
2566
2567
		    /* Translate the match to the beginning of the
		     * line. */
2568
2569
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2570
2571
2572

		    /* Skip over a zero-length regex match. */
		    if (startmatch.rm_so == startmatch.rm_eo)
2573
			startmatch.rm_eo++;
2574
		    else if (startmatch.rm_so < endpos &&
2575
			startmatch.rm_eo > startpos) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2576
2577
			x_start = (startmatch.rm_so <= startpos) ? 0 :
				strnlenpt(fileptr->data,
2578
				startmatch.rm_so) - start;
2579

2580
2581
2582
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2583
2584
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2585
2586
2587

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

2588
			mvwaddnstr(edit, line, x_start, converted +
2589
				index, paintlen);
2590
		    }
2591
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2592
		}
2593
	    } else if (fileptr->multidata != NULL && fileptr->multidata[tmpcolor->id] != CNONE) {
2594
		/* This is a multi-line regex.  There are two steps.
2595
2596
2597
2598
2599
2600
		 * First, we have to see if the beginning of the line is
		 * colored by a start on an earlier line, and an end on
		 * this line or later.
		 *
		 * We find the first line before fileptr matching the
		 * start.  If every match on that line is followed by an
2601
2602
2603
2604
		 * end, then go to step two.  Otherwise, find the next
		 * line after start_line matching the end.  If that line
		 * is not before fileptr, then paint the beginning of
		 * this line. */
2605
		const filestruct *start_line = fileptr->prev;
2606
		    /* The first line before fileptr matching start. */
2607
		regoff_t start_col;
2608
		    /* Where it starts in that line. */
2609
		const filestruct *end_line;
2610
2611
2612
		short md = fileptr->multidata[tmpcolor->id];

		if (md == -1)
2613
2614
		    /* Assume this until we know otherwise. */
		    fileptr->multidata[tmpcolor->id] = CNONE;
2615
		else if (md == CNONE)
2616
		    goto end_of_loop;
2617
2618
		else if (md == CWHOLELINE) {
		    mvwaddnstr(edit, line, 0, converted, -1);
2619
		    goto end_of_loop;
2620
2621
2622
2623
2624
		} else if (md == CBEGINBEFORE) {
		    regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0);
		    paintlen = actual_x(converted, strnlenpt(fileptr->data,
			endmatch.rm_eo) - start);
		    mvwaddnstr(edit, line, 0, converted, paintlen);
2625
		    goto end_of_loop;
2626
		}
2627

2628
		while (start_line != NULL && regexec(tmpcolor->start,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2629
2630
			start_line->data, 1, &startmatch, 0) ==
			REG_NOMATCH) {
2631
2632
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
2633
2634
		    if (regexec(tmpcolor->end, start_line->data, 0,
			NULL, 0) == 0)
2635
2636
2637
			goto step_two;
		    start_line = start_line->prev;
		}
2638
2639
2640

		/* Skip over a zero-length regex match. */
		if (startmatch.rm_so == startmatch.rm_eo)
2641
		    startmatch.rm_eo++;
2642
		else {
2643
2644
		    /* No start found, so skip to the next step. */
		    if (start_line == NULL)
2645
			goto step_two;
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
		    /* 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
2675
			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2676
			end_line = end_line->next;
2677

2678
2679
		    /* No end found, or it is too early. */
		    if (end_line == NULL || (end_line == fileptr &&
2680
			endmatch.rm_eo <= startpos))
2681
			goto step_two;
2682

2683
2684
2685
2686
2687
2688
2689
		    /* 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. */
2690
		    if (end_line != fileptr) {
2691
			paintlen = -1;
2692
2693
			fileptr->multidata[tmpcolor->id] = CWHOLELINE;
		    } else {
2694
2695
2696
			paintlen = actual_x(converted,
				strnlenpt(fileptr->data,
				endmatch.rm_eo) - start);
2697
2698
			fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
		    }
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
		    mvwaddnstr(edit, line, 0, converted, paintlen);
  step_two:
		    /* Second step, we look for starts on this line. */
		    start_col = 0;

		    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
2712
			 * beginning of the line. */
2713
2714
2715
2716
			startmatch.rm_so += start_col;
			startmatch.rm_eo += start_col;

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

2720
			index = actual_x(converted, x_start);
2721

2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
			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);
2739

2740
2741
2742
2743
				assert(0 <= x_start && x_start < COLS);

				mvwaddnstr(edit, line, x_start,
					converted + index, paintlen);
2744
				if (paintlen > 0)
2745
				    fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
2746

2747
2748
2749
2750
2751
2752
2753
2754
			    }
			} 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 &&
2755
2756
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
2757
				end_line = end_line->next;
2758

2759
2760
			    if (end_line != NULL) {
				assert(0 <= x_start && x_start < COLS);
2761

2762
2763
2764
2765
2766
				mvwaddnstr(edit, line, x_start,
					converted + index, -1);
				/* We painted to the end of the line, so
				 * don't bother checking any more
				 * starts. */
2767
				fileptr->multidata[tmpcolor->id] = CENDAFTER;
2768
2769
				break;
			    }
2770
			}
2771
			start_col = startmatch.rm_so + 1;
2772
		    }
2773
2774
		}
	    }
2775
  end_of_loop:
2776
2777
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2778
	}
2779
    }
2780
#endif /* !DISABLE_COLOR */
2781

2782
#ifndef NANO_TINY
2783
    /* If the mark is on, we need to display it. */
2784
    if (openfile->mark_set && (fileptr->lineno <=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2785
	openfile->mark_begin->lineno || fileptr->lineno <=
2786
	openfile->current->lineno) && (fileptr->lineno >=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2787
	openfile->mark_begin->lineno || fileptr->lineno >=
2788
	openfile->current->lineno)) {
2789
	/* fileptr is at least partially selected. */
2790
	const filestruct *top;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2791
	    /* Either current or mark_begin, whichever is first. */
2792
	size_t top_x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2793
	    /* current_x or mark_begin_x, corresponding to top. */
2794
2795
	const filestruct *bot;
	size_t bot_x;
2796
	int x_start;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2797
	    /* Starting column for mvwaddnstr().  Zero-based. */
2798
	int paintlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2799
2800
	    /* Number of characters to paint on this line.  There are
	     * COLS characters on a whole line. */
2801
	size_t index;
2802
	    /* Index in converted where we paint. */
2803

2804
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2805
2806
2807
2808
2809

	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
2810

2811
	/* The selected bit of fileptr is on this page. */
2812
2813
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2814
2815
2816
2817

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

2819
2820
2821
2822
2823
	    /* 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. */
2824
2825
2826
2827
2828
	    if (bot_x >= endpos)
		paintlen = -1;
	    else
		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
			start);
2829
2830
2831
2832
2833
2834
2835
2836

	    /* 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;
	    }
2837
2838
2839

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

2840
	    index = actual_x(converted, x_start);
2841

2842
2843
2844
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2845
	    wattron(edit, hilite_attribute);
2846
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2847
		paintlen);
2848
	    wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
2849
	}
2850
    }
2851
#endif /* !NANO_TINY */
Chris Allegretta's avatar
Chris Allegretta committed
2852
2853
}

2854
/* Just update one line in the edit buffer.  This is basically a wrapper
2855
 * for edit_draw().  The line will be displayed starting with
2856
 * fileptr->data[index].  Likely arguments are current_x or zero.
2857
 * Returns: Number of additional lines consumed (needed for SOFTWRAP). */
2858
int update_line(filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2859
{
2860
    int line = 0;
2861
	/* The line in the edit window that we want to update. */
2862
    int extralinesused = 0;
2863
2864
2865
2866
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2867

2868
    assert(fileptr != NULL);
2869

2870
#ifndef NANO_TINY
2871
    if (ISSET(SOFTWRAP)) {
2872
2873
	filestruct *tmp;

2874
2875
	for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
	    line += (strlenpt(tmp->data) / COLS) + 1;
2876
    } else
2877
#endif
2878
2879
	line = fileptr->lineno - openfile->edittop->lineno;

2880
    if (line < 0 || line >= editwinrows)
Chris Allegretta's avatar
Chris Allegretta committed
2881
	return 1;
2882

2883
    /* First, blank out the line. */
2884
    blank_line(edit, line, 0, COLS);
2885

2886
2887
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2888
#ifndef NANO_TINY
2889
2890
2891
    if (ISSET(SOFTWRAP))
	index = 0;
    else
2892
#endif
2893
	index = strnlenpt(fileptr->data, index);
2894
    page_start = get_page_start(index);
2895

2896
2897
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2898
2899
2900
#ifdef NANO_TINY
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
#else
2901
2902
2903
    converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
#ifdef DEBUG
    if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2904
	fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2905
#endif
2906
#endif /* !NANO_TINY */
2907

2908
    /* Paint the line. */
2909
    edit_draw(fileptr, converted, line, page_start);
2910
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2911

2912
#ifndef NANO_TINY
2913
    if (!ISSET(SOFTWRAP)) {
2914
#endif
2915
2916
2917
2918
	if (page_start > 0)
	    mvwaddch(edit, line, 0, '$');
	if (strlenpt(fileptr->data) > page_start + COLS)
	    mvwaddch(edit, line, COLS - 1, '$');
2919
#ifndef NANO_TINY
2920
    } else {
2921
	size_t full_length = strlenpt(fileptr->data);
2922
	for (index += COLS; index <= full_length && line < editwinrows; index += COLS) {
2923
2924
	    line++;
#ifdef DEBUG
2925
	    fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
2926
#endif
2927
	    blank_line(edit, line, 0, COLS);
2928
2929

	    /* Expand the line, replacing tabs with spaces, and control
2930
	     * characters with their displayed forms. */
2931
2932
2933
2934
2935
	    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
2936
2937
2938

	    /* Paint the line. */
	    edit_draw(fileptr, converted, line, index);
2939
	    free(converted);
2940
2941
2942
	    extralinesused++;
	}
    }
2943
#endif /* !NANO_TINY */
2944
    return extralinesused;
Chris Allegretta's avatar
Chris Allegretta committed
2945
2946
}

2947
/* Return TRUE if we need an update after moving horizontally, and FALSE
2948
 * otherwise.  We need one if the mark is on or if pww_save and
2949
 * placewewant are on different pages. */
2950
bool need_horizontal_update(size_t pww_save)
2951
2952
{
    return
2953
#ifndef NANO_TINY
2954
	openfile->mark_set ||
2955
#endif
2956
	get_page_start(pww_save) !=
2957
	get_page_start(openfile->placewewant);
2958
2959
}

2960
/* Return TRUE if we need an update after moving vertically, and FALSE
2961
 * otherwise.  We need one if the mark is on or if pww_save and
2962
 * placewewant are on different pages. */
2963
bool need_vertical_update(size_t pww_save)
2964
2965
{
    return
2966
#ifndef NANO_TINY
2967
	openfile->mark_set ||
2968
#endif
2969
	get_page_start(pww_save) !=
2970
	get_page_start(openfile->placewewant);
2971
2972
}

2973
/* When edittop changes, try and figure out how many lines
2974
 * we really have to work with (i.e. set maxrows). */
2975
2976
2977
2978
2979
void compute_maxrows(void)
{
    int n;
    filestruct *foo = openfile->edittop;

2980
2981
2982
2983
2984
    if (!ISSET(SOFTWRAP)) {
	maxrows = editwinrows;
	return;
    }

2985
2986
    maxrows = 0;
    for (n = 0; n < editwinrows && foo; n++) {
2987
	maxrows++;
2988
	n += strlenpt(foo->data) / COLS;
2989
2990
2991
	foo = foo->next;
    }

2992
2993
2994
    if (n < editwinrows)
	maxrows += editwinrows - n;

2995
#ifdef DEBUG
2996
    fprintf(stderr, "compute_maxrows(): maxrows = %d\n", maxrows);
2997
2998
2999
#endif
}

3000
3001
/* 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
3002
3003
 * scrolling.  direction is the direction to scroll, either UPWARD or
 * DOWNWARD, and nlines is the number of lines to scroll.  We change
3004
3005
 * edittop, and assume that current and current_x are up to date.  We
 * also assume that scrollok(edit) is FALSE. */
3006
void edit_scroll(scroll_dir direction, ssize_t nlines)
3007
{
3008
    ssize_t i;
3009
    filestruct *foo;
3010
    bool do_redraw = FALSE;
3011

3012
3013
    /* Don't bother scrolling less than one line. */
    if (nlines < 1)
3014
3015
	return;

3016
    if (need_vertical_update(0))
3017
3018
	do_redraw = TRUE;

3019
3020
3021
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

3022
    /* Move the top line of the edit window up or down (depending on the
3023
3024
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
3025
    for (i = nlines; i > 0; i--) {
3026
	if (direction == UPWARD) {
3027
	    if (openfile->edittop == openfile->fileage)
3028
		break;
3029
	    openfile->edittop = openfile->edittop->prev;
3030
	} else {
3031
	    if (openfile->edittop == openfile->filebot)
3032
		break;
3033
	    openfile->edittop = openfile->edittop->next;
3034
	}
3035
3036

#ifndef NANO_TINY
3037
	/* Don't over-scroll on long lines. */
3038
	if (ISSET(SOFTWRAP) && direction == UPWARD) {
3039
	    ssize_t len = strlenpt(openfile->edittop->data) / COLS;
3040
	    i -= len;
3041
3042
3043
	    if (len > 0)
		do_redraw = TRUE;
	}
3044
#endif
3045
3046
    }

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3050
3051
    /* 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
3052
     * call edit_refresh() beforehand if we need to. */
3053
    if (nlines == 0 || do_redraw || nlines >= editwinrows) {
3054
	if (do_redraw || nlines >= editwinrows)
3055
	    edit_refresh_needed = TRUE;
3056
3057
	return;
    }
3058
3059
3060

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
3061
    scrollok(edit, TRUE);
3062
    wscrl(edit, (direction == UPWARD) ? -nlines : nlines);
3063
3064
    scrollok(edit, FALSE);

3065
3066
3067
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

3068
3069
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
3070
3071
    if ((direction == UPWARD && openfile->edittop ==
	openfile->fileage) || (direction == DOWNWARD &&
3072
3073
	openfile->edittop->lineno + editwinrows - 1 >=
	openfile->filebot->lineno))
3074
	nlines = editwinrows;
3075

3076
3077
3078
3079
3080
3081
    /* 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
3082

3083
3084
    if (nlines > editwinrows)
	nlines = editwinrows;
3085
3086
3087

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

3090
3091
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
3092
    if (direction == DOWNWARD) {
3093
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
3094
3095
3096
	    foo = foo->next;
    }

3097
3098
3099
3100
3101
3102
    /* 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--) {
3103
3104
	if ((i == nlines && direction == DOWNWARD) || (i == 1 &&
		direction == UPWARD)) {
3105
3106
3107
3108
3109
	    if (do_redraw)
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
3110
		openfile->current_x : 0);
3111
	foo = foo->next;
3112
    }
3113
    compute_maxrows();
3114
3115
3116
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3117
 * updated.  Use this if we've moved without changing any text. */
3118
void edit_redraw(filestruct *old_current, size_t pww_save)
3119
{
3120
    bool do_redraw = need_vertical_update(0) ||
3121
	need_vertical_update(pww_save);
3122
    filestruct *foo = NULL;
3123

3124
3125
    /* If either old_current or current is offscreen, scroll the edit
     * window until it's onscreen and get out. */
3126
3127
    if (old_current->lineno < openfile->edittop->lineno ||
	old_current->lineno >= openfile->edittop->lineno +
3128
	maxrows || openfile->current->lineno <
3129
	openfile->edittop->lineno || openfile->current->lineno >=
3130
	openfile->edittop->lineno + maxrows) {
3131
#ifdef DEBUG
3132
3133
	fprintf(stderr, "edit_redraw(): line %ld was offscreen, oldcurrent = %ld edittop = %ld",
		(long)openfile->current->lineno, (long)old_current->lineno, (long)openfile->edittop->lineno);
3134
#endif
3135

3136
3137
#ifndef NANO_TINY
	/* If the mark is on, update all the lines between old_current
3138
3139
	 * and either the old first line or old last line (depending on
	 * whether we've scrolled up or down) of the edit window. */
3140
	if (openfile->mark_set) {
3141
	    ssize_t old_lineno;
3142
	    filestruct *old_edittop = openfile->edittop;
3143
3144
3145
3146

	    if (old_edittop->lineno < openfile->edittop->lineno)
		old_lineno = old_edittop->lineno;
	    else
3147
		old_lineno = (old_edittop->lineno + maxrows <=
3148
3149
3150
			openfile->filebot->lineno) ?
			old_edittop->lineno + editwinrows :
			openfile->filebot->lineno;
3151
3152
3153

	    foo = old_current;

3154
	    while (foo->lineno != old_lineno) {
3155
3156
		update_line(foo, 0);

3157
		foo = (foo->lineno > old_lineno) ? foo->prev :
3158
3159
3160
3161
3162
			foo->next;
	    }
	}
#endif /* !NANO_TINY */

3163
3164
3165
	/* Put edittop in range of current, get the difference in lines
	 * between the original edittop and the current edittop, and
	 * then restore the original edittop. */
3166
	edit_update(CENTER);
3167

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3168
3169
	/* Update old_current if we're not on the same page as
	 * before. */
3170
3171
3172
	if (do_redraw)
	    update_line(old_current, 0);

3173
#ifndef NANO_TINY
3174
3175
3176
	/* 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. */
3177
	if (openfile->mark_set) {
3178
	    while (foo->lineno != openfile->current->lineno) {
3179
3180
		update_line(foo, 0);

3181
		foo = (foo->lineno > openfile->current->lineno) ?
3182
3183
3184
3185
3186
			foo->prev : foo->next;
	    }
	}
#endif /* !NANO_TINY */

3187
3188
3189
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3190
3191
3192
    /* 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. */
3193
    foo = old_current;
3194

3195
    while (foo != openfile->current) {
3196
	if (do_redraw)
3197
	    update_line(foo, 0);
3198

3199
#ifndef NANO_TINY
3200
	if (!openfile->mark_set)
3201
3202
#endif
	    break;
3203

3204
#ifndef NANO_TINY
3205
3206
	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
		foo->next;
3207
#endif
3208
    }
3209

3210
    if (do_redraw)
3211
	update_line(openfile->current, openfile->current_x);
3212
3213
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3214
3215
/* 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
3216
3217
void edit_refresh(void)
{
3218
    filestruct *foo;
3219
    int nlines;
3220

3221
    /* Figure out what maxrows should really be. */
3222
    compute_maxrows();
3223

3224
3225
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
3226
3227
	maxrows) {
#ifdef DEBUG
3228
3229
	fprintf(stderr, "edit_refresh(): line = %ld, edittop %ld + maxrows %d\n",
		(long)openfile->current->lineno, (long)openfile->edittop->lineno, maxrows);
3230
3231
#endif

3232
3233
	/* Put the top line of the edit window in range of the current
	 * line. */
3234
	edit_update(CENTER);
3235
    }
Chris Allegretta's avatar
Chris Allegretta committed
3236

3237
3238
    foo = openfile->edittop;

3239
#ifdef DEBUG
3240
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3241
#endif
3242

3243
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3244
	nlines += update_line(foo, (foo == openfile->current) ?
3245
		openfile->current_x : 0);
3246
3247
3248
	foo = foo->next;
    }

3249
    for (; nlines < editwinrows; nlines++)
3250
3251
3252
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
3253
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3254
3255
}

3256
3257
3258
3259
/* 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. */
3260
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
3261
{
3262
    filestruct *foo = openfile->current;
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
    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)
3274
	goal = editwinrows / 2;
3275
3276
    else {
	goal = openfile->current_y;
3277

3278
	/* Limit goal to (editwinrows - 1) lines maximum. */
3279
3280
	if (goal > editwinrows - 1)
	    goal = editwinrows - 1;
Chris Allegretta's avatar
Chris Allegretta committed
3281
    }
3282

3283
    for (; goal > 0 && foo->prev != NULL; goal--) {
3284
	foo = foo->prev;
3285
#ifndef NANO_TINY
3286
3287
	if (ISSET(SOFTWRAP) && foo)
	    goal -= strlenpt(foo->data) / COLS;
3288
#endif
3289
    }
3290
    openfile->edittop = foo;
3291
#ifdef DEBUG
3292
    fprintf(stderr, "edit_update(): setting edittop to lineno %ld\n", (long)openfile->edittop->lineno);
3293
#endif
3294
    compute_maxrows();
3295
    edit_refresh_needed = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
3296
3297
}

3298
/* Unconditionally redraw the entire screen. */
3299
void total_redraw(void)
3300
{
3301
3302
3303
3304
3305
3306
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 4: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
3307
    wrefresh(curscr);
3308
#endif
3309
3310
}

3311
3312
/* Unconditionally redraw the entire screen, and then refresh it using
 * the current file. */
3313
3314
void total_refresh(void)
{
3315
    total_redraw();
3316
    titlebar(NULL);
3317
    edit_refresh();
3318
    bottombars(currmenu);
3319
3320
}

3321
3322
/* Display the main shortcut list on the last two rows of the bottom
 * portion of the window. */
3323
3324
void display_main_list(void)
{
3325
#ifndef DISABLE_COLOR
3326
3327
3328
    if (openfile->syntax
	  && (openfile->syntax->formatter || openfile->syntax->linter))
	set_lint_or_format_shortcuts();
3329
3330
3331
3332
    else
	set_spell_shortcuts();
#endif

3333
    bottombars(MMAIN);
3334
3335
}

3336
3337
3338
3339
3340
3341
/* 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. */
3342
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3343
{
3344
    filestruct *f;
3345
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3346
    size_t i, cur_xpt = xplustabs() + 1;
3347
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3348
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3349

3350
    assert(openfile->fileage != NULL && openfile->current != NULL);
3351

3352
    f = openfile->current->next;
3353
    c = openfile->current->data[openfile->current_x];
3354
3355

    openfile->current->next = NULL;
3356
    openfile->current->data[openfile->current_x] = '\0';
3357
3358
3359

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

3360
    openfile->current->data[openfile->current_x] = c;
3361
    openfile->current->next = f;
3362

3363
3364
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
3365
	return;
3366
    }
Chris Allegretta's avatar
Chris Allegretta committed
3367

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3368
    /* Display the current cursor position on the statusbar, and set
3369
     * disable_cursorpos to FALSE. */
3370
3371
    linepct = 100 * openfile->current->lineno /
	openfile->filebot->lineno;
3372
    colpct = 100 * cur_xpt / cur_lenpt;
3373
3374
    charpct = (openfile->totsize == 0) ? 0 : 100 * i /
	openfile->totsize;
3375
3376

    statusbar(
3377
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3378
	(long)openfile->current->lineno,
3379
	(long)openfile->filebot->lineno, linepct,
3380
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3381
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3382

3383
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3384
3385
}

3386
/* Unconditionally display the current cursor position. */
3387
void do_cursorpos_void(void)
3388
{
3389
    do_cursorpos(FALSE);
3390
3391
}

3392
3393
void enable_nodelay(void)
{
3394
3395
    nodelay_mode = TRUE;
    nodelay(edit, TRUE);
3396
3397
3398
3399
}

void disable_nodelay(void)
{
3400
3401
    nodelay_mode = FALSE;
    nodelay(edit, FALSE);
3402
3403
}

3404
3405
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3406
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3407
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3408
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3409

3410
    y = get_page_start(y) + COLS - y;
3411
	/* Now y is the number of columns that we can display on this
3412
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3413

3414
3415
3416
3417
3418
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3419
    reset_cursor();
3420
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
3421

3422
    if (highlight)
3423
	wattron(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3424

3425
    /* This is so we can show zero-length matches. */
3426
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3427
	waddch(edit, ' ');
3428
    else
3429
	waddnstr(edit, word, actual_x(word, y));
3430
3431
3432

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

3434
    if (highlight)
3435
	wattroff(edit, hilite_attribute);
Chris Allegretta's avatar
Chris Allegretta committed
3436
3437
}

3438
#ifndef DISABLE_EXTRA
Benno Schulenberg's avatar
Benno Schulenberg committed
3439
#define CREDIT_LEN 53
3440
3441
#define XLCREDIT_LEN 8

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3442
3443
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3444
3445
void do_credits(void)
{
3446
3447
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3448
    int kbinput = ERR, crpos = 0, xlpos = 0;
3449
3450
3451
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3452
3453
	VERSION,
	"",
3454
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3455
3456
3457
3458
3459
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3460
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3461
	"David Benbennick",
Benno Schulenberg's avatar
Benno Schulenberg committed
3462
	"Mark Majeres",
3463
	"Mike Frysinger",
3464
	"Benno Schulenberg",
Chris Allegretta's avatar
Chris Allegretta committed
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
	"Ken Tyler",
	"Sven Guckes",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3475
	NULL,				/* "Special thanks to:" */
3476
	"Monique, Brielle & Joseph",
Chris Allegretta's avatar
Chris Allegretta committed
3477
3478
3479
3480
3481
3482
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3483
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3484
	"Linus Torvalds",
3485
	NULL,				/* "For ncurses:" */
3486
3487
3488
3489
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3490
3491
3492
3493
3494
3495
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3496
	"(C) 1999 - 2014",
3497
	"Free Software Foundation, Inc.",
3498
3499
3500
3501
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3502
	"http://www.nano-editor.org/"
3503
3504
    };

3505
    const char *xlcredits[XLCREDIT_LEN] = {
3506
3507
3508
3509
3510
3511
3512
3513
	N_("The nano text editor"),
	N_("version"),
	N_("Brought to you by:"),
	N_("Special thanks to:"),
	N_("The Free Software Foundation"),
	N_("For ncurses:"),
	N_("and anyone else we forgot..."),
	N_("Thank you for using nano!")
3514
    };
3515

3516
3517
3518
3519
3520
3521
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3522
3523
    curs_set(0);
    nodelay(edit, TRUE);
3524

3525
    blank_titlebar();
3526
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3527
    blank_edit();
3528
3529
    blank_statusbar();
    blank_bottombars();
3530

3531
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3532
    wrefresh(edit);
3533
    wrefresh(bottomwin);
3534
    napms(700);
3535

3536
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3537
	if ((kbinput = wgetch(edit)) != ERR)
3538
	    break;
3539

3540
	if (crpos < CREDIT_LEN) {
3541
	    const char *what;
3542
3543
	    size_t start_x;

3544
	    if (credits[crpos] == NULL) {
3545
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3546

3547
		what = _(xlcredits[xlpos]);
3548
		xlpos++;
3549
	    } else
3550
		what = credits[crpos];
3551

3552
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3553
3554
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3555
	}
3556

3557
3558
3559
3560
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3561
	napms(700);
3562

3563
	scrollok(edit, TRUE);
3564
	wscrl(edit, 1);
3565
	scrollok(edit, FALSE);
3566
	wrefresh(edit);
3567

3568
	if ((kbinput = wgetch(edit)) != ERR)
3569
	    break;
3570
	napms(700);
3571

3572
	scrollok(edit, TRUE);
3573
	wscrl(edit, 1);
3574
	scrollok(edit, FALSE);
3575
	wrefresh(edit);
3576
3577
    }

3578
3579
3580
    if (kbinput != ERR)
	ungetch(kbinput);

3581
3582
3583
3584
3585
3586
    if (!old_more_space || !old_no_help) {
	UNSET(MORE_SPACE);
	UNSET(NO_HELP);
	window_init();
    }

3587
    curs_set(1);
3588
    nodelay(edit, FALSE);
3589

3590
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3591
}
3592
#endif /* !DISABLE_EXTRA */