winio.c 89.9 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
2545
 * for edit_draw().  The line will be displayed starting with
 * fileptr->data[index].  Likely arguments are current_x or zero. */
2546
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2547
{
2548
    int line;
2549
	/* The line in the edit window that we want to update. */
2550
2551
2552
2553
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2554

2555
    assert(fileptr != NULL);
2556

2557
    line = fileptr->lineno - openfile->edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2558

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

2562
2563
    if (line < 0 || line >= editwinrows)
	return;
2564

2565
    /* First, blank out the line. */
2566
    blank_line(edit, line, 0, COLS);
2567

2568
2569
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2570
    index = strnlenpt(fileptr->data, index);
2571
    page_start = get_page_start(index);
2572

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

2577
    /* Paint the line. */
2578
    edit_draw(fileptr, converted, line, page_start);
2579
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2580

2581
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2582
	mvwaddch(edit, line, 0, '$');
2583
    if (strlenpt(fileptr->data) > page_start + COLS)
2584
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
2585
2586
}

2587
2588
/* 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
2589
 * placewewant are on different pages. */
2590
bool need_horizontal_update(size_t old_pww)
2591
2592
2593
{
    return
#ifndef NANO_SMALL
2594
	openfile->mark_set ||
2595
#endif
2596
2597
	get_page_start(old_pww) !=
	get_page_start(openfile->placewewant);
2598
2599
}

2600
2601
2602
2603
/* 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)
2604
2605
2606
{
    return
#ifndef NANO_SMALL
2607
	openfile->mark_set ||
2608
#endif
2609
2610
	get_page_start(old_pww) !=
	get_page_start(openfile->placewewant);
2611
2612
2613
2614
2615
}

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

2625
2626
    /* Don't bother scrolling less than one line. */
    if (nlines < 1)
2627
2628
	return;

2629
2630
2631
    /* Part 1: nlines is the number of lines we're going to scroll the
     * text of the edit window. */

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2650
2651
2652
    /* 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. */
2653
2654
2655
2656
2657
2658
2659
    if (nlines == 0)
	return;

    if (nlines >= editwinrows) {
	edit_refresh();
	return;
    }
2660
2661
2662

    /* Scroll the text of the edit window up or down nlines lines,
     * depending on the value of direction. */
2663
2664
2665
2666
    scrollok(edit, TRUE);
    wscrl(edit, (direction == UP) ? -nlines : nlines);
    scrollok(edit, FALSE);

2667
2668
2669
    /* Part 2: nlines is the number of lines in the scrolled region of
     * the edit window that we need to draw. */

2670
2671
2672
2673
2674
    /* 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))
2675
	nlines = editwinrows;
2676

2677
2678
2679
2680
2681
    /* 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
2682
    nlines += (nlines == 1) ? 1 : 2;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2683

2684
2685
    if (nlines > editwinrows)
	nlines = editwinrows;
2686
2687
2688

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

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

2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
    /* 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) ?
2711
		openfile->current_x : 0);
2712
	foo = foo->next;
2713
2714
2715
2716
    }
}

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

2724
2725
    /* If either old_current or current is offscreen, scroll the edit
     * window until it's onscreen and get out. */
2726
2727
2728
2729
2730
    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) {
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
	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;

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

2754
2755
2756
	return;
    }

2757
2758
2759
    /* 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. */
2760
    foo = old_current;
2761

2762
    while (foo != openfile->current) {
2763
	if (do_redraw)
2764
	    update_line(foo, 0);
2765

2766
#ifndef NANO_SMALL
2767
	if (!openfile->mark_set)
2768
2769
#endif
	    break;
2770

2771
#ifndef NANO_SMALL
2772
2773
	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
		foo->next;
2774
#endif
2775
    }
2776

2777
    if (do_redraw)
2778
	update_line(openfile->current, openfile->current_x);
