winio.c 93.2 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-2005 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
 *   any later version.                                                   *
 *                                                                        *
11
12
13
14
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
Chris Allegretta's avatar
Chris Allegretta committed
15
16
17
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
18
19
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
20
21
22
 *                                                                        *
 **************************************************************************/

23
24
25
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
26

27
#include <stdio.h>
Chris Allegretta's avatar
Chris Allegretta committed
28
29
#include <stdarg.h>
#include <string.h>
30
#include <unistd.h>
31
#include <errno.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
32
#include <ctype.h>
Chris Allegretta's avatar
Chris Allegretta committed
33
34
#include "proto.h"

35
static int *key_buffer = NULL;
36
37
38
39
40
41
				/* The default keystroke buffer,
				 * containing all the keystrokes we have
				 * at a given point. */
static size_t key_buffer_len = 0;
				/* The length of the default keystroke
				 * buffer. */
42
43
static int statusblank = 0;
				/* The number of keystrokes left after
44
45
				 * we call statusbar(), before we
				 * actually blank the statusbar. */
46
47
48
static bool disable_cursorpos = FALSE;
				/* Should we temporarily disable
				 * constant cursor position display? */
49

50
51
52
53
54
55
56
57
58
59
60
61
/* Control character compatibility:
 *
 * - NANO_BACKSPACE_KEY is Ctrl-H, which is Backspace under ASCII, ANSI,
 *   VT100, and VT220.
 * - NANO_TAB_KEY is Ctrl-I, which is Tab under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_ENTER_KEY is Ctrl-M, which is Enter under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_XON_KEY is Ctrl-Q, which is XON under ASCII, ANSI, VT100,
 *   VT220, and VT320.
 * - NANO_XOFF_KEY is Ctrl-S, which is XOFF under ASCII, ANSI, VT100,
 *   VT220, and VT320.
62
 * - NANO_CONTROL_8 is Ctrl-8 (Ctrl-?), which is Delete under ASCII,
63
64
 *   ANSI, VT100, and VT220, and which is Backspace under VT320.
 *
65
 * Note: VT220 and VT320 also generate Esc [ 3 ~ for Delete.  By
66
67
 * default, xterm assumes it's running on a VT320 and generates Ctrl-8
 * (Ctrl-?) for Backspace and Esc [ 3 ~ for Delete.  This causes
68
 * problems for VT100-derived terminals such as the FreeBSD console,
69
 * which expect Ctrl-H for Backspace and Ctrl-8 (Ctrl-?) for Delete, and
70
71
72
73
74
75
76
77
78
 * on which the VT320 sequences are translated by the keypad to KEY_DC
 * and [nothing].  We work around this conflict via the REBIND_DELETE
 * flag: if it's not set, we assume VT320 compatibility, and if it is,
 * we assume VT100 compatibility.  Thanks to Lee Nelson and Wouter van
 * Hemel for helping work this conflict out.
 *
 * Escape sequence compatibility:
 *
 * We support escape sequences for ANSI, VT100, VT220, VT320, the Linux
79
 * console, the FreeBSD console, the Mach console (a.k.a. the Hurd
80
81
82
83
84
85
86
87
 * console), xterm, rxvt, and Eterm.  Among these, there are several
 * conflicts and omissions, outlined as follows:
 *
 * - Tab on ANSI == PageUp on FreeBSD console; the former is omitted.
 *   (Ctrl-I is also Tab on ANSI, which we already support.)
 * - PageDown on FreeBSD console == Center (5) on numeric keypad with
 *   NumLock off on Linux console; the latter is omitted.  (The editing
 *   keypad key is more important to have working than the numeric
88
 *   keypad key, because the latter has no value when NumLock is off.)
89
90
91
92
 * - F1 on FreeBSD console == the mouse key on xterm/rxvt/Eterm; the
 *   latter is omitted.  (Mouse input will only work properly if the
 *   extended keypad value KEY_MOUSE is generated on mouse events
 *   instead of the escape sequence.)
93
 * - F9 on FreeBSD console == PageDown on Mach console; the former is
94
95
96
 *   omitted.  (The editing keypad is more important to have working
 *   than the function keys, because the functions of the former are not
 *   arbitrary and the functions of the latter are.)
97
 * - F10 on FreeBSD console == PageUp on Mach console; the former is
98
 *   omitted.  (Same as above.)
99
 * - F13 on FreeBSD console == End on Mach console; the former is
100
 *   omitted.  (Same as above.)
101
102
103
104
105
106
107
 * - 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
 *   omitted.  (Same as above.)
108
109
110
 *
 * Note that Center (5) on the numeric keypad with NumLock off can also
 * be the Begin key. */
111

112
113
114
115
#ifndef NANO_SMALL
/* Reset all the input routines that rely on character sequences. */
void reset_kbinput(void)
{
116
    parse_kbinput(NULL, NULL, NULL, TRUE);
117
    get_byte_kbinput(0, TRUE);
118
    get_unicode_kbinput(0, TRUE);
119
120
121
}
#endif

122
123
124
/* Read in a sequence of keystrokes from win and save them in the
 * default keystroke buffer.  This should only be called when the
 * default keystroke buffer is empty. */
125
void get_key_buffer(WINDOW *win)
126
{
127
    int input;
128
129
130
131
132
133

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

    /* Read in the first character using blocking input. */
134
135
136
#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
#endif
137

138
139
140
141
    /* Just before reading in the first character, display any pending
     * screen updates. */
    doupdate();

142
143
144
145
146
147
    while ((input = wgetch(win)) == ERR) {
	/* If errno is EIO, it means that the input source that we were
	 * using is gone, so die gracefully. */
	if (errno == EIO)
	    handle_hupterm(0);
    }
148

149

150
151
152
#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif
153

154
155
    /* Increment the length of the keystroke buffer, save the value of
     * the keystroke in key, and set key_code to TRUE if the keystroke
156
     * is an extended keypad value or FALSE if it isn't. */
157
    key_buffer_len++;
158
159
    key_buffer = (int *)nmalloc(sizeof(int));
    key_buffer[0] = input;
160

161
162
163
164
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
165
#ifndef NANO_SMALL
166
	allow_pending_sigwinch(TRUE);
167
#endif
168

169
	input = wgetch(win);
170

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

	/* Otherwise, increment the length of the keystroke buffer, save
	 * the value of the keystroke in key, and set key_code to TRUE
177
178
	 * if the keystroke is an extended keypad value or FALSE if it
	 * isn't. */
179
	key_buffer_len++;
180
181
182
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
	key_buffer[key_buffer_len - 1] = input;
183
184
185
186

#ifndef NANO_SMALL
	allow_pending_sigwinch(FALSE);
#endif
187
188
189
190
    }

    /* Switch back to non-blocking input. */
    nodelay(win, FALSE);
191
192

#ifdef DEBUG
193
    fprintf(stderr, "get_key_buffer(): key_buffer_len = %lu\n", (unsigned long)key_buffer_len);
194
#endif
195
}
196

197
/* Return the length of the default keystroke buffer. */
198
size_t get_key_buffer_len(void)
199
200
201
202
203
204
{
    return key_buffer_len;
}

/* Add the contents of the keystroke buffer input to the default
 * keystroke buffer. */
205
void unget_input(int *input, size_t input_len)
206
207
208
209
{
#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
    allow_pending_sigwinch(FALSE);
210
#endif
211

212
    /* If input is empty, get out. */
213
    if (input_len == 0)
214
215
216
217
218
	return;

    /* If adding input would put the default keystroke buffer beyond
     * maximum capacity, only add enough of input to put it at maximum
     * capacity. */
219
220
    if (key_buffer_len + input_len < key_buffer_len)
	input_len = (size_t)-1 - key_buffer_len;
221
222
223
224

    /* Add the length of input to the length of the default keystroke
     * buffer, and reallocate the default keystroke buffer so that it
     * has enough room for input. */
225
226
227
    key_buffer_len += input_len;
    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
	sizeof(int));
228
229
230
231

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

    /* Copy input to the beginning of the default keystroke buffer. */
237
    memcpy(key_buffer, input, input_len * sizeof(int));
238
239
}

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

249
    unget_input(&kbinput, 1);
250
251

    if (meta_key) {
252
253
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
254
255
256
257
258
259
260
261
    }
}

/* Try to read input_len characters from the default keystroke buffer.
 * If the default keystroke buffer is empty and win isn't NULL, try to
 * read in more characters from win and add them to the default
 * keystroke buffer before doing anything else.  If the default
 * keystroke buffer is empty and win is NULL, return NULL. */
262
int *get_input(WINDOW *win, size_t input_len)
263
{
264
    int *input;
265

266
#ifndef NANO_SMALL
267
    allow_pending_sigwinch(TRUE);
268
269
270
    allow_pending_sigwinch(FALSE);
#endif

271
272
    if (key_buffer_len == 0) {
	if (win != NULL)
273
	    get_key_buffer(win);
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288

	if (key_buffer_len == 0)
	    return NULL;
    }

    /* If input_len is greater than the length of the default keystroke
     * buffer, only read the number of characters in the default
     * keystroke buffer. */
    if (input_len > key_buffer_len)
	input_len = key_buffer_len;

    /* Subtract input_len from the length of the default keystroke
     * buffer, and allocate the keystroke buffer input so that it
     * has enough room for input_len keystrokes. */
    key_buffer_len -= input_len;
289
    input = (int *)nmalloc(input_len * sizeof(int));
290
291
292

    /* Copy input_len characters from the beginning of the default
     * keystroke buffer into input. */
293
    memcpy(input, key_buffer, input_len * sizeof(int));
294
295
296
297
298
299

    /* If the default keystroke buffer is empty, mark it as such. */
    if (key_buffer_len == 0) {
	free(key_buffer);
	key_buffer = NULL;
    /* If the default keystroke buffer isn't empty, move its
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
300
301
     * beginning forward far enough so that the keystrokes in input are
     * no longer at its beginning. */
302
303
    } else {
	memmove(key_buffer, key_buffer + input_len, key_buffer_len *
304
305
306
		sizeof(int));
	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
		sizeof(int));
307
308
309
    }

    return input;