2779
2780
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2781
2782
/* 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
2783
2784
void edit_refresh(void)
{
2785
2786
    const filestruct *foo;
    int nlines;
2787

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

2799
2800
    foo = openfile->edittop;

2801
#ifdef DEBUG
2802
    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
2803
#endif
2804

2805
    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
2806
	update_line(foo, (foo == openfile->current) ?
2807
		openfile->current_x : 0);
2808
2809
2810
	foo = foo->next;
    }

2811
    for (; nlines < editwinrows; nlines++)
2812
2813
2814
	blank_line(edit, nlines, 0, COLS);

    reset_cursor();
2815
    wnoutrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2816
2817
}

2818
2819
2820
2821
/* 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. */
2822
void edit_update(update_type location)
Chris Allegretta's avatar
Chris Allegretta committed
2823
{
2824
    filestruct *foo = openfile->current;
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
    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;
2839

2840
2841
2842
	/* Limit goal to (editwinrows - 1) lines maximum. */
	if (goal > editwinrows - 1)
	    goal = editwinrows - 1;
Chris Allegretta's avatar
Chris Allegretta committed
2843
    }
2844

2845
    for (; goal > 0 && foo != openfile->edittop; goal--)
2846
2847
	foo = foo->prev;

2848
    openfile->edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
2849
2850
}

2851
void total_redraw(void)
2852
{
2853
2854
2855
2856
2857
2858
2859
2860
#ifdef USE_SLANG
    /* Slang curses emulation brain damage, part 3: Slang doesn't define
     * curscr. */
    SLsmg_touch_screen();
    SLsmg_refresh();
#else
    wrefresh(curscr);
#endif
2861
2862
2863
2864
}

void total_refresh(void)
{
2865
    total_redraw();
2866
    titlebar(NULL);
2867
    edit_refresh();
2868
    bottombars(currshortcut);
2869
2870
2871
2872
2873
2874
2875
}

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