310
311
}

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/* 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
 * sequences, and/or extended keypad values.  Set meta_key to TRUE when
 * we get a meta key sequence, and set func_key to TRUE when we get an
 * extended keypad value.  Supported extended keypad values consist of
 * [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
 * off.  Assume nodelay(win) is FALSE. */
int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
{
    int kbinput;

    /* Read in a character and interpret it.  Continue doing this until
     * we get a recognized value or sequence. */
    while ((kbinput = parse_kbinput(win, meta_key, func_key
#ifndef NANO_SMALL
		, FALSE
#endif
		)) == ERR);

    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
 * a function key.  Assume nodelay(win) is FALSE. */
int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key
341
#ifndef NANO_SMALL
342
	, bool reset
343
344
#endif
	)
345

346
{
347
    static int escapes = 0, byte_digits = 0;
348
    int *kbinput, retval = ERR;
349

350
#ifndef NANO_SMALL
351
352
    if (reset) {
	escapes = 0;
353
	byte_digits = 0;
354
	return ERR;
355
    }
356
#endif
357

358
359
    *meta_key = FALSE;
    *func_key = FALSE;
360

361
362
363
    /* Read in a character. */
    while ((kbinput = get_input(win, 1)) == NULL);

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    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. */
		    break;
		default:
		    /* More than two escapes: reset the escape counter
		     * and wait for more input. */
		    escapes = 0;
	    }
	    break;
382
#if !defined(NANO_SMALL) && defined(KEY_RESIZE)
383
384
385
386
387
	/* Since we don't change the default SIGWINCH handler when
	 * NANO_SMALL is defined, KEY_RESIZE is never generated.  Also,
	 * Slang and SunOS 5.7-5.9 don't support KEY_RESIZE. */
	case KEY_RESIZE:
	    break;
388
389
#endif
#ifdef PDCURSES
390
391
392
393
394
395
396
	case KEY_SHIFT_L:
	case KEY_SHIFT_R:
	case KEY_CONTROL_L:
	case KEY_CONTROL_R:
	case KEY_ALT_L:
	case KEY_ALT_R:
	    break;
397
#endif
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
	default:
	    switch (escapes) {
		case 0:
		    switch (*kbinput) {
			case NANO_CONTROL_8:
			    retval = ISSET(REBIND_DELETE) ?
				NANO_DELETE_KEY : NANO_BACKSPACE_KEY;
			    break;
			case KEY_DOWN:
			    retval = NANO_NEXTLINE_KEY;
			    break;
			case KEY_UP:
			    retval = NANO_PREVLINE_KEY;
			    break;
			case KEY_LEFT:
			    retval = NANO_BACK_KEY;
			    break;
			case KEY_RIGHT:
			    retval = NANO_FORWARD_KEY;
			    break;
418
#ifdef KEY_HOME
419
420
421
422
			/* HP-UX 10 and 11 don't support KEY_HOME. */
			case KEY_HOME:
			    retval = NANO_HOME_KEY;
			    break;
423
#endif
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
			case KEY_BACKSPACE:
			    retval = NANO_BACKSPACE_KEY;
			    break;
			case KEY_DC:
			    retval = ISSET(REBIND_DELETE) ?
				NANO_BACKSPACE_KEY : NANO_DELETE_KEY;
			    break;
			case KEY_IC:
			    retval = NANO_INSERTFILE_KEY;
			    break;
			case KEY_NPAGE:
			    retval = NANO_NEXTPAGE_KEY;
			    break;
			case KEY_PPAGE:
			    retval = NANO_PREVPAGE_KEY;
			    break;
			case KEY_ENTER:
			    retval = NANO_ENTER_KEY;
			    break;
			case KEY_A1:	/* Home (7) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_HOME_KEY;
			    break;
			case KEY_A3:	/* PageUp (9) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_PREVPAGE_KEY;
			    break;
			case KEY_B2:	/* Center (5) on numeric keypad
					 * with NumLock off. */
			    break;
			case KEY_C1:	/* End (1) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_END_KEY;
			    break;
			case KEY_C3:	/* PageDown (4) on numeric
					 * keypad with NumLock off. */
			    retval = NANO_NEXTPAGE_KEY;
			    break;
462
#ifdef KEY_BEG
463
464
465
466
			/* Slang doesn't support KEY_BEG. */
			case KEY_BEG:	/* Center (5) on numeric keypad
					 * with NumLock off. */
			    break;
467
#endif
468
#ifdef KEY_END
469
470
471
472
			/* HP-UX 10 and 11 don't support KEY_END. */
			case KEY_END:
			    retval = NANO_END_KEY;
			    break;
473
474
#endif
#ifdef KEY_SUSPEND
475
476
477
478
			/* Slang doesn't support KEY_SUSPEND. */
			case KEY_SUSPEND:
			    retval = NANO_SUSPEND_KEY;
			    break;
479
480
#endif
#ifdef KEY_SLEFT
481
482
483
484
			/* Slang doesn't support KEY_SLEFT. */
			case KEY_SLEFT:
			    retval = NANO_BACK_KEY;
			    break;
485
486
#endif
#ifdef KEY_SRIGHT
487
488
489
490
			/* Slang doesn't support KEY_SRIGHT. */
			case KEY_SRIGHT:
			    retval = NANO_FORWARD_KEY;
			    break;
491
#endif
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
			default:
			    retval = *kbinput;
			    break;
		    }
		    break;
		case 1:
		    /* One escape followed by a non-escape: escape
		     * sequence mode.  Reset the escape counter.  If
		     * there aren't any other keys waiting, we have a
		     * meta key sequence, so set meta_key to TRUE and
		     * save the lowercase version of the non-escape
		     * character as the result.  If there are other keys
		     * waiting, we have a true escape sequence, so
		     * interpret it. */
		    escapes = 0;
507
		    if (get_key_buffer_len() == 0) {
508
509
510
511
512
513
514
515
516
517
518
519
			*meta_key = TRUE;
			retval = tolower(*kbinput);
		    } else {
			int *seq;
			size_t seq_len;
			bool ignore_seq;

			/* 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);
520
			seq_len = get_key_buffer_len();
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
			seq = get_input(NULL, seq_len);
			retval = get_escape_seq_kbinput(seq, seq_len,
				&ignore_seq);

			/* If the escape sequence is unrecognized and
			 * not ignored, put back all of its characters
			 * except for the initial escape. */
			if (retval == ERR && !ignore_seq)
			    unget_input(seq, seq_len);

			free(seq);
		    }
		    break;
		case 2:
		    /* Two escapes followed by one or more decimal
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
536
		     * digits: byte sequence mode.  If the byte
537
538
539
540
541
542
543
544
545
546
547
548
549
550
		     * 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. */
		    if (('0' <= *kbinput && *kbinput <= '6' &&
			byte_digits == 0) || ('0' <= *kbinput &&
			*kbinput <= '9' && byte_digits > 0)) {
			int byte;

			byte_digits++;
			byte = get_byte_kbinput(*kbinput
551
#ifndef NANO_SMALL
552
				, FALSE
553
#endif
554
555
				);

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

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

567
568
			    /* Put back the multibyte equivalent of the
			     * byte value. */
569
570
			    byte_mb = make_mbchar((long)byte,
				&byte_mb_len);
571
572
573
574
575
576
577
578
579
580
581

			    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);
582
			}
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
		    } else {
			/* Reset the escape counter. */
			escapes = 0;
			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,
			     * and we're not in the middle of a byte
			     * sequence: control character sequence
			     * mode.  Interpret the control sequence and
			     * save the corresponding control character
			     * as the result. */
			    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;
			}
		    }
		    break;
	    }
    }
608

609
610
611
612
    /* If we have a result and it's an extended keypad value (i.e, a
     * value outside of byte range), set func_key to TRUE. */
    if (retval != ERR)
	*func_key = !is_byte(retval);
613
614

#ifdef DEBUG
615
    fprintf(stderr, "parse_kbinput(): kbinput = %d, meta_key = %d, func_key = %d, escapes = %d, byte_digits = %d, retval = %d\n", *kbinput, (int)*meta_key, (int)*func_key, escapes, byte_digits, retval);
616
617
#endif

618
    /* Return the result. */
619
620
621
    return retval;
}

622
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
623
 * keypad values, into their corresponding key values.  These sequences
624
625
626
627
628
 * are generated when the keypad doesn't support the needed keys.  If
 * the escape sequence is recognized but we want to ignore it, return
 * ERR and set ignore_seq to TRUE; if it's unrecognized, return ERR and
 * set ignore_seq to FALSE.  Assume that Escape has already been read
 * in. */
629
int get_escape_seq_kbinput(const int *seq, size_t seq_len, bool
630
	*ignore_seq)
631
{
632
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
633

634
635
    *ignore_seq = FALSE;

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

1123
#ifdef DEBUG
1124
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d, ignore_seq = %d\n", retval, (int)*ignore_seq);
1125
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1126

1127
    return retval;
1128
1129
}

1130
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1131
 * letters A (up), B (down), C (right), and D (left).  These are common
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
 * to many escape sequences. */
int get_escape_seq_abcd(int kbinput)
{
    switch (tolower(kbinput)) {
	case 'a':
	    return NANO_PREVLINE_KEY;
	case 'b':
	    return NANO_NEXTLINE_KEY;
	case 'c':
	    return NANO_FORWARD_KEY;
	case 'd':
	    return NANO_BACK_KEY;
	default:
	    return ERR;
    }
}

1149
1150
1151
/* Translate a byte sequence: turn a three-digit decimal number from
 * 000 to 255 into its corresponding byte value. */
int get_byte_kbinput(int kbinput
1152
1153
1154
1155
#ifndef NANO_SMALL
	, bool reset
#endif
	)
1156
{
1157
    static int byte_digits = 0, byte = 0;
1158
    int retval = ERR;
1159
1160

#ifndef NANO_SMALL
1161
    if (reset) {
1162
	byte_digits = 0;
1163
	byte = 0;
1164
1165
	return ERR;
    }
1166
1167
#endif

1168
1169
    /* Increment the byte digit counter. */
    byte_digits++;
1170

1171
    switch (byte_digits) {
1172
	case 1:
1173
1174
	    /* One digit: reset the byte sequence holder and add the
	     * digit we got to the 100's position of the byte sequence
1175
	     * holder. */
1176
	    byte = 0;
1177
	    if ('0' <= kbinput && kbinput <= '2')
1178
		byte += (kbinput - '0') * 100;
1179
1180
	    else
		/* If the character we got isn't a decimal digit, or if
1181
		 * it is and it would put the byte sequence out of byte
1182
1183
1184
1185
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 2:
1186
1187
	    /* Two digits: add the digit we got to the 10's position of
	     * the byte sequence holder. */
1188
1189
1190
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1191
1192
	    else
		/* If the character we got isn't a decimal digit, or if
1193
		 * it is and it would put the byte sequence out of byte
1194
1195
1196
1197
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 3:
1198
1199
1200
	    /* Three digits: add the digit we got to the 1's position of
	     * the byte sequence holder, and save the corresponding word
	     * value as the result. */
1201
1202
1203
1204
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
		byte += (kbinput - '0');
		retval = byte;
1205
	    } else
1206
		/* If the character we got isn't a decimal digit, or if
1207
		 * it is and it would put the byte sequence out of word
1208
1209
1210
		 * range, save it as the result. */
		retval = kbinput;
	    break;
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
	default:
	    /* More than three digits: save the character we got as the
	     * result. */
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1222
	byte = 0;
1223
1224
1225
    }

#ifdef DEBUG
1226
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1227
1228
1229
1230
1231
#endif

    return retval;
}

1232
1233
1234
/* Translate a Unicode sequence: turn a six-digit hexadecimal number
 * from 000000 to 10FFFF (case-insensitive) into its corresponding
 * multibyte value. */
1235
long get_unicode_kbinput(int kbinput
1236
1237
1238
1239
1240
#ifndef NANO_SMALL
	, bool reset
#endif
	)
{
1241
1242
1243
    static int uni_digits = 0;
    static long uni = 0;
    long retval = ERR;
1244
1245
1246

#ifndef NANO_SMALL
    if (reset) {
1247
1248
	uni_digits = 0;
	uni = 0;
1249
1250
1251
1252
	return ERR;
    }
#endif

1253
    /* Increment the Unicode digit counter. */
1254
    uni_digits++;
1255

1256
    switch (uni_digits) {
1257
	case 1:
1258
	    /* One digit: reset the Unicode sequence holder and add the
1259
	     * digit we got to the 0x100000's position of the Unicode
1260
1261
	     * sequence holder. */
	    uni = 0;
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
	    if ('0' <= kbinput && kbinput <= '1')
		uni += (kbinput - '0') * 0x100000;
	    else
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
		retval = kbinput;
	    break;
	case 2:
	    /* Two digits: add the digit we got to the 0x10000's
	     * position of the Unicode sequence holder. */
	    if ('0' == kbinput || (uni < 0x100000 && '1' <= kbinput &&
		kbinput <= '9'))
		uni += (kbinput - '0') * 0x10000;
	    else if (uni < 0x100000 && 'a' <= tolower(kbinput) &&
		tolower(kbinput) <= 'f')
		uni += (tolower(kbinput) + 10 - 'a') * 0x10000;
	    else
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
		retval = kbinput;
	    break;
	case 3:
	    /* Three digits: add the digit we got to the 0x1000's
	     * position of the Unicode sequence holder. */
1288
	    if ('0' <= kbinput && kbinput <= '9')
1289
		uni += (kbinput - '0') * 0x1000;
1290
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1291
		uni += (tolower(kbinput) + 10 - 'a') * 0x1000;
1292
1293
	    else
		/* If the character we got isn't a hexadecimal digit, or
1294
1295
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
1296
1297
		retval = kbinput;
	    break;
1298
1299
	case 4:
	    /* Four digits: add the digit we got to the 0x100's position
1300
	     * of the Unicode sequence holder. */
1301
	    if ('0' <= kbinput && kbinput <= '9')
1302
		uni += (kbinput - '0') * 0x100;
1303
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1304
		uni += (tolower(kbinput) + 10 - 'a') * 0x100;
1305
	    else
1306
		/* If the character we got isn't a hexadecimal digit, or
1307
1308
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
1309
1310
		retval = kbinput;
	    break;
1311
1312
	case 5:
	    /* Five digits: add the digit we got to the 0x10's position
1313
	     * of the Unicode sequence holder. */
1314
	    if ('0' <= kbinput && kbinput <= '9')
1315
		uni += (kbinput - '0') * 0x10;
1316
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1317
		uni += (tolower(kbinput) + 10 - 'a') * 0x10;
1318
1319
	    else
		/* If the character we got isn't a hexadecimal digit, or
1320
1321
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
1322
1323
		retval = kbinput;
	    break;
1324
1325
	case 6:
	    /* Six digits: add the digit we got to the 1's position of
1326
1327
	     * the Unicode sequence holder, and save the corresponding
	     * Unicode value as the result. */
1328
	    if ('0' <= kbinput && kbinput <= '9') {
1329
1330
		uni += (kbinput - '0');
		retval = uni;
1331
1332
	    } else if ('a' <= tolower(kbinput) && tolower(kbinput) <=
		'f') {
1333
1334
		uni += (tolower(kbinput) + 10 - 'a');
		retval = uni;
1335
	    } else
1336
		/* If the character we got isn't a hexadecimal digit, or
1337
1338
		 * if it is and it would put the Unicode sequence out of
		 * valid range, save it as the result. */
1339
1340
1341
		retval = kbinput;
	    break;
	default:
1342
	    /* More than six digits: save the character we got as the
1343
1344
1345
1346
	     * result. */
	    retval = kbinput;
	    break;
    }
1347

1348
1349
    /* If we have a result, reset the Unicode digit counter and the
     * Unicode sequence holder. */
1350
    if (retval != ERR) {
1351
1352
	uni_digits = 0;
	uni = 0;
1353
    }
1354

1355
#ifdef DEBUG
1356
    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n", kbinput, uni_digits, uni, retval);
1357
1358
#endif

1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
    return retval;
}

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

     /* Ctrl-2 (Ctrl-Space, Ctrl-@, Ctrl-`) */
    if (kbinput == '2' || kbinput == ' ' || kbinput == '@' ||
	kbinput == '`')
	retval = NANO_CONTROL_SPACE;
    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */
    else if ('3' <= kbinput && kbinput <= '7')
	retval = kbinput - 24;
    /* Ctrl-8 (Ctrl-?) */
    else if (kbinput == '8' || kbinput == '?')
	retval = NANO_CONTROL_8;
    /* Ctrl-A to Ctrl-_ */
    else if ('A' <= kbinput && kbinput <= '_')
	retval = kbinput - 64;
    /* Ctrl-a to Ctrl-~ */
    else if ('a' <= kbinput && kbinput <= '~')
	retval = kbinput - 96;
    else
	retval = kbinput;

1387
#ifdef DEBUG
1388
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1389
1390
#endif

1391
1392
    return retval;
}
1393

1394
1395
1396
1397
/* Put the output-formatted characters in output back into the default
 * keystroke buffer, so that they can be parsed and displayed as output
 * again. */
void unparse_kbinput(char *output, size_t output_len)
1398
{
1399
1400
    int *input;
    size_t i;
1401

1402
1403
1404
1405
1406
1407
1408
1409
    if (output_len == 0)
	return;

    input = (int *)nmalloc(output_len * sizeof(int));
    for (i = 0; i < output_len; i++)
	input[i] = (int)output[i];
    unget_input(input, output_len);
    free(input);
1410
1411
}

1412
/* Read in a stream of characters verbatim, and return the length of the
1413
1414
1415
1416
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1417

1418
    /* Turn off flow control characters if necessary so that we can type
1419
1420
     * them in verbatim, and turn the keypad off if necessary so that we
     * don't get extended keypad values. */
1421
1422
    if (ISSET(PRESERVE))
	disable_flow_control();
1423
1424
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, FALSE);
1425
1426
1427

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

    /* Turn flow control characters back on if necessary and turn the
1430
     * keypad back on if necessary now that we're done. */
1431
1432
    if (ISSET(PRESERVE))
	enable_flow_control();
1433
1434
    if (!ISSET(REBIND_KEYPAD))
	keypad(win, TRUE);
1435

1436
    return retval;
1437
1438
}

1439
1440
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
1441
1442
 * the input into the corresponding multibyte value if possible.  After
 * that, leave the input as-is. */ 
1443
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1444
{
1445
1446
    int *kbinput, *retval;
    long uni;
1447

1448
1449
1450
    /* Read in the first keystroke. */
    while ((kbinput = get_input(win, 1)) == NULL);

1451
    /* Check whether the first keystroke is a hexadecimal digit. */
1452
    uni = get_unicode_kbinput(*kbinput
1453
#ifndef NANO_SMALL
1454
	, FALSE
1455
#endif
1456
1457
	);

1458
1459
    /* If the first keystroke isn't a hexadecimal digit, put back the
     * first keystroke. */
1460
    if (uni != ERR)
1461
1462
1463
1464
	unget_input(kbinput, 1);
    /* Otherwise, read in keystrokes until we have a complete word
     * sequence, and put back the corresponding word value. */
    else {
1465
1466
	char *uni_mb;
	int uni_mb_len, *seq, i;
1467

1468
	while (uni == ERR) {
1469
	    while ((kbinput = get_input(win, 1)) == NULL);
1470

1471
	    uni = get_unicode_kbinput(*kbinput
1472
#ifndef NANO_SMALL
1473
		, FALSE
1474
#endif
1475
1476
		);
	}
1477

1478
1479
	/* Put back the multibyte equivalent of the Unicode value. */
	uni_mb = make_mbchar(uni, &uni_mb_len);
1480

1481
	seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1482

1483
1484
	for (i = 0; i < uni_mb_len; i++)
	    seq[i] = (unsigned char)uni_mb[i];
1485

1486
	unget_input(seq, uni_mb_len);
1487
1488

	free(seq);
1489
	free(uni_mb);
1490
1491
    }

1492
    /* Get the complete sequence, and save the characters in it as the
1493
     * result. */
1494
    *kbinput_len = get_key_buffer_len();
1495
    retval = get_input(NULL, *kbinput_len);
1496
1497
1498
1499

    return retval;
}