2876
2877
2878
2879
2880
2881
/* 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. */
2882
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
2883
{
2884
    filestruct *f;
2885
    char c;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2886
    size_t i, cur_xpt = xplustabs() + 1;
2887
    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
2888
    int linepct, colpct, charpct;
Chris Allegretta's avatar
Chris Allegretta committed
2889

2890
    assert(openfile->fileage != NULL && openfile->current != NULL);
2891

2892
    f = openfile->current->next;
2893
    c = openfile->current->data[openfile->current_x];
2894
2895

    openfile->current->next = NULL;
2896
    openfile->current->data[openfile->current_x] = '\0';
2897
2898
2899

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

2900
    openfile->current->data[openfile->current_x] = c;
2901
    openfile->current->next = f;
2902

2903
    /* Check whether totsize is correct.  If it isn't, there is a bug
2904
     * somewhere. */
2905
    assert(openfile->current != openfile->filebot || i == openfile->totsize);
2906

2907
2908
    if (constant && disable_cursorpos) {
	disable_cursorpos = FALSE;
2909
	return;
2910
    }
Chris Allegretta's avatar
Chris Allegretta committed
2911

2912
2913
    /* Display the current cursor position on the statusbar, and set 
     * disable_cursorpos to FALSE. */
2914
2915
    linepct = 100 * openfile->current->lineno /
	openfile->filebot->lineno;
2916
    colpct = 100 * cur_xpt / cur_lenpt;
2917
2918
    charpct = (openfile->totsize == 0) ? 0 : 100 * i /
	openfile->totsize;
2919
2920

    statusbar(
2921
	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
2922
	(long)openfile->current->lineno,
2923
	(long)openfile->filebot->lineno, linepct,
2924
	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
2925
	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
2926

2927
    disable_cursorpos = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
2928
2929
}

2930
void do_cursorpos_void(void)
2931
{
2932
    do_cursorpos(FALSE);
2933
2934
}

2935
2936
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
2937
void do_replace_highlight(bool highlight, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
2938
{
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2939
    size_t y = xplustabs(), word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
2940

2941
    y = get_page_start(y) + COLS - y;
2942
	/* Now y is the number of columns that we can display on this
2943
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
2944

2945
2946
2947
2948
2949
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
2950
    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
2951

2952
    if (highlight)
Chris Allegretta's avatar
Chris Allegretta committed
2953
2954
	wattron(edit, A_REVERSE);

2955
#ifdef HAVE_REGEX_H
2956
2957
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2958
	waddch(edit, ' ');
2959
    else
2960
#endif
2961
	waddnstr(edit, word, actual_x(word, y));
2962
2963
2964

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

2966
    if (highlight)
Chris Allegretta's avatar
Chris Allegretta committed
2967
2968
2969
	wattroff(edit, A_REVERSE);
}

2970
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2971
#define CREDIT_LEN 54
2972
2973
#define XLCREDIT_LEN 8

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2974
2975
/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
 * are FALSE. */
2976
2977
void do_credits(void)
{
2978
2979
    bool old_more_space = ISSET(MORE_SPACE);
    bool old_no_help = ISSET(NO_HELP);
2980
    int kbinput = ERR, crpos = 0, xlpos = 0;
2981
2982
2983
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
2984
2985
	VERSION,
	"",
2986
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
2987
2988
2989
2990
2991
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
2992
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2993
	"David Benbennick",
2994
	"Mike Frysinger",
Chris Allegretta's avatar
Chris Allegretta committed
2995
2996
	"Ken Tyler",
	"Sven Guckes",
2997
	NULL,				/* credits[15], handled below. */
Chris Allegretta's avatar
Chris Allegretta committed
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3010
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
3011
3012
3013
3014
3015
3016
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3017
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3018
	"Linus Torvalds",
3019
	NULL,				/* "For ncurses:" */
3020
3021
3022
3023
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3024
3025
3026
3027
3028
3029
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3030
	"(c) 1999-2005 Chris Allegretta",
3031
3032
3033
3034
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3035
	"http://www.nano-editor.org/"
3036
3037
    };

3038
    const char *xlcredits[XLCREDIT_LEN] = {
3039
3040
3041
3042
3043
3044
3045
3046
	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!")
3047
    };
3048

3049
    /* credits[15]: Make sure this name is displayed properly, since we
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3050
3051
     * can't dynamically assign it above, using Unicode 00F6 (Latin
     * Small Letter O with Diaresis) if applicable. */
3052
    credits[15] =
3053
#ifdef ENABLE_UTF8
3054
3055
3056
3057
	 ISSET(USE_UTF8) ? "Florian K\xC3\xB6nig" :
#endif
	"Florian K\xF6nig";

3058
3059
3060
3061
3062
3063
    if (!old_more_space || !old_no_help) {
	SET(MORE_SPACE);
	SET(NO_HELP);
	window_init();
    }

3064
3065
    curs_set(0);
    nodelay(edit, TRUE);
3066

3067
    blank_titlebar();
3068
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
3069
    blank_edit();
3070
3071
    blank_statusbar();
    blank_bottombars();
3072

3073
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3074
    wrefresh(edit);
3075
    wrefresh(bottomwin);
3076
    napms(700);
3077

3078
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3079
	if ((kbinput = wgetch(edit)) != ERR)
3080
	    break;
3081

3082
	if (crpos < CREDIT_LEN) {
3083
	    const char *what;
3084
3085
	    size_t start_x;

3086
	    if (credits[crpos] == NULL) {
3087
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3088

3089
		what = _(xlcredits[xlpos]);
3090
		xlpos++;
3091
	    } else
3092
		what = credits[crpos];
3093

3094
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3095
3096
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);
3097
	}
3098

3099
3100
3101
3102
	wrefresh(edit);

	if ((kbinput = wgetch(edit)) != ERR)
	    break;
3103
	napms(700);
3104

3105
	scrollok(edit, TRUE);
3106
	wscrl(edit, 1);
3107
	scrollok(edit, FALSE);
3108
	wrefresh(edit);
3109

3110
	if ((kbinput = wgetch(edit)) != ERR)
3111
	    break;
3112
	napms(700);
3113

3114
	scrollok(edit, TRUE);
3115
	wscrl(edit, 1);
3116
	scrollok(edit, FALSE);
3117
	wrefresh(edit);
3118
3119
    }

3120
3121
3122
    if (kbinput != ERR)
	ungetch(kbinput);

3123
3124
3125
3126
3127
3128
    if (!old_more_space || !old_no_help) {
	UNSET(MORE_SPACE);
	UNSET(NO_HELP);
	window_init();
    }

3129
    curs_set(1);
3130
    nodelay(edit, FALSE);
3131

3132
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3133
}
3134
#endif