1500
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1501
1502
/* Check for a mouse event, and if one's taken place, save the
 * coordinates where it took place in mouse_x and mouse_y.  After that,
1503
1504
 * assuming allow_shortcuts is FALSE, if the shortcut list on the
 * bottom two lines of the screen is visible and the mouse event took
1505
1506
1507
 * place on it, figure out which shortcut was clicked and put back the
 * equivalent keystroke(s).  Return FALSE if no keystrokes were
 * put back, or TRUE if at least one was.  Assume that KEY_MOUSE has
1508
 * already been read in. */
1509
bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1510
1511
1512
1513
1514
1515
1516
1517
{
    MEVENT mevent;

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

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1518
	return FALSE;
1519
1520
1521
1522
1523

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

1524
1525
1526
    /* If we're allowing shortcuts, the current shortcut list is being
     * displayed on the last two lines of the screen, and the mouse
     * event took place inside it, we need to figure out which shortcut
1527
     * was clicked and put back the equivalent keystroke(s) for it. */
1528
1529
    if (allow_shortcuts && !ISSET(NO_HELP) && wenclose(bottomwin,
	*mouse_y, *mouse_x)) {
1530
	int i, j;
1531
	size_t currslen;
1532
1533
1534
1535
1536
1537
1538
1539
	    /* The number of shortcuts in the current shortcut list. */
	const shortcut *s = currshortcut;
	    /* The actual shortcut we clicked on, starting at the first
	     * one in the current shortcut list. */

	/* Get the shortcut lists' length. */
	if (currshortcut == main_list)
	    currslen = MAIN_VISIBLE;
1540
	else {
1541
1542
	    currslen = length_of_list(currshortcut);

1543
1544
1545
1546
1547
1548
	    /* We don't show any more shortcuts than the main list
	     * does. */
	    if (currslen > MAIN_VISIBLE)
		currslen = MAIN_VISIBLE;
	}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1549
1550
	/* Calculate the width of each shortcut in the list.  It's the
	 * same for all of them. */
1551
1552
1553
1554
1555
	if (currslen < 2)
	    i = COLS / 6;
	else
	    i = COLS / ((currslen / 2) + (currslen % 2));

1556
	/* Calculate the y-coordinate relative to the beginning of
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1557
1558
1559
1560
	 * the shortcut list in bottomwin, i.e, with the sizes of
	 * topwin, edit, and the first line of bottomwin subtracted
	 * out. */
	j = *mouse_y - (2 - no_more_space()) - editwinrows - 1;
1561
1562
1563
1564
1565

	/* If we're on the statusbar, beyond the end of the shortcut
	 * list, or beyond the end of a shortcut on the right side of
	 * the screen, don't do anything. */
	if (j < 0 || (*mouse_x / i) >= currslen)
1566
	    return FALSE;
1567
1568
	j = (*mouse_x / i) * 2 + j;
	if (j >= currslen)
1569
	    return FALSE;
1570
1571
1572
1573
1574
1575

	/* Go through the shortcut list to determine which shortcut was
	 * clicked. */
	for (; j > 0; j--)
	    s = s->next;

1576
1577
1578
	/* And put back the equivalent key.  Assume that each shortcut
	 * has, at the very least, an equivalent control key, an
	 * equivalent primary meta key sequence, or both. */
1579
	if (s->ctrlval != NANO_NO_KEY) {
1580
	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1581
1582
	    return TRUE;
	} else if (s->metaval != NANO_NO_KEY) {
1583
	    unget_kbinput(s->metaval, TRUE, FALSE);
1584
1585
	    return TRUE;
	}
1586
    }
1587
    return FALSE;
1588
}
1589
1590
#endif /* !DISABLE_MOUSE */

1591
const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool
1592
	*meta_key, bool *func_key)
1593
1594
1595
1596
{
    const shortcut *s = s_list;
    size_t slen = length_of_list(s_list);

1597
#ifdef DEBUG
1598
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %d, func_key = %d\n", *kbinput, (int)*meta_key, (int)*func_key);
1599
1600
#endif

1601
1602
1603
1604
1605
1606
    /* Check for shortcuts. */
    for (; slen > 0; slen--) {
	/* We've found a shortcut if:
	 *
	 * 1. The key exists.
	 * 2. The key is a control key in the shortcut list.
1607
1608
1609
1610
1611
	 * 3. meta_key is TRUE and the key is the primary or
	 *    miscellaneous meta sequence in the shortcut list.
	 * 4. func_key is TRUE and the key is a function key in the
	 *    shortcut list. */

1612
1613
1614
1615
	if (*kbinput != NANO_NO_KEY && (*kbinput == s->ctrlval ||
		(*meta_key == TRUE && (*kbinput == s->metaval ||
		*kbinput == s->miscval)) || (*func_key == TRUE &&
		*kbinput == s->funcval))) {
1616
1617
1618
1619
1620
1621
1622
1623
	    break;
	}

	s = s->next;
    }

    /* Translate the shortcut to either its control key or its meta key
     * equivalent.  Assume that the shortcut has an equivalent control
1624
     * key, an equivalent primary meta key sequence, or both. */
1625
1626
1627
    if (slen > 0) {
	if (s->ctrlval != NANO_NO_KEY) {
	    *meta_key = FALSE;
1628
	    *func_key = FALSE;
1629
	    *kbinput = s->ctrlval;
1630
	    return s;
1631
1632
	} else if (s->metaval != NANO_NO_KEY) {
	    *meta_key = TRUE;
1633
	    *func_key = FALSE;
1634
	    *kbinput = s->metaval;
1635
	    return s;
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
	}
    }

    return NULL;
}

#ifndef NANO_SMALL
const toggle *get_toggle(int kbinput, bool meta_key)
{
    const toggle *t = toggles;

1647
1648
1649
1650
#ifdef DEBUG
    fprintf(stderr, "get_toggle(): kbinput = %d, meta_key = %d\n", kbinput, (int)meta_key);
#endif

1651
1652
1653
    /* Check for toggles. */
    for (; t != NULL; t = t->next) {
	/* We've found a toggle if meta_key is TRUE and the key is in
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1654
	 * the meta key toggle list. */
1655
1656
1657
1658
1659
1660
1661
1662
	if (meta_key && kbinput == t->val)
	    break;
    }

    return t;
}
#endif /* !NANO_SMALL */

1663
1664
1665
1666
1667
1668
1669
1670
1671
/* 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);
    for (; n > 0; n--)
	waddch(win, ' ');
}

1672
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1673
{
1674
    blank_line(topwin, 0, 0, COLS);
1675
1676
}

1677
1678
1679
void blank_topbar(void)
{
    if (!ISSET(MORE_SPACE))
1680
	blank_line(topwin, 1, 0, COLS);
1681
1682
}

Chris Allegretta's avatar
Chris Allegretta committed
1683
1684
void blank_edit(void)
{
1685
    int i;
1686
    for (i = 0; i < editwinrows; i++)
1687
	blank_line(edit, i, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1688
1689
1690
1691
}

void blank_statusbar(void)
{
1692
    blank_line(bottomwin, 0, 0, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1693
1694
}

1695
1696
1697
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
1698
1699
	blank_line(bottomwin, 1, 0, COLS);
	blank_line(bottomwin, 2, 0, COLS);
1700
1701
1702
    }
}

1703
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1704
{
1705
    if (statusblank > 0)
1706
	statusblank--;
1707
1708

    if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
1709
1710
1711
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
1712
	wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1713
1714
1715
    }
}

1716
1717
1718
1719
/* 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
1720
1721
1722
1723
1724
 * 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)
1725
1726
{
    size_t start_index;
1727
	/* Index in buf of the first character shown. */
1728
    size_t column;
1729
	/* Screen column that start_index corresponds to. */
1730
1731
1732
1733
1734
1735
1736
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */

1737
1738
1739
    char *buf_mb = charalloc(mb_cur_max());
    int buf_mb_len;

1740
1741
1742
1743
1744
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

1745
1746
1747
1748
1749
    if (len == 0)
	return mallocstrcpy(NULL, "");

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

1751
    assert(column <= start_col);
1752

1753
    /* Allocate enough space for the entire line. */
1754
    alloc_len = (mb_cur_max() * (COLS + 1));
1755

1756
1757
1758
    converted = charalloc(alloc_len + 1);
    index = 0;

1759
1760
    if (buf[start_index] != '\t' && (column < start_col || (dollars &&
	column > 0))) {
1761
1762
	/* We don't display all of buf[start_index] since it starts to
	 * the left of the screen. */
1763
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1764

1765
	if (is_cntrl_mbchar(buf_mb)) {
1766
	    if (column < start_col) {
1767
1768
		char *ctrl_buf_mb = charalloc(mb_cur_max());
		int ctrl_buf_mb_len, i;
1769

1770
1771
		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
			&ctrl_buf_mb_len);
1772

1773
1774
		for (i = 0; i < ctrl_buf_mb_len; i++)
		    converted[index++] = ctrl_buf_mb[i];
1775

1776
		start_col += mbwidth(ctrl_buf_mb);
1777

1778
		free(ctrl_buf_mb);
1779

1780
		start_index += buf_mb_len;
1781
	    }
1782
	}
1783
#ifdef ENABLE_UTF8
1784
	else if (ISSET(USE_UTF8) && mbwidth(buf_mb) > 1) {
1785
	    converted[index++] = ' ';
1786
	    start_col++;
1787
1788

	    start_index += buf_mb_len;
1789
	}
1790
#endif
1791
1792
    }

1793
    while (index < alloc_len - 1 && buf[start_index] != '\0') {
1794
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1795

1796
	/* If buf contains a tab character, interpret it. */
1797
	if (*buf_mb == '\t') {
1798
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
1799
1800
1801
1802
1803
1804
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = 0; i < whitespace_len[0]; i++)
		    converted[index++] = whitespace[i];
	    } else
1805
#endif
1806
		converted[index++] = ' '; 
1807
	    start_col++;
1808
	    while (start_col % tabsize != 0) {
1809
		converted[index++] = ' ';
1810
1811
		start_col++;
	    }
1812
	/* If buf contains a control character, interpret it.  If buf
1813
1814
	 * contains an invalid multibyte control character, display it
	 * as such.*/
1815
	} else if (is_cntrl_mbchar(buf_mb)) {
1816
1817
	    char *ctrl_buf_mb = charalloc(mb_cur_max());
	    int ctrl_buf_mb_len, i;
1818

1819
	    converted[index++] = '^';
1820
1821
	    start_col++;

1822
1823
	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
		&ctrl_buf_mb_len);
1824

1825
1826
	    for (i = 0; i < ctrl_buf_mb_len; i++)
		converted[index++] = ctrl_buf_mb[i];
1827

1828
	    start_col += mbwidth(ctrl_buf_mb);
1829

1830
	    free(ctrl_buf_mb);
1831
	/* If buf contains a space character, interpret it. */
1832
	} else if (*buf_mb == ' ') {
1833
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
1834
1835
1836
1837
1838
1839
1840
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = whitespace_len[0]; i < whitespace_len[0] +
			whitespace_len[1]; i++)
		    converted[index++] = whitespace[i];
	    } else
1841
#endif
1842
		converted[index++] = ' '; 
1843
	    start_col++;
1844
1845
1846
	/* If buf contains a non-control character, interpret it.  If
	 * buf contains an invalid multibyte non-control character,
	 * display it as such. */
1847
	} else {
1848
1849
	    char *nctrl_buf_mb = charalloc(mb_cur_max());
	    int nctrl_buf_mb_len, i;
1850

1851
1852
	    nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
		&nctrl_buf_mb_len);
1853

1854
1855
1856
1857
1858
1859
	    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);
1860
1861
	}

1862
	start_index += buf_mb_len;
1863
1864
    }

1865
1866
    free(buf_mb);

1867
    if (index < alloc_len - 1)
1868
1869
1870
1871
	converted[index] = '\0';

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

1874
    return converted;
1875
1876
}

1877
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
1878
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1879
    int space = COLS;
1880
	/* The space we have available for display. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1881
1882
1883
    size_t verlen = strlenpt(VERMSG) + 1;
	/* The length of the version message in columns, plus one for
	 * padding. */
1884
    const char *prefix;
1885
	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
1886
    size_t prefixlen;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1887
	/* The length of the prefix in columns, plus one for padding. */
1888
    const char *state;
1889
1890
	/* "Modified", "View", or "".  Shows the state of this
	 * buffer. */
1891
    size_t statelen = 0;
1892
	/* The length of the state in columns, or the length of
1893
	 * "Modified" if the state is blank. */
1894
1895
    char *exppath = NULL;
	/* The file name, expanded for display. */
1896
    bool newfie = FALSE;
1897
	/* Do we say "New Buffer"? */
1898
    bool dots = FALSE;
1899
1900
	/* Do we put an ellipsis before the path? */

1901
    assert(path != NULL || openfile->filename != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1902
1903

    wattron(topwin, A_REVERSE);
1904
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
1905

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1906
1907
1908
1909
    /* 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)
1910
1911
	space = 0;
    else {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1912
1913
1914
1915
	/* 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;
1916
    }
Chris Allegretta's avatar
Chris Allegretta committed
1917

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1918
    if (space >= 4) {
1919
1920
	/* Add a space after the version message, and account for both
	 * it and the two spaces before it. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1921
1922
1923
1924
1925
	mvwaddnstr(topwin, 0, 2, VERMSG, actual_x(VERMSG, verlen));
	verlen += 3;

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

1928
1929
1930
1931
1932
1933
1934
1935
1936
#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") : "";

1937
    statelen = strlenpt((state[0] != '\0') ? state : _("Modified"));
1938

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1939
1940
    /* If possible, add a space before state. */
    if (space > 0 && statelen < space)
1941
	statelen++;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1942
    else
1943
1944
1945
	goto the_end;

#ifndef DISABLE_BROWSER
1946
    /* path should be a directory if we're in the file browser. */
1947
1948
1949
1950
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
1951
    if (openfile->filename[0] == '\0') {
1952
	prefix = _("New Buffer");
1953
	newfie = TRUE;
1954
1955
    } else
	prefix = _("File:");
1956

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1959
    /* If newfie is FALSE, add a space after prefix. */
1960
    if (!newfie && prefixlen + statelen < space)
1961
1962
	prefixlen++;

1963
1964
    /* If we're not in the file browser, path should be the current
     * filename. */
1965
    if (path == NULL)
1966
	path = openfile->filename;
1967

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1968
    /* Account for the full lengths of the prefix and the state. */
1969
1970
1971
1972
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1973
	/* space is now the room we have for the filename. */
1974

1975
    if (!newfie) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1976
	size_t lenpt = strlenpt(path), start_col;
1977

1978
	dots = (lenpt >= space);
1979
1980
1981
1982
1983
1984
1985
1986

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

	exppath = display_string(path, start_col, space, FALSE);
1987
1988
1989
    }

    if (!dots) {
1990
1991
1992
	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
	    /* The length of the expanded filename. */

1993
	/* There is room for the whole filename, so we center it. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1994
1995
	mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3),
		prefix, actual_x(prefix, prefixlen));
1996
	if (!newfie) {
1997
1998
1999
2000
2001
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    } else {
	/* We will say something like "File: ...ename". */
2002
2003
	mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix,
		prefixlen));
2004
	if (space <= -3 || newfie)
2005
2006
	    goto the_end;
	waddch(topwin, ' ');
2007
2008
	waddnstr(topwin, "...", space + 3);
	if (space <= 0)
2009
	    goto the_end;
2010
	waddstr(topwin, exppath);
2011
2012
2013
2014
2015
    }

  the_end:
    free(exppath);

2016
    if (state[0] != '\0') {
2017
	if (statelen >= COLS - 1)
2018
2019
	    mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
	else {
2020
	    assert(COLS - statelen - 1 >= 0);
2021

2022
	    mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2023
		actual_x(state, statelen));
2024
	}
2025
    }
2026

Chris Allegretta's avatar
Chris Allegretta committed
2027
    wattroff(topwin, A_REVERSE);
2028

2029
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2030
    reset_cursor();
2031
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2032
2033
}

2034
2035
/* Set the modified flag if it isn't already set, and then update the
 * titlebar. */
2036
2037
void set_modified(void)
{
2038
2039
    if (!openfile->modified) {
	openfile->modified = TRUE;
2040
2041
2042
2043
	titlebar(NULL);
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2044
2045
2046
/* 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. */
2047
2048
2049
void statusbar(const char *msg, ...)
{
    va_list ap;
2050
2051
2052
2053
2054
    char *bar, *foo;
    size_t start_x, foo_len;
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
    bool old_whitespace;
#endif
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068

    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(). */
    if (curses_ended) {
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

    /* Blank out the line. */
    blank_statusbar();

2069
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2070
2071
    old_whitespace = ISSET(WHITESPACE_DISPLAY);
    UNSET(WHITESPACE_DISPLAY);
2072
#endif
2073
2074
2075
2076
    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);
2077
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2078
2079
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
2080
#endif
2081
2082
2083
    free(bar);
    foo_len = strlenpt(foo);
    start_x = (COLS - foo_len - 4) / 2;
2084

2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
    wmove(bottomwin, 0, start_x);
    wattron(bottomwin, A_REVERSE);
    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
    free(foo);
    waddstr(bottomwin, " ]");
    wattroff(bottomwin, A_REVERSE);
    wnoutrefresh(bottomwin);
    reset_cursor();
    wnoutrefresh(edit);
	/* Leave the cursor at its position in the edit window, not in
	 * the statusbar. */
2097

2098
    disable_cursorpos = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2099
2100
2101
2102
2103

    /* If we're doing quick statusbar blanking, and constant cursor
     * position display is off, blank the statusbar after only one
     * keystroke.  Otherwise, blank it after twenty-five keystrokes,
     * as Pico does. */
2104
2105
    statusblank =
#ifndef NANO_SMALL
2106
	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2107
2108
#endif
	25;
2109
2110
}

2111
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
2112
{
2113
    size_t i, colwidth, slen;
2114

Chris Allegretta's avatar
Chris Allegretta committed
2115
2116
2117
    if (ISSET(NO_HELP))
	return;

2118
2119
    if (s == main_list) {
	slen = MAIN_VISIBLE;
2120

2121
	assert(slen <= length_of_list(s));
2122
    } else {
2123
2124
	slen = length_of_list(s);

2125
	/* Don't show any more shortcuts than the main list does. */
2126
2127
2128
2129
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2130
    /* There will be this many characters per column.  We need at least
2131
     * 3 to display anything properly. */
2132
    colwidth = COLS / ((slen / 2) + (slen % 2));
Chris Allegretta's avatar
Chris Allegretta committed
2133

2134
    blank_bottombars();
2135

2136
    for (i = 0; i < slen; i++, s = s->next) {
2137
	const char *keystr;
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
	char foo[4] = "";

	/* Yucky sentinel values that we can't handle a better way. */
	if (s->ctrlval == NANO_CONTROL_SPACE)
	    strcpy(foo, "^ ");
	else if (s->ctrlval == NANO_CONTROL_8)
	    strcpy(foo, "^?");
	/* Normal values.  Assume that the shortcut has an equivalent
	 * control key, meta key sequence, or both. */
	else if (s->ctrlval != NANO_NO_KEY)
	    sprintf(foo, "^%c", s->ctrlval + 64);
	else if (s->metaval != NANO_NO_KEY)
	    sprintf(foo, "M-%c", toupper(s->metaval));

	keystr = foo;
2153
2154

	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2155
	onekey(keystr, s->desc, colwidth);
Chris Allegretta's avatar
Chris Allegretta committed
2156
    }
2157

2158
2159
    wnoutrefresh(bottomwin);
    reset_cursor();
2160
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2161
2162
}

2163
2164
2165
2166
2167
2168
/* 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
2169
{
2170
2171
    size_t keystroke_len = strlenpt(keystroke) + 1;

2172
2173
    assert(keystroke != NULL && desc != NULL);

2174
    wattron(bottomwin, A_REVERSE);
2175
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2176
    wattroff(bottomwin, A_REVERSE);
2177
2178
2179
2180
2181
2182

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

2183
2184
    if (len > 0) {
	waddch(bottomwin, ' ');
2185
	waddnstr(bottomwin, desc, actual_x(desc, len));
Chris Allegretta's avatar
Chris Allegretta committed
2186
2187
2188
    }
}

2189
2190
/* 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
2191
2192
void reset_cursor(void)
{
2193
2194
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
2195
    if (openfile == NULL) {
2196
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2197
	return;
2198
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2199

2200
2201
2202
    openfile->current_y = openfile->current->lineno -
	openfile->edittop->lineno;
    if (openfile->current_y < editwinrows) {
2203
	size_t x = xplustabs();
2204
	wmove(edit, openfile->current_y, x - get_page_start(x));
2205
     }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2206
}
Chris Allegretta's avatar
Chris Allegretta committed
2207

2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
/* 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. */
void edit_draw(const filestruct *fileptr, const char *converted, int
	line, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2218
{
2219
#if !defined(NANO_SMALL) || defined(ENABLE_COLOR)
2220
2221
2222
2223
2224
2225
2226
2227
2228
    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. */
2229
2230
#endif

2231
    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2232
    assert(strlenpt(converted) <= COLS);
2233

2234
    /* Just paint the string in any case (we'll add color or reverse on
2235
     * just the text that needs it). */
2236
    mvwaddstr(edit, line, 0, converted);
2237

Chris Allegretta's avatar
Chris Allegretta committed
2238
#ifdef ENABLE_COLOR
2239
2240
2241
2242
    /* 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;
2243
2244
2245
2246
2247

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
2248
		/* Number of chars to paint on this line.  There are COLS
2249
		 * characters on a whole line. */
2250
	    size_t index;
2251
		/* Index in converted where we paint. */
2252
2253
2254
2255
	    regmatch_t startmatch;
		/* Match position for start_regex. */
	    regmatch_t endmatch;
		/* Match position for end_regex. */
2256
2257
2258
2259

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2260
2261
	    /* Two notes about regexec().  Return value 0 means there is
	     * a match.  Also, rm_eo is the first non-matching character
2262
2263
2264
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
2265
	    if (tmpcolor->end == NULL) {
2266
2267
2268
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2269
2270
2271
2272
2273
2274
2275
2276
		 * last match.  Even though two matches may overlap, we
		 * want to ignore them, so that we can highlight
		 * C-strings correctly. */
		while (k < endpos) {
		    /* Note the fifth parameter to regexec().  It says
		     * not to match the beginning-of-line character
		     * unless k is 0.  If regexec() returns REG_NOMATCH,
		     * there are no more matches in the line. */
2277
		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2278
2279
			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
			REG_NOMATCH)
2280
			break;
2281
2282
		    /* Translate the match to the beginning of the
		     * line. */
2283
2284
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2285
2286
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
2287
2288
			statusbar(
				_("Refusing zero-length regex match"));
2289
		    } else if (startmatch.rm_so < endpos &&
2290
			startmatch.rm_eo > startpos) {
2291
			if (startmatch.rm_so <= startpos)
2292
			    x_start = 0;
2293
			else
2294
2295
			    x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
2296

2297
2298
2299
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
2300
2301
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
2302
2303
2304

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

2305
2306
			mvwaddnstr(edit, line, x_start, converted +
				index, paintlen);
2307
		    }
2308
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2309
		}
2310
	    } else {
2311
		/* This is a multi-line regex.  There are two steps.
2312
2313
2314
2315
2316
2317
		 * 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
2318
2319
2320
2321
		 * 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. */
2322
		const filestruct *start_line = fileptr->prev;
2323
		    /* The first line before fileptr matching start. */
2324
		regoff_t start_col;
2325
		    /* Where it starts in that line. */
2326
2327
		const filestruct *end_line;

2328
		while (start_line != NULL && regexec(tmpcolor->start,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2329
2330
			start_line->data, 1, &startmatch, 0) ==
			REG_NOMATCH) {
2331
2332
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
2333
2334
		    if (regexec(tmpcolor->end, start_line->data, 0,
			NULL, 0) == 0)
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
			goto step_two;
		    start_line = start_line->prev;
		}
		/* No start found, so skip to the next step. */
		if (start_line == NULL)
		    goto step_two;
		/* 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;
2345
		while (TRUE) {
2346
2347
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
2348
2349
 		    if (regexec(tmpcolor->end, start_line->data +
			start_col + startmatch.rm_eo, 0, NULL,
2350
			(start_col + startmatch.rm_eo == 0) ? 0 :
2351
2352
			REG_NOTBOL) == REG_NOMATCH)
			/* No end found after this start. */
2353
			break;
2354
		    start_col++;
2355
		    if (regexec(tmpcolor->start, start_line->data +
2356
2357
			start_col, 1, &startmatch,
			REG_NOTBOL) == REG_NOMATCH)
2358
2359
			/* No later start on this line. */
			goto step_two;
2360
		}
2361
2362
		/* Indeed, there is a start not followed on this line by
		 * an end. */
2363
2364
2365

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

		/* No end found, or it is too early. */
2374
2375
		if (end_line == NULL || (end_line == fileptr &&
			endmatch.rm_eo <= startpos))
2376
2377
2378
		    goto step_two;

		/* Now paint the start of fileptr. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
		if (end_line != 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. */
		    paintlen = -1;
		else
		    /* Otherwise, paintlen is the expanded location of
		     * the end of the match minus the expanded location
		     * of the beginning of the page. */
		    paintlen = actual_x(converted,
			strnlenpt(fileptr->data, endmatch.rm_eo) -
			start);
2391

2392
		mvwaddnstr(edit, line, 0, converted, paintlen);
2393

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2394
2395
  step_two:
		/* Second step, we look for starts on this line. */
2396
		start_col = 0;
2397

2398
		while (start_col < endpos) {
2399
2400
2401
2402
		    if (regexec(tmpcolor->start, fileptr->data +
			start_col, 1, &startmatch, (start_col == 0) ?
			0 : REG_NOTBOL) == REG_NOMATCH || start_col +
			startmatch.rm_so >= endpos)
2403
2404
2405
2406
2407
2408
2409
			/* No more starts on this line. */
			break;
		    /* Translate the match to be relative to the
		     * beginning of the line. */
		    startmatch.rm_so += start_col;
		    startmatch.rm_eo += start_col;

2410
		    if (startmatch.rm_so <= startpos)
2411
			x_start = 0;
2412
		    else
2413
2414
			x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
2415

2416
		    index = actual_x(converted, x_start);
2417

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2418
2419
2420
2421
		    if (regexec(tmpcolor->end, fileptr->data +
			startmatch.rm_eo, 1, &endmatch,
			(startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) ==
			0) {
2422
			/* Translate the end match to be relative to the
2423
			 * beginning of the line. */
2424
2425
2426
			endmatch.rm_so += startmatch.rm_eo;
			endmatch.rm_eo += startmatch.rm_eo;
			/* There is an end on this line.  But does it
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2427
2428
			 * appear on this page, and is the match more
			 * than zero characters long? */
2429
			if (endmatch.rm_eo > startpos &&
2430
				endmatch.rm_eo > startmatch.rm_so) {
2431
			    paintlen = actual_x(converted + index,
2432
2433
				strnlenpt(fileptr->data,
				endmatch.rm_eo) - start - x_start);
2434
2435

			    assert(0 <= x_start && x_start < COLS);
2436

2437
2438
			    mvwaddnstr(edit, line, x_start, converted +
				index, paintlen);
2439
			}
2440
		    } else {
2441
2442
2443
			/* There is no end on this line.  But we haven't
			 * yet looked for one on later lines. */
			end_line = fileptr->next;
2444

2445
			while (end_line != NULL &&
2446
2447
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
2448
			    end_line = end_line->next;
2449

2450
			if (end_line != NULL) {
2451
			    assert(0 <= x_start && x_start < COLS);
2452

2453
2454
			    mvwaddnstr(edit, line, x_start, converted +
				index, -1);
2455
2456
2457
			    /* We painted to the end of the line, so
			     * don't bother checking any more starts. */
			    break;
2458
2459
			}
		    }
2460
		    start_col = startmatch.rm_so + 1;
2461
2462
		}
	    }
2463

2464
2465
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2466
	}
2467
    }
2468
#endif /* ENABLE_COLOR */
2469

2470
#ifndef NANO_SMALL
2471
    /* If the mark is on, we need to display it. */
2472
    if (openfile->mark_set && (fileptr->lineno <=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2473
	openfile->mark_begin->lineno || fileptr->lineno <=
2474
	openfile->current->lineno) && (fileptr->lineno >=
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2475
	openfile->mark_begin->lineno || fileptr->lineno >=
2476
	openfile->current->lineno)) {
2477
	/* fileptr is at least partially selected. */
2478
	const filestruct *top;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2479
	    /* Either current or mark_begin, whichever is first. */
2480
	size_t top_x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2481
	    /* current_x or mark_begin_x, corresponding to top. */
2482
2483
	const filestruct *bot;
	size_t bot_x;
2484
	int x_start;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2485
	    /* Starting column for mvwaddnstr().  Zero-based. */
2486
	int paintlen;
2487
	    /* Number of chars to paint on this line.  There are COLS
2488
	     * characters on a whole line. */
2489
	size_t index;
2490
	    /* Index in converted where we paint. */
2491

2492
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2493
2494
2495
2496
2497

	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
2498

2499
	/* The selected bit of fileptr is on this page. */
2500
2501
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2502
2503
2504
2505

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

	    if (bot_x >= endpos)
2508
2509
2510
2511
		/* If the end of the mark is off the page, paintlen is
		 * -1, meaning that everything on the line gets
		 * painted. */
		paintlen = -1;
2512
	    else
2513
2514
2515
		/* Otherwise, paintlen is the expanded location of the
		 * end of the mark minus the expanded location of the
		 * beginning of the mark. */
2516
2517
		paintlen = strnlenpt(fileptr->data, bot_x) -
			(x_start + start);
2518
2519
2520
2521
2522
2523
2524
2525

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

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

2529
	    index = actual_x(converted, x_start);
2530

2531
2532
2533
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

2534
	    wattron(edit, A_REVERSE);
2535
	    mvwaddnstr(edit, line, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2536
		paintlen);
2537
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
2538
	}
2539
    }
2540
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2541
2542
}

2543
/* Just update one line in the edit buffer.  This is basically a wrapper
2544
 * for edit_draw().
2545
 *
2546
2547
2548
 * If fileptr != current, then index is considered 0.  The line will be
 * displayed starting with fileptr->data[index].  Likely args are
 * current_x or 0. */
2549
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2550
{
2551
    int line;
2552
	/* The line in the edit window that we want to update. */
2553
2554
2555
2556
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2557

2558
    assert(fileptr != NULL);
2559

2560
    line = fileptr->lineno - openfile->edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2561

2562
2563
    /* We assume the line numbers are valid.  Is that really true? */
    assert(line < 0 || line == check_linenumbers(fileptr));
2564

2565
2566
    if (line < 0 || line >= editwinrows)
	return;
2567

2568
    /* First, blank out the line. */
2569
    blank_line(edit, line, 0, COLS);
2570

2571
2572
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2573
2574
    index = (fileptr == openfile->current) ? strnlenpt(fileptr->data,
	index) : 0;
2575
    page_start = get_page_start(index);
2576

2577
2578
    /* Expand the line, replacing tabs with spaces, and control
     * characters with their displayed forms. */
2579
    converted = display_string(fileptr->data, page_start, COLS, TRUE);
Chris Allegretta's avatar
Chris Allegretta committed
2580

2581
    /* Paint the line. */
2582
    edit_draw(fileptr, converted, line, page_start);
2583
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2584

2585
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2586
	mvwaddch(edit, line, 0, '$');
2587
    if (strlenpt(fileptr->data) > page_start + COLS)
2588
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
2589
2590
}

2591
2592
/* Return TRUE if we need an update after moving horizontally, and FALSE
 * otherwise.  We need one if the mark is on or if old_pww and
2593
 * placewewant are on different pages. */
2594
bool need_horizontal_update(size_t old_pww)
2595
2596
2597
{
    return
#ifndef NANO_SMALL
2598
	openfile->mark_set ||
2599
#endif
2600
2601
	get_page_start(old_pww) !=
	get_page_start(openfile->placewewant);
2602
2603
}

2604
2605
2606
2607
/* Return TRUE if we need an update after moving vertically, and FALSE
 * otherwise.  We need one if the mark is on or if old_pww and
 * placewewant are on different pages. */
bool need_vertical_update(size_t old_pww)
2608
2609
2610
{
    return
#ifndef NANO_SMALL
2611
	openfile->mark_set ||
2612
#endif
2613
2614
	get_page_start(old_pww) !=
	get_page_start(openfile->placewewant);
2615
2616
2617
2618
2619
}

/* 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
 * scrolling.  direction is the direction to scroll, either UP or DOWN,
2620
 * and nlines is the number of lines to scroll.  We change edittop, and
2621
2622
 * assume that current and current_x are up to date.  We also assume
 * that scrollok(edit) is FALSE. */
2623
void edit_scroll(scroll_dir direction, ssize_t nlines)
2624
{
2625
    bool do_redraw = need_vertical_update(0);
2626
    const filestruct *foo;
2627
    ssize_t i;
2628

2629
2630
    /* Don't bother scrolling less than one line. */
    if (nlines < 1)
2631
2632
	return;

2633
2634
2635
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

2636
    /* Move the top line of the edit window up or down (depending on the
2637
2638
     * value of direction) nlines lines, or as many lines as we can if
     * there are fewer than nlines lines available. */
2639
2640
    for (i = nlines; i > 0; i--) {
	if (direction == UP) {
2641
	    if (openfile->edittop->prev == NULL)
2642
		break;
2643
	    openfile->edittop = openfile->edittop->prev;
2644
	} else {
2645
	    if (openfile->edittop->next == NULL)
2646
		break;
2647
	    openfile->edittop = openfile->edittop->next;
2648
2649
2650
	}
    }

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2654
2655
2656
    /* 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
     * in the latter case, call edit_refresh() beforehand. */
2657
2658
2659
2660
2661
2662
2663
    if (nlines == 0)
	return;

    if (nlines >= editwinrows) {
	edit_refresh();
	return;
    }
2664
2665
2666

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2667
2668
2669
2670
    scrollok(edit, TRUE);
    wscrl(edit, (direction == UP) ? -nlines : nlines);
    scrollok(edit, FALSE);

2671
2672
2673
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

2674
2675
2676
2677
2678
    /* If the top or bottom line of the file is now visible in the edit
     * window, we need to draw the entire edit window. */
    if ((direction == UP && openfile->edittop == openfile->fileage) ||
	(direction == DOWN && openfile->edittop->lineno + editwinrows -
	1 >= openfile->filebot->lineno))
2679
	nlines = editwinrows;
2680

2681
2682
2683
2684
2685
    /* 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. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2686
    nlines += (nlines == 1) ? 1 : 2;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2687

2688
2689
    if (nlines > editwinrows)
	nlines = editwinrows;
2690
2691
2692

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

2695
2696
    /* If we scrolled down, move down to the line before the scrolled
     * region. */
2697
    if (direction == DOWN) {
2698
	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
2699
2700
2701
	    foo = foo->next;
    }

2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
    /* 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--) {
	if ((i == nlines && direction == DOWN) || (i == 1 &&
		direction == UP)) {
	    if (do_redraw)
		update_line(foo, (foo == openfile->current) ?
			openfile->current_x : 0);
	} else
	    update_line(foo, (foo == openfile->current) ?
2715
		openfile->current_x : 0);
2716
	foo = foo->next;
2717
2718
2719
2720
    }
}

/* Update any lines between old_current and current that need to be
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2721
 * updated.  Use this if we've moved without changing any text. */
2722
void edit_redraw(const filestruct *old_current, size_t old_pww)
2723
{
2724
    bool do_redraw = need_vertical_update(0) ||
2725
	need_vertical_update(old_pww);
2726
2727
    const filestruct *foo;

2728
2729
    /* If either old_current or current is offscreen, scroll the edit
     * window until it's onscreen and get out. */
2730
2731
2732
2733
2734
    if (old_current->lineno < openfile->edittop->lineno ||
	old_current->lineno >= openfile->edittop->lineno +
	editwinrows || openfile->current->lineno <
	openfile->edittop->lineno || openfile->current->lineno >=
	openfile->edittop->lineno + editwinrows) {
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
	filestruct *old_edittop = openfile->edittop;
	ssize_t nlines;

	/* 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. */
	edit_update(
#ifndef NANO_SMALL
		ISSET(SMOOTH_SCROLL) ? NONE :
#endif
		CENTER);

	nlines = openfile->edittop->lineno - old_edittop->lineno;

	openfile->edittop = old_edittop;

2751
2752
	/* Scroll the edit window up or down until edittop is in range
	 * of current. */
2753
2754
2755
2756
2757
	if (nlines < 0)
	    edit_scroll(UP, -nlines);
	else
	    edit_scroll(DOWN, nlines);

2758
2759
2760
	return;
    }

2761
2762
2763
    /* Update old_current and current if we're not on the first page
     * and/or we're not on the same page as before.  If the mark is on,
     * update all the lines between old_current and current too. */
2764
    foo = old_current;
2765

2766
    while (foo != openfile->current) {
2767
	if (do_redraw)
2768
	    update_line(foo, 0);
2769

2770
#ifndef NANO_SMALL
2771
	if (!openfile->mark_set)
2772
2773
#endif
	    break;
2774

2775
#ifndef NANO_SMALL
2776
2777
	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
		foo->next;
2778
#endif
2779
    }
2780

2781
    if (do_redraw)
2782
	update_line(openfile->current, openfile->current_x);
2783
2784
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2785
2786
/* 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
2787
2788
void edit_refresh(void)
{
2789
2790
    const filestruct *foo;
    int nlines;
2791

2792
2793
2794
    if (openfile->current->lineno < openfile->edittop->lineno ||
	openfile->current->lineno >= openfile->edittop->lineno +
	editwinrows)
2795
2796
	/* Put the top line of the edit window in range of the current
	 * line. */
2797
2798
	edit_update(
#ifndef NANO_SMALL
2799
		ISSET(SMOOTH_SCROLL) ? NONE :
2800
2801
#endif
		CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
2802

2803
2804
    foo = openfile->edittop;

2805
#ifdef DEBUG
2806
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
2807
#endif
2808

2809
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
2810
	update_line(foo, (foo == openfile->current) ?
2811
		openfile->current_x : 0);
2812
2813
2814
	foo = foo->next;
    }

2815
    for (; nlines < editwinrows; nlines++)
2816
2817
2818
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
2819
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2820
2821
}

2822
2823
2824
2825
/* 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. */
2826
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
2827
{
2828
    filestruct *foo = openfile->current;
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
    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)
	goal = editwinrows / 2;
    else {
	goal = openfile->current_y;
2843

2844
2845
2846
	/* Limit goal to (editwinrows - 1) lines maximum. */
	if (goal > editwinrows - 1)
	    goal = editwinrows - 1;
Chris Allegretta's avatar
Chris Allegretta committed
2847
    }
2848

2849
2850
2851
    for (; goal > 0 && foo->prev != NULL; goal--)
	foo = foo->prev;

2852
    openfile->edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
2853
2854
}

2855
2856
2857
/* Ask a simple yes/no question, specified in msg, on the statusbar.
 * Return 1 for Y, 0 for N, 2 for All (if all is TRUE when passed in)
 * and -1 for abort (^C). */
2858
int do_yesno(bool all, const char *msg)
Chris Allegretta's avatar
Chris Allegretta committed
2859
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2860
    int ok = -2, width = 16;
2861
2862
    const char *yesstr;		/* String of yes characters accepted. */
    const char *nostr;		/* Same for no. */
Chris Allegretta's avatar
Chris Allegretta committed
2863
    const char *allstr;		/* And all, surprise! */
2864
2865

    assert(msg != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
2866

2867
    /* yesstr, nostr, and allstr are strings of any length.  Each string
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2868
2869
2870
2871
2872
     * consists of all single-byte characters accepted as valid
     * characters for that value.  The first value will be the one
     * displayed in the shortcuts.  Translators: if possible, specify
     * both the shortcuts for your language and English.  For example,
     * in French: "OoYy" for "Oui". */
2873
2874
2875
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
2876
2877

    if (!ISSET(NO_HELP)) {
2878
	char shortstr[3];		/* Temp string for Y, N, A. */
2879

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2880
2881
2882
	if (COLS < 32)
	    width = COLS / 2;

2883
	/* Write the bottom of the screen. */
2884
	blank_bottombars();
2885

2886
	sprintf(shortstr, " %c", yesstr[0]);
2887
	wmove(bottomwin, 1, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2888
	onekey(shortstr, _("Yes"), width);
2889
2890

	if (all) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2891
	    wmove(bottomwin, 1, width);
2892
	    shortstr[1] = allstr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2893
	    onekey(shortstr, _("All"), width);
2894
2895
	}

2896
	wmove(bottomwin, 2, 0);
2897
	shortstr[1] = nostr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2898
	onekey(shortstr, _("No"), width);
2899

2900
	wmove(bottomwin, 2, 16);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2901
	onekey("^C", _("Cancel"), width);
Chris Allegretta's avatar
Chris Allegretta committed
2902
    }
2903

Chris Allegretta's avatar
Chris Allegretta committed
2904
    wattron(bottomwin, A_REVERSE);
2905
2906

    blank_statusbar();
2907
    mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1));
2908

Chris Allegretta's avatar
Chris Allegretta committed
2909
    wattroff(bottomwin, A_REVERSE);
2910

2911
2912
2913
    /* Refresh the edit window and the statusbar before getting
     * input. */
    wnoutrefresh(edit);
2914
    wnoutrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
2915

2916
    do {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2917
	int kbinput;
2918
	bool meta_key, func_key;
2919
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2920
	int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
2921
#endif
2922

2923
	kbinput = get_kbinput(bottomwin, &meta_key, &func_key);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2924

2925
	if (kbinput == NANO_REFRESH_KEY) {
2926
	    total_redraw();
2927
2928
	    continue;
	} else if (kbinput == NANO_CANCEL_KEY)
2929
	    ok = -1;
2930
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2931
	else if (kbinput == KEY_MOUSE) {
2932
	    get_mouseinput(&mouse_x, &mouse_y, FALSE);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2933
2934

	    if (mouse_x != -1 && mouse_y != -1 && !ISSET(NO_HELP) &&
2935
		wenclose(bottomwin, mouse_y, mouse_x) &&
2936
2937
		mouse_x < (width * 2) && mouse_y - (2 -
		no_more_space()) - editwinrows - 1 >= 0) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2938
		int x = mouse_x / width;
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
			/* Calculate the x-coordinate relative to the
			 * two columns of the Yes/No/All shortcuts in
			 * bottomwin. */
		int y = mouse_y - (2 - no_more_space()) -
			editwinrows - 1;
			/* Calculate the y-coordinate relative to the
			 * beginning of the Yes/No/All shortcuts in
			 * bottomwin, i.e, with the sizes of topwin,
			 * edit, and the first line of bottomwin
			 * subtracted out. */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2949
2950
2951

		assert(0 <= x && x <= 1 && 0 <= y && y <= 1);

2952
2953
		/* x == 0 means they clicked Yes or No.  y == 0 means
		 * Yes or All. */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2954
2955
2956
2957
2958
		ok = -2 * x * y + x - y + 1;

		if (ok == 2 && !all)
		    ok = -2;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
2959
	}
2960
#endif
2961
	/* Look for the kbinput in the yes, no and (optionally) all
2962
	 * strings. */
2963
2964
2965
2966
2967
2968
2969
2970
2971
	else if (strchr(yesstr, kbinput) != NULL)
	    ok = 1;
	else if (strchr(nostr, kbinput) != NULL)
	    ok = 0;
	else if (all && strchr(allstr, kbinput) != NULL)
	    ok = 2;
    } while (ok == -2);

    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
2972
2973
}

2974
void total_redraw(void)
2975
{
2976
2977
2978
2979
2980
2981
2982
2983
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 3: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
    wrefresh(curscr);
#endif
2984
2985
2986
2987
}

void total_refresh(void)
{
2988
    total_redraw();
2989
    titlebar(NULL);
2990
    edit_refresh();
2991
    bottombars(currshortcut);
2992
2993
2994
2995
2996
2997
2998
}

void display_main_list(void)
{
    bottombars(main_list);
}

2999
3000
3001
3002
3003
3004
/* 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. */
3005
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3006
{
3007
    filestruct *f;
3008
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3009
    size_t i, cur_xpt = xplustabs() + 1;
3010
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3011
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
3012

3013
    assert(openfile->fileage != NULL && openfile->current != NULL);
3014

3015
    f = openfile->current->next;
3016
    c = openfile->current->data[openfile->current_x];
3017
3018

    openfile->current->next = NULL;
3019
    openfile->current->data[openfile->current_x] = '\0';
3020
3021
3022

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

3023
    openfile->current->data[openfile->current_x] = c;
3024
    openfile->current->next = f;
3025

3026
    /* Check whether totsize is correct.  If it isn't, there is a bug
3027
     * somewhere. */
3028
    assert(openfile->current != openfile->filebot || i == openfile->totsize);
3029

3030
3031
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
3032
	return;
3033
    }
Chris Allegretta's avatar
Chris Allegretta committed
3034

3035
3036
    /* Display the current cursor position on the statusbar, and set 
     * disable_cursorpos to FALSE. */
3037
3038
    linepct = 100 * openfile->current->lineno /
	openfile->filebot->lineno;
3039
    colpct = 100 * cur_xpt / cur_lenpt;
3040
3041
    charpct = (openfile->totsize == 0) ? 0 : 100 * i /
	openfile->totsize;
3042
3043

    statusbar(
3044
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3045
	(long)openfile->current->lineno,
3046
	(long)openfile->filebot->lineno, linepct,
3047
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3048
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3049

3050
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
3051
3052
}

3053
void do_cursorpos_void(void)
3054
{
3055
    do_cursorpos(FALSE);
3056
3057
}

3058
3059
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3060
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3061
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3062
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3063

3064
    y = get_page_start(y) + COLS - y;
3065
	/* Now y is the number of columns that we can display on this
3066
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3067

3068
3069
3070
3071
3072
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3073
    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
3074

3075
    if (highlight)
Chris Allegretta's avatar
Chris Allegretta committed
3076
3077
	wattron(edit, A_REVERSE);

3078
#ifdef HAVE_REGEX_H
3079
3080
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3081
	waddch(edit, ' ');
3082
    else
3083
#endif
3084
	waddnstr(edit, word, actual_x(word, y));
3085
3086
3087

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

3089
    if (highlight)
Chris Allegretta's avatar
Chris Allegretta committed
3090
3091
3092
	wattroff(edit, A_REVERSE);
}

3093
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3094
#define CREDIT_LEN 54
3095
3096
#define XLCREDIT_LEN 8

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3097
3098
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
3099
3100
void do_credits(void)
{
3101
3102
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
3103
    int kbinput = ERR, crpos = 0, xlpos = 0;
3104
3105
3106
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3107
3108
	VERSION,
	"",
3109
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3110
3111
3112
3113
3114
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3115
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3116
	"David Benbennick",
3117
	"Mike Frysinger",
Chris Allegretta's avatar
Chris Allegretta committed
3118
3119
	"Ken Tyler",
	"Sven Guckes",
3120
	NULL,				/* credits[15], handled below. */
Chris Allegretta's avatar
Chris Allegretta committed
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3133
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
3134
3135
3136
3137
3138
3139
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3140
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3141
	"Linus Torvalds",
3142
	NULL,				/* "For ncurses:" */
3143
3144
3145
3146
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3147
3148
3149
3150
3151
3152
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3153
	"(c) 1999-2005 Chris Allegretta",
3154
3155
3156
3157
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3158
	"http://www.nano-editor.org/"
3159
3160
    };

3161
    const char *xlcredits[XLCREDIT_LEN] = {
3162
3163
3164
3165
3166
3167
3168
3169
	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!")
3170
    };
3171

3172
    /* credits[15]: Make sure this name is displayed properly, since we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3173
3174
     * can't dynamically assign it above, using Unicode 00F6 (Latin
     * Small Letter O with Diaresis) if applicable. */
3175
    credits[15] =
3176
#ifdef ENABLE_UTF8
3177
3178
3179
3180
	 ISSET(USE_UTF8) ? "Florian K\xC3\xB6nig" :
#endif
	"Florian K\xF6nig";

3181
3182
3183
3184
3185
3186
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3187
3188
    curs_set(0);
    nodelay(edit, TRUE);
3189

3190
    blank_titlebar();
3191
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3192
    blank_edit();
3193
3194
    blank_statusbar();
    blank_bottombars();
3195

3196
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3197
    wrefresh(edit);
3198
    wrefresh(bottomwin);
3199
    napms(700);
3200

3201
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3202
	if ((kbinput = wgetch(edit)) != ERR)
3203
	    break;
3204

3205
	if (crpos < CREDIT_LEN) {
3206
	    const char *what;
3207
3208
	    size_t start_x;

3209
	    if (credits[crpos] == NULL) {
3210
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3211

3212
		what = _(xlcredits[xlpos]);
3213
		xlpos++;
3214
	    } else
3215
		what = credits[crpos];
3216

3217
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3218
3219
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3220
	}
3221

3222
3223
3224
3225
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3226
	napms(700);
3227

3228
	scrollok(edit, TRUE);
3229
	wscrl(edit, 1);
3230
	scrollok(edit, FALSE);
3231
	wrefresh(edit);
3232

3233
	if ((kbinput = wgetch(edit)) != ERR)
3234
	    break;
3235
	napms(700);
3236

3237
	scrollok(edit, TRUE);
3238
	wscrl(edit, 1);
3239
	scrollok(edit, FALSE);
3240
	wrefresh(edit);
3241
3242
    }

3243
3244
3245
    if (kbinput != ERR)
	ungetch(kbinput);

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

3252
    curs_set(1);
3253
    nodelay(edit, FALSE);
3254

3255
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3256
}
3257
#endif