winio.c 102 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-2004 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
11
12
13
14
15
16
17
18
19
20
21
 *   any later version.                                                   *
 *                                                                        *
 *   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.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
#include <stdarg.h>
#include <string.h>
26
#include <stdlib.h>
27
#include <unistd.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
28
#include <ctype.h>
29
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
30
31
32
#include "proto.h"
#include "nano.h"

33
34
35
static int statusblank = 0;	/* Number of keystrokes left after
				 * we call statusbar(), before we
				 * actually blank the statusbar. */
36
37
38
static bool resetstatuspos = FALSE;
				/* Should we reset the cursor position
				 * at the statusbar prompt? */
39

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

102
103
104
105
#ifndef NANO_SMALL
/* Reset all the input routines that rely on character sequences. */
void reset_kbinput(void)
{
106
    get_translated_kbinput(0, NULL, TRUE);
107
108
109
110
111
    get_ascii_kbinput(0, 0, TRUE);
    get_untranslated_kbinput(0, 0, FALSE, TRUE);
}
#endif

112
113
/* Put back the input character stored in kbinput.  If meta_key is TRUE,
 * put back the Escape character after putting back kbinput. */
114
void unget_kbinput(int kbinput, bool meta_key, bool func_key)
115
{
116
117
    /* If this character is outside the ASCII range and func_key is
     * FALSE, treat it as a wide character and put back its equivalent
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
118
     * UTF-8 sequence. */
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    if (kbinput > 255 && !func_key)
    {
	int i;
	char *s = charalloc(MB_CUR_MAX + 1);
	wchar_t wc = (wchar_t)kbinput;

	i = wctomb(s, wc);

	if (i == -1)
	    /* This wide character is unrecognized.  Send it back. */
	    ungetch(kbinput);
	else {
	    for (; i > 0; i--)
		ungetch(s[i - 1]);
	}
	free(s);
    } else
	ungetch(kbinput);
137
138
139
140
    if (meta_key)
	ungetch(NANO_CONTROL_3);
}

141
/* Read in a single input character.  If it's ignored, swallow it and go
142
 * on.  Otherwise, try to translate it from ASCII, extended keypad
143
 * values, and/or escape sequences.  Set meta_key to TRUE when we get a
144
145
 * meta sequence, and set func_key to TRUE when we get an extended
 * keypad sequence.  Supported extended keypad values consist of [arrow
146
147
 * key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace, the
 * editing keypad (Insert, Delete, Home, End, PageUp, and PageDown), the
148
 * function keypad (F1-F16), and the numeric keypad with NumLock off.
149
 * Assume nodelay(win) is FALSE. */
150
int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
151
{
152
    int kbinput, retval = ERR;
153
    seq_type seq;
154

155
156
157
158
#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
#endif

159
    *meta_key = FALSE;
160
    *func_key = FALSE;
161

162
163
164
165
166
167
    while (retval == ERR) {
	/* Read a character using blocking input, since using
	 * non-blocking input will eat up all unused CPU.  Then pass it
	 * to get_translated_kbinput().  Continue until we get a
	 * complete sequence. */
	kbinput = wgetch(win);
168
	retval = get_translated_kbinput(kbinput, &seq
169
#ifndef NANO_SMALL
170
		, FALSE
171
#endif
172
		);
173

174
175
176
177
178
179
	/* If we have a complete sequence now, we should interpret its
	 * translated value instead of the value of the last character
	 * we got. */
	if (retval != ERR)
	    kbinput = retval;

180
181
182
	/* If we got a one-character sequence and it's outside the ASCII
	 * range, set func_key to TRUE. */
	if (seq == NO_SEQ) {
183
	    if (kbinput > 255)
184
185
186
187
188
189
		*func_key = TRUE;
	/* If we got a multi-character sequence, read it in, including
	 * the initial character, as verbatim input. */
	} else {
	    int *sequence = NULL;
	    size_t seq_len;
190

191
192
	    sequence = get_verbatim_kbinput(win, kbinput, sequence,
		&seq_len, FALSE);
193

194
195
196
197
198
199
200
201
202
	    /* Handle escape sequences. */
	    if (seq == ESCAPE_SEQ) {
		/* If the escape sequence is one character long, set
		 * meta_key to TRUE, make the sequence character
		 * lowercase, and save that as the result. */
		if (seq_len == 1) {
		    *meta_key = TRUE;
		    retval = tolower(kbinput);
		/* If the escape sequence is more than one character
203
		 * long, set func_key to TRUE, translate the escape
204
205
206
207
		 * sequence into the corresponding key value, and save
		 * that as the result. */
		} else if (seq_len > 1) {
		    bool ignore_seq;
208

209
		    *func_key = TRUE;
210
		    retval = get_escape_seq_kbinput(sequence, seq_len,
211
212
			&ignore_seq);

213
214
215
216
		    if (retval == ERR && !ignore_seq) {
			/* This escape sequence is unrecognized.  Send
			 * it back. */
			for (; seq_len > 1; seq_len--)
217
218
			    unget_kbinput(sequence[seq_len - 1], FALSE,
				FALSE);
219
220
			retval = sequence[0];
		    }
221
		}
222
223
	    /* Handle UTF-8 sequences. */
	    } else if (seq == UTF8_SEQ) {
224
225
226
		/* If we have a UTF-8 sequence, translate the UTF-8
		 * sequence into the corresponding wide character value,
		 * and save that as the result. */
227
228
229
230
231
232
233
234
235
236
237
238
		int i = 0;
		char *s = charalloc(seq_len + 1);
		wchar_t wc;

		for (; i < seq_len; i++)
		    s[i] = (char)sequence[i];
		s[seq_len] = '\0';

		if (mbtowc(&wc, s, MB_CUR_MAX) == -1) {
		    /* This UTF-8 sequence is unrecognized.  Send it
		     * back. */
		    for (; seq_len > 1; seq_len--)
239
240
			unget_kbinput(sequence[seq_len - 1], FALSE,
				FALSE);
241
242
243
		    retval = sequence[0];
		} else
		    retval = wc;
244
		free(s);
245
	    }
246
	    free(sequence);
247
248
249
	}
    }

250
#ifdef DEBUG
251
    fprintf(stderr, "get_kbinput(): kbinput = %d, meta_key = %d, func_key = %d\n", kbinput, (int)*meta_key, (int)*func_key);
252
#endif
253

254
255
256
257
#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif

258
    return retval;
259
260
}

261
262
263
264
265
/* Translate acceptable ASCII, extended keypad values, and escape and
 * UTF-8 sequences into their corresponding key values.  Set seq to
 * ESCAPE_SEQ when we get an escape sequence, or UTF8_SEQ when we get a
 * UTF-8 sequence.  Assume nodelay(win) is FALSE. */
int get_translated_kbinput(int kbinput, seq_type *seq
266
#ifndef NANO_SMALL
267
	, bool reset
268
269
#endif
	)
270
{
271
272
    static int escapes = 0;
    static size_t ascii_digits = 0;
273
    int retval = ERR;
274

275
276
277
278
279
#ifndef NANO_SMALL
    if (reset) {
	escapes = 0;
	ascii_digits = 0;
	return ERR;
280
    }
281
#endif
282

283
    *seq = NO_SEQ;
284
285

    switch (kbinput) {
286
287
288
289
290
291
292
293
294
295
	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. */
296
297
		    break;
		default:
298
299
300
		    /* More than two escapes: reset the escape counter
		     * and wait for more input. */
		    escapes = 0;
301
302
	    }
	    break;
303
304
305
306
307
#if !defined(NANO_SMALL) && defined(KEY_RESIZE)
	/* 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:
308
	    break;
309
310
311
312
313
314
315
316
#endif
#ifdef PDCURSES
	case KEY_SHIFT_L:
	case KEY_SHIFT_R:
	case KEY_CONTROL_L:
	case KEY_CONTROL_R:
	case KEY_ALT_L:
	case KEY_ALT_R:
317
	    break;
318
#endif
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
	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;
339
#ifdef KEY_HOME
340
341
342
343
			/* HP-UX 10 and 11 don't support KEY_HOME. */
			case KEY_HOME:
			    retval = NANO_HOME_KEY;
			    break;
344
#endif
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
			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;
364
365
366
367
368
369
370
371
			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;
372
			case KEY_B2:	/* Center (5) on numeric keypad
373
374
375
376
377
378
379
380
381
382
					 * 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;
383
#ifdef KEY_BEG
384
385
			/* Slang doesn't support KEY_BEG. */
			case KEY_BEG:	/* Center (5) on numeric keypad
386
					 * with NumLock off. */
387
			    break;
388
#endif
389
#ifdef KEY_END
390
391
392
393
			/* HP-UX 10 and 11 don't support KEY_END. */
			case KEY_END:
			    retval = NANO_END_KEY;
			    break;
394
395
#endif
#ifdef KEY_SUSPEND
396
397
398
399
			/* Slang doesn't support KEY_SUSPEND. */
			case KEY_SUSPEND:
			    retval = NANO_SUSPEND_KEY;
			    break;
400
401
#endif
#ifdef KEY_SLEFT
402
403
404
405
			/* Slang doesn't support KEY_SLEFT. */
			case KEY_SLEFT:
			    retval = NANO_BACK_KEY;
			    break;
406
407
#endif
#ifdef KEY_SRIGHT
408
409
410
411
			/* Slang doesn't support KEY_SRIGHT. */
			case KEY_SRIGHT:
			    retval = NANO_FORWARD_KEY;
			    break;
412
#endif
413
414
415
416
417
418
419
420
			default:
			    retval = kbinput;
			    break;
		    }
		    break;
		case 1:
		    /* One escape followed by a non-escape: escape
		     * sequence mode.  Reset the escape counter and set
421
		     * seq to ESCAPE_SEQ. */
422
		    escapes = 0;
423
		    *seq = ESCAPE_SEQ;
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
462
463
464
465
466
		    break;
		case 2:
		    switch (kbinput) {
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			    /* Two escapes followed by one or more
			     * digits: ASCII character sequence mode.
			     * If the digit sequence's range is limited
			     * to 2XX (the first digit is in the '0' to
			     * '2' range and it's the first digit, or if
			     * it's in the full digit range and it's not
			     * the first digit), increment the ASCII
			     * digit counter and interpret the digit.
			     * If the digit sequence's range is not
			     * limited to 2XX, fall through. */
			    if (kbinput <= '2' || ascii_digits > 0) {
				ascii_digits++;
				kbinput = get_ascii_kbinput(kbinput,
					ascii_digits
#ifndef NANO_SMALL
					, FALSE
#endif
					);

				if (kbinput != ERR) {
				    /* If we've read in a complete ASCII
				     * digit sequence, reset the ASCII
				     * digit counter and the escape
				     * counter and save the corresponding
				     * ASCII character as the result. */
				    ascii_digits = 0;
				    escapes = 0;
				    retval = kbinput;
				}
			    }
467
			    break;
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
			default:
			    /* Reset the escape counter. */
			    escapes = 0;
			    if (ascii_digits == 0)
				/* Two escapes followed by a non-digit
				 * or a digit that would create an ASCII
				 * digit sequence greater than 2XX, and
				 * we're not in the middle of an ASCII
				 * character 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 were in the middle of an ASCII
				 * character sequence, reset the ASCII
				 * digit counter and save the character
				 * we got as the result. */
				ascii_digits = 0;
				retval = kbinput;
			    }
		    }
	    }
491
    }
492

493
494
495
496
497
    /* A character other than ERR with its high bit set: UTF-8 sequence
     * mode.  Set seq to UTF8_SEQ. */
    if (retval != ERR && 127 < retval && retval <= 255)
	*seq = UTF8_SEQ;

498
#ifdef DEBUG
499
    fprintf(stderr, "get_translated_kbinput(): kbinput = %d, seq = %d, escapes = %d, ascii_digits = %lu, retval = %d\n", kbinput, (int)*seq, escapes, (unsigned long)ascii_digits, retval);
500
#endif
501
502
503

    /* Return the result. */
    return retval;
504
505
}

506
507
508
509
/* Translate an ASCII character sequence: turn a three-digit decimal
 * ASCII code from 000-255 into its corresponding ASCII character. */
int get_ascii_kbinput(int kbinput, size_t ascii_digits
#ifndef NANO_SMALL
510
	, bool reset
511
512
#endif
	)
513
{
514
515
    static int ascii_kbinput = 0;
    int retval = ERR;
516

517
518
519
520
#ifndef NANO_SMALL
    if (reset) {
	ascii_kbinput = 0;
	return ERR;
521
    }
522
#endif
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
    switch (ascii_digits) {
	case 1:
	    /* Read in the first of the three ASCII digits. */
	    switch (kbinput) {
		/* Add the digit we got to the 100's position of the
		 * ASCII character sequence holder. */
		case '0':
		case '1':
		case '2':
		    ascii_kbinput += (kbinput - '0') * 100;
		    break;
		default:
		    retval = kbinput;
    	    }
538
	    break;
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
	case 2:
	    /* Read in the second of the three ASCII digits. */
	    switch (kbinput) {
		/* Add the digit we got to the 10's position of the
		 * ASCII character sequence holder. */
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		    ascii_kbinput += (kbinput - '0') * 10;
		    break;
		case '6':
		case '7':
		case '8':
		case '9':
		    if (ascii_kbinput < 200) {
			ascii_kbinput += (kbinput - '0') * 10;
			break;
		    }
		default:
		    retval = kbinput;
562
563
	    }
	    break;
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
	case 3:
	    /* Read in the third of the three ASCII digits. */
	    switch (kbinput) {
		/* Add the digit we got to the 1's position of the ASCII
		 * character sequence holder, and save the corresponding
		 * ASCII character as the result. */
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		    ascii_kbinput += (kbinput - '0');
		    retval = ascii_kbinput;
		    break;
		case '6':
		case '7':
		case '8':
		case '9':
		    if (ascii_kbinput < 250) {
			ascii_kbinput += (kbinput - '0');
			retval = ascii_kbinput;
			break;
		    }
		default:
		    retval = kbinput;
590
	    }
591
	    break;
592
593
594
    }

#ifdef DEBUG
595
    fprintf(stderr, "get_ascii_kbinput(): kbinput = %d, ascii_digits = %lu, ascii_kbinput = %d, retval = %d\n", kbinput, (unsigned long)ascii_digits, ascii_kbinput, retval);
596
#endif
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633

    /* If the result is an ASCII character, reset the ASCII character
     * sequence holder. */
    if (retval != ERR)
	ascii_kbinput = 0;

    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 = ERR;

    /* We don't handle Ctrl-2 here, since Esc Esc 2 could be the first
     * part of an ASCII character sequence. */

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

#ifdef DEBUG
634
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
635
636
#endif

637
638
639
    return retval;
}

640
/* Translate escape sequences, most of which correspond to extended
641
 * keypad values, nto their corresponding key values.  These sequences
642
643
644
645
646
 * 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. */
647
int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool
648
	*ignore_seq)
649
{
650
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
651

652
653
    *ignore_seq = FALSE;

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

1142
#ifdef DEBUG
1143
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d, ignore_seq = %d\n", retval, (int)*ignore_seq);
1144
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1145

1146
    return retval;
1147
1148
}

1149
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1150
 * letters A (up), B (down), C (right), and D (left).  These are common
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
 * 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;
    }
}

1168
/* Read in a string of input characters (e.g. an escape sequence)
1169
1170
1171
1172
1173
 * verbatim.  If first isn't ERR, make it the first character of the
 * string.  Store the string in v_kbinput and return the length of the
 * string in v_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, int first, int *v_kbinput, size_t
	*v_len, bool allow_ascii)
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
{
    int kbinput;
    size_t i = 0, v_newlen = 0;

#ifndef NANO_SMALL
    allow_pending_sigwinch(TRUE);
#endif

    *v_len = 0;
    v_kbinput = (int *)nmalloc(sizeof(int));

    /* Turn off flow control characters if necessary so that we can type
     * them in verbatim, and turn the keypad off so that we don't get
     * extended keypad values outside the ASCII range. */
    if (ISSET(PRESERVE))
	disable_flow_control();
    keypad(win, FALSE);

1192
1193
1194
    /* If first is ERR, read the first character using blocking input,
     * since using non-blocking input will eat up all unused CPU.
     * Otherwise, treat first as the first character.  Then increment
1195
     * v_len and save the character in v_kbinput. */
1196
1197
1198
1199
    if (first == ERR)
	kbinput = wgetch(win);
    else
	kbinput = first;
1200
1201
1202
    (*v_len)++;
    v_kbinput[0] = kbinput;
#ifdef DEBUG
1203
    fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len);
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
#endif

    /* Read any following characters using non-blocking input, until
     * there aren't any left to be read, and save the complete string of
     * characters in v_kbinput, incrementing v_len accordingly.  We read
     * them all at once in order to minimize the chance that there might
     * be a delay greater than nodelay() provides for between them, in
     * which case we'll stop before all of them are read. */
    nodelay(win, TRUE);
    while ((kbinput = wgetch(win)) != ERR) {
	(*v_len)++;
	v_kbinput = (int *)nrealloc(v_kbinput, *v_len * sizeof(int));
	v_kbinput[*v_len - 1] = kbinput;
#ifdef DEBUG
1218
	fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len);
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
#endif
    }
    nodelay(win, FALSE);

    /* Pass the string of characters to get_untranslated_kbinput(), one
     * by one, so it can handle them as ASCII character sequences and/or
     * escape sequences.  Filter out ERR's from v_kbinput in the
     * process; they shouldn't occur in the string of characters unless
     * we're reading an incomplete sequence, in which case we only want
     * to keep the complete sequence. */
    for (; i < *v_len; i++) {
	v_kbinput[v_newlen] = get_untranslated_kbinput(v_kbinput[i], i,
		allow_ascii
#ifndef NANO_SMALL
		, FALSE
#endif
		);
	if (v_kbinput[i] != ERR && v_kbinput[v_newlen] != ERR)
	    v_newlen++;
    }

    if (v_newlen == 0) {
	/* If there were no characters after the ERR's were filtered
	 * out, set v_len and reallocate v_kbinput to account for
	 * one character, and set that character to ERR. */
	*v_len = 1;
	v_kbinput = (int *)nrealloc(v_kbinput, sizeof(int));
	v_kbinput[0] = ERR;
    } else if (v_newlen != *v_len) {
	/* If there were fewer characters after the ERR's were filtered
	 * out, set v_len and reallocate v_kbinput to account for
	 * the new number of characters. */
	*v_len = v_newlen;
	v_kbinput = (int *)nrealloc(v_kbinput, *v_len * sizeof(int));
    }

    /* If allow_ascii is TRUE and v_kbinput[0] is ERR, we need to
     * complete an ASCII character sequence.  Keep reading in characters
     * using blocking input until we get a complete sequence. */
    if (allow_ascii && v_kbinput[0] == ERR) {
	while (v_kbinput[0] == ERR) {
	    kbinput = wgetch(win);
	    v_kbinput[0] = get_untranslated_kbinput(kbinput, i,
		allow_ascii
#ifndef NANO_SMALL
		, FALSE
#endif
		);
	    i++;
	}
    }

    /* Turn flow control characters back on if necessary and turn the
     * keypad back on now that we're done. */
    if (ISSET(PRESERVE))
	enable_flow_control();
    keypad(win, TRUE);

#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif

    return v_kbinput;
}

1284
int get_untranslated_kbinput(int kbinput, size_t position, bool
1285
1286
	allow_ascii
#ifndef NANO_SMALL
1287
	, bool reset
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
#endif
	)
{
    static size_t ascii_digits = 0;
    int retval;

#ifndef NANO_SMALL
    if (reset) {
	ascii_digits = 0;
	return ERR;
    }
#endif

    if (allow_ascii) {
	/* position is equal to the number of ASCII digits we've read so
	 * far, and kbinput is a digit from '0' to '9': ASCII character
	 * sequence mode.  If the digit sequence's range is limited to
	 * 2XX (the first digit is in the '0' to '2' range and it's the
	 * first digit, or if it's in the full digit range and it's not
	 * the first digit), increment the ASCII digit counter and
	 * interpret the digit.  If the digit sequence's range is not
	 * limited to 2XX, fall through. */
	if (position == ascii_digits && kbinput >= '0' && kbinput <= '9') {
	    if (kbinput <= '2' || ascii_digits > 0) {
		ascii_digits++;
		kbinput = get_ascii_kbinput(kbinput, ascii_digits
#ifndef NANO_SMALL
			, FALSE
#endif
			);
		if (kbinput != ERR)
		    /* If we've read in a complete ASCII digit sequence,
		     * reset the ASCII digit counter. */
		    ascii_digits = 0;
	    }
	} else if (ascii_digits > 0)
	    /* position is not equal to the number of ASCII digits we've
	     * read or kbinput is a non-digit, and we're in the middle
	     * of an ASCII character sequence.  Reset the ASCII digit
	     * counter. */
	    ascii_digits = 0;
    }

    /* Save the corresponding ASCII character as the result if we've
     * read in a complete ASCII digit sequence, or the passed-in
     * character if we haven't. */
     retval = kbinput;

#ifdef DEBUG
1337
    fprintf(stderr, "get_untranslated_kbinput(): kbinput = %d, position = %lu, ascii_digits = %lu\n", kbinput, (unsigned long)position, (unsigned long)ascii_digits);
1338
1339
1340
1341
1342
#endif

    return retval;
}

1343
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1344
1345
/* 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,
1346
1347
 * assuming allow_shortcuts is FALSE, if the shortcut list on the
 * bottom two lines of the screen is visible and the mouse event took
1348
1349
1350
 * 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
1351
 * already been read in. */
1352
bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1353
1354
1355
1356
1357
1358
1359
1360
{
    MEVENT mevent;

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

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1361
	return FALSE;
1362
1363
1364
1365
1366

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

1367
1368
1369
    /* 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
1370
     * was clicked and put back the equivalent keystroke(s) for it. */
1371
1372
    if (allow_shortcuts && !ISSET(NO_HELP) && wenclose(bottomwin,
	*mouse_y, *mouse_x)) {
1373
	int i, j;
1374
	size_t currslen;
1375
1376
1377
1378
1379
1380
1381
1382
	    /* 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;
1383
	else {
1384
1385
	    currslen = length_of_list(currshortcut);

1386
1387
1388
1389
1390
1391
	    /* We don't show any more shortcuts than the main list
	     * does. */
	    if (currslen > MAIN_VISIBLE)
		currslen = MAIN_VISIBLE;
	}

1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
	/* Calculate the width of each shortcut in the list (it's the
	 * same for all of them). */
	if (currslen < 2)
	    i = COLS / 6;
	else
	    i = COLS / ((currslen / 2) + (currslen % 2));

	/* Calculate the y-coordinates relative to the beginning of
	 * bottomwin, i.e, the bottom three lines of the screen. */
	j = *mouse_y - (editwinrows + 3);

	/* 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)
1407
	    return FALSE;
1408
1409
	j = (*mouse_x / i) * 2 + j;
	if (j >= currslen)
1410
	    return FALSE;
1411
1412
1413
1414
1415
1416

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

1417
1418
1419
	/* 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. */
1420
	if (s->ctrlval != NANO_NO_KEY)
1421
	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1422
	else if (s->metaval != NANO_NO_KEY)
1423
	    unget_kbinput(s->metaval, TRUE, FALSE);
1424

1425
	return TRUE;
1426
    }
1427
    return FALSE;
1428
}
1429
1430
1431
#endif /* !DISABLE_MOUSE */

const shortcut *get_shortcut(const shortcut *s_list, int kbinput, bool
1432
	*meta_key, bool *func_key)
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
{
    const shortcut *s = s_list;
    size_t slen = length_of_list(s_list);

    /* 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.
1443
1444
1445
1446
1447
	 * 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. */

1448
	if (kbinput != NANO_NO_KEY && (kbinput == s->ctrlval ||
1449
1450
		(*meta_key == TRUE && (kbinput == s->metaval ||
		kbinput == s->miscval)) || (*func_key == TRUE &&
1451
		kbinput == s->funcval))) {
1452
1453
1454
1455
1456
1457
1458
1459
	    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
1460
     * key, an equivalent primary meta key sequence, or both. */
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
    if (slen > 0) {
	if (s->ctrlval != NANO_NO_KEY) {
	    *meta_key = FALSE;
	    kbinput = s->ctrlval;
	} else if (s->metaval != NANO_NO_KEY) {
	    *meta_key = TRUE;
	    kbinput = s->metaval;
	}
	return s;
    }

    return NULL;
}

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

    /* Check for toggles. */
    for (; t != NULL; t = t->next) {
	/* We've found a toggle if meta_key is TRUE and the key is in
	 * the meta toggle list. */
	if (meta_key && kbinput == t->val)
	    break;
    }

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

1492
int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs)
1493
1494
1495
1496
1497
1498
1499
1500
{
    bool keyhandled = FALSE;
    int kbinput, retval;
    const shortcut *s;
#ifndef NANO_SMALL
    const toggle *t;
#endif

1501
    kbinput = get_kbinput(edit, meta_key, func_key);
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520

    /* Universal shortcuts.  These aren't in any shortcut lists, but we
     * should handle them anyway. */
    switch (kbinput) {
	case NANO_XON_KEY:
	    statusbar(_("XON ignored, mumble mumble."));
	    return ERR;
	case NANO_XOFF_KEY:
	    statusbar(_("XOFF ignored, mumble mumble."));
	    return ERR;
#ifndef NANO_SMALL
	case NANO_SUSPEND_KEY:
	    if (ISSET(SUSPEND))
		do_suspend(0);
	    return ERR;
#endif
#ifndef DISABLE_MOUSE
	case KEY_MOUSE:
	    if (get_edit_mouse()) {
1521
		kbinput = get_kbinput(edit, meta_key, func_key);
1522
1523
1524
1525
1526
1527
1528
		break;
	    } else
		return ERR;
#endif
    }

    /* Check for a shortcut in the main list. */
1529
    s = get_shortcut(main_list, kbinput, meta_key, func_key);
1530
1531
1532
1533
1534
1535
1536

    if (s != NULL) {
	/* We got a shortcut.  Run the shortcut's corresponding function
	 * if it has one. */
	if (s->func != do_cut_text)
	    cutbuffer_reset();
	if (s->func != NULL) {
1537
1538
1539
1540
1541
1542
	    if (allow_funcs) {
		if (ISSET(VIEW_MODE) && !s->viewok)
		    print_view_warning();
		else
		    s->func();
	    }
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
	    keyhandled = TRUE;
	}
    }

#ifndef NANO_SMALL
    else {
	/* If we didn't get a shortcut, check for a toggle. */
	t = get_toggle(kbinput, *meta_key);

	/* We got a toggle.  Switch the value of the toggle's
	 * corresponding flag. */
	if (t != NULL) {
	    cutbuffer_reset();
	    if (allow_funcs)
		do_toggle(t);
	    keyhandled = TRUE;
	}
    }
#endif

    /* If we got a shortcut with a corresponding function or a toggle,
     * reset meta_key and retval.  If we didn't, keep the value of
     * meta_key and return the key we got in retval. */
    if (allow_funcs && keyhandled) {
	*meta_key = FALSE;
	retval = ERR;
    } else {
	cutbuffer_reset();
	retval = kbinput;
    }

    return retval;
}

#ifndef DISABLE_MOUSE
bool get_edit_mouse(void)
{
    int mouse_x, mouse_y;
    bool retval;

    retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);

    if (!retval) {
	/* We can click in the edit window to move the cursor. */
	if (wenclose(edit, mouse_y, mouse_x)) {
	    bool sameline;
		/* Did they click on the line with the cursor?  If they
		 * clicked on the cursor, we set the mark. */
	    size_t xcur;
		/* The character they clicked on. */

	    /* Subtract out the size of topwin.  Perhaps we need a
	     * constant somewhere? */
	    mouse_y -= 2;

	    sameline = (mouse_y == current_y);

	    /* Move to where the click occurred. */
	    for (; current_y < mouse_y && current->next != NULL; current_y++)
		current = current->next;
	    for (; current_y > mouse_y && current->prev != NULL; current_y--)
		current = current->prev;

	    xcur = actual_x(current->data, get_page_start(xplustabs()) +
		mouse_x);

#ifndef NANO_SMALL
	    /* Clicking where the cursor is toggles the mark, as does
	     * clicking beyond the line length with the cursor at the
	     * end of the line. */
	    if (sameline && xcur == current_x) {
		if (ISSET(VIEW_MODE)) {
		    print_view_warning();
		    return retval;
		}
		do_mark();
	    }
1620
1621
#endif

1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
	    current_x = xcur;
	    placewewant = xplustabs();
	    edit_refresh();
	}
    }
    /* FIXME: If we clicked on a location in the statusbar, the cursor
     * should move to the location we clicked on.  This functionality
     * should be in get_statusbar_mouse() when it's written. */

    return retval;
}
#endif /* !DISABLE_MOUSE */

Chris Allegretta's avatar
Chris Allegretta committed
1635
1636
1637
1638
/* Return the placewewant associated with current_x.  That is, xplustabs
 * is the zero-based column position of the cursor.  Value is no smaller
 * than current_x. */
size_t xplustabs(void)
Chris Allegretta's avatar
Chris Allegretta committed
1639
{
Chris Allegretta's avatar
Chris Allegretta committed
1640
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
1641
1642
}

1643
1644
1645
1646
/* actual_x() gives the index in str of the character displayed at
 * column xplus.  That is, actual_x() is the largest value such that
 * strnlenpt(str, actual_x(str, xplus)) <= xplus. */
size_t actual_x(const char *str, size_t xplus)
Chris Allegretta's avatar
Chris Allegretta committed
1647
{
Chris Allegretta's avatar
Chris Allegretta committed
1648
    size_t i = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1649
	/* The position in str, returned. */
Chris Allegretta's avatar
Chris Allegretta committed
1650
    size_t length = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1651
	/* The screen display width to str[i]. */
Chris Allegretta's avatar
Chris Allegretta committed
1652

1653
    assert(str != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1654

1655
1656
    for (; length < xplus && *str != '\0'; i++, str++) {
	if (*str == '\t')
1657
	    length += tabsize - (length % tabsize);
1658
	else if (is_cntrl_char(*str))
Chris Allegretta's avatar
Chris Allegretta committed
1659
1660
1661
1662
	    length += 2;
	else
	    length++;
    }
1663
1664
    assert(length == strnlenpt(str - i, i));
    assert(i <= strlen(str - i));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1665

Chris Allegretta's avatar
Chris Allegretta committed
1666
1667
    if (length > xplus)
	i--;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1668

Chris Allegretta's avatar
Chris Allegretta committed
1669
    return i;
1670
1671
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1672
/* A strlen() with tabs factored in, similar to xplustabs().  How many
1673
 * columns wide are the first size characters of buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1674
size_t strnlenpt(const char *buf, size_t size)
1675
{
Chris Allegretta's avatar
Chris Allegretta committed
1676
1677
    size_t length = 0;

1678
1679
1680
1681
    assert(buf != NULL);
    for (; *buf != '\0' && size != 0; size--, buf++) {
	if (*buf == '\t')
	    length += tabsize - (length % tabsize);
1682
	else if (is_cntrl_char(*buf))
1683
1684
1685
1686
	    length += 2;
	else
	    length++;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1687
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
1688
1689
}

1690
/* How many columns wide is buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1691
size_t strlenpt(const char *buf)
1692
{
1693
    return strnlenpt(buf, (size_t)-1);
1694
1695
}

1696
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1697
{
1698
    mvwaddstr(topwin, 0, 0, hblank);
1699
1700
}

Chris Allegretta's avatar
Chris Allegretta committed
1701
1702
void blank_edit(void)
{
1703
    int i;
1704
    for (i = 0; i < editwinrows; i++)
Chris Allegretta's avatar
Chris Allegretta committed
1705
1706
1707
1708
1709
1710
1711
1712
	mvwaddstr(edit, i, 0, hblank);
}

void blank_statusbar(void)
{
    mvwaddstr(bottomwin, 0, 0, hblank);
}

1713
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1714
{
1715
1716
1717
1718
    if (statusblank > 1)
	statusblank--;
    else if (statusblank == 1 && !ISSET(CONSTUPDATE)) {
	statusblank = 0;
1719
1720
1721
1722
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
	wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1723
1724
1725
    }
}

1726
1727
1728
1729
1730
1731
1732
1733
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
}

1734
1735
1736
1737
1738
/* 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
 * string is dynamically allocated, and should be freed. */
1739
char *display_string(const char *buf, size_t start_col, size_t len)
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
{
    size_t start_index;
	/* Index in buf of first character shown in return value. */
    size_t column;
	/* Screen column start_index corresponds to. */
    size_t end_index;
	/* Index in buf of last character shown in return value. */
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */

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

    start_index = actual_x(buf, start_col);
    column = strnlenpt(buf, start_index);
    assert(column <= start_col);
    end_index = actual_x(buf, start_col + len - 1);
    alloc_len = strnlenpt(buf, end_index + 1) - column;
    if (len > alloc_len + column - start_col)
	len = alloc_len + column - start_col;
    converted = charalloc(alloc_len + 1);
    buf += start_index;
    index = 0;

    for (; index < alloc_len; buf++) {
1769
1770
1771
1772
1773
1774
1775
	if (*buf == '\t') {
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[0] :
#endif
		' '; 
	    while ((column + index) % tabsize)
1776
		converted[index++] = ' ';
1777
	} else if (is_cntrl_char(*buf)) {
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
	    converted[index++] = '^';
	    if (*buf == '\n')
		/* Treat newlines embedded in a line as encoded nulls;
		 * the line in question should be run through unsunder()
		 * before reaching here. */
		converted[index++] = '@';
	    else if (*buf == NANO_CONTROL_8)
		converted[index++] = '?';
	    else
		converted[index++] = *buf + 64;
1788
1789
1790
1791
1792
1793
1794
	} else if (*buf == ' ')
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[1] :
#endif
		' ';
	else
1795
1796
1797
1798
1799
1800
1801
1802
1803
	    converted[index++] = *buf;
    }
    assert(len <= alloc_len + column - start_col);
    charmove(converted, converted + start_col - column, len);
    null_at(&converted, len);

    return charealloc(converted, len + 1);
}

Chris Allegretta's avatar
Chris Allegretta committed
1804
/* Repaint the statusbar when getting a character in nanogetstr().  buf
1805
 * should be no longer than max(0, COLS - 4).
Chris Allegretta's avatar
Chris Allegretta committed
1806
 *
Chris Allegretta's avatar
Chris Allegretta committed
1807
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
1808
 * off! */
1809
void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
1810
{
1811
1812
    size_t x_real = strnlenpt(inputbuf, x);
    int wid = COLS - strlen(buf) - 2;
1813

Chris Allegretta's avatar
Chris Allegretta committed
1814
1815
    assert(0 <= x && x <= strlen(inputbuf));

1816
    wattron(bottomwin, A_REVERSE);
1817
    blank_statusbar();
1818

Chris Allegretta's avatar
Chris Allegretta committed
1819
1820
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834

    if (COLS > 1)
	waddch(bottomwin, x_real < wid ? ' ' : '$');
    if (COLS > 2) {
	size_t page_start = x_real - x_real % wid;
	char *expanded = display_string(inputbuf, page_start, wid);

	assert(wid > 0);
	assert(strlen(expanded) <= wid);
	waddstr(bottomwin, expanded);
	free(expanded);
	wmove(bottomwin, 0, COLS - wid + x_real - page_start);
    } else
	wmove(bottomwin, 0, COLS - 1);
1835
    wattroff(bottomwin, A_REVERSE);
1836
1837
}

1838
/* Get the input from the keyboard; this should only be called from
Chris Allegretta's avatar
Chris Allegretta committed
1839
 * statusq(). */
1840
int nanogetstr(bool allow_tabs, const char *buf, const char *def,
1841
1842
1843
#ifndef NANO_SMALL
		historyheadtype *history_list,
#endif
1844
		const shortcut *s
1845
#ifndef DISABLE_TABCOMP
1846
		, bool *list
1847
#endif
1848
		)
Chris Allegretta's avatar
Chris Allegretta committed
1849
1850
{
    int kbinput;
1851
    bool meta_key, func_key;
1852
    static size_t x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
1853
	/* the cursor position in 'answer' */
1854
    size_t xend;
Chris Allegretta's avatar
Chris Allegretta committed
1855
	/* length of 'answer', the status bar text */
1856
    bool tabbed = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1857
1858
1859
	/* used by input_tab() */
    const shortcut *t;

1860
1861
1862
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
1863
    char *currentbuf = NULL;
1864
    char *complete = NULL;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1865
1866
1867
1868
1869
1870
1871
1872
1873
    int last_kbinput = 0;

    /* This variable is used in the search history code.  use_cb == 0 
       means that we're using the existing history and ignoring
       currentbuf.  use_cb == 1 means that the entry in answer should be
       moved to currentbuf or restored from currentbuf to answer. 
       use_cb == 2 means that the entry in currentbuf should be moved to
       answer or restored from answer to currentbuf. */
    int use_cb = 0;
1874
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1875
    xend = strlen(def);
Chris Allegretta's avatar
Chris Allegretta committed
1876

1877
    /* Only put x at the end of the string if it's uninitialized, if it
1878
1879
1880
1881
       would be past the end of the string as it is, or if
       resetstatuspos is TRUE.  Otherwise, leave it alone.  This is so
       the cursor position stays at the same place if a prompt-changing
       toggle is pressed. */
1882
    if (x == (size_t)-1 || x > xend || resetstatuspos)
Chris Allegretta's avatar
Chris Allegretta committed
1883
1884
	x = xend;

Chris Allegretta's avatar
Chris Allegretta committed
1885
    answer = charealloc(answer, xend + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1886
1887
1888
1889
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1890

1891
#if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
1892
    currshortcut = s;
1893
1894
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1895
    /* Get the input! */
1896

Chris Allegretta's avatar
Chris Allegretta committed
1897
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
1898

1899
1900
    /* Make sure any editor screen updates are displayed before getting
       input */
1901
1902
    wnoutrefresh(edit);
    wrefresh(bottomwin);
1903

1904
1905
1906
1907
1908
    /* If we're using restricted mode, we aren't allowed to change the
     * name of a file once it has one because that would allow writing
     * to files not specified on the command line.  In this case,
     * disable all keys that would change the text if the filename isn't
     * blank and we're at the "Write File" prompt. */
1909
    while ((kbinput = get_kbinput(bottomwin, &meta_key, &func_key)) !=
1910
	NANO_CANCEL_KEY && kbinput != NANO_ENTER_KEY) {
1911
	for (t = s; t != NULL; t = t->next) {
1912
#ifdef DEBUG
1913
	    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
1914
1915
#endif

1916
1917
1918
	    /* Temporary hack to interpret NANO_HELP_FKEY correctly. */
	    if (kbinput == t->funcval)
		kbinput = t->ctrlval;
1919

1920
	    if (kbinput == t->ctrlval && is_cntrl_char(kbinput)) {
1921

1922
#ifndef DISABLE_HELP
1923
1924
		/* Have to do this here, it would be too late to do it
		   in statusq() */
1925
		if (kbinput == NANO_HELP_KEY) {
1926
1927
1928
1929
		    do_help();
		    break;
		}
#endif
1930
1931
#ifndef NANO_SMALL
		/* Have to handle these here too, for the time being */
1932
		if (kbinput == NANO_PREVLINE_KEY || kbinput == NANO_NEXTLINE_KEY)
1933
1934
		    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1935

1936
		return t->ctrlval;
Chris Allegretta's avatar
Chris Allegretta committed
1937
1938
	    }
	}
1939
	assert(x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
1940

Chris Allegretta's avatar
Chris Allegretta committed
1941
	if (kbinput != '\t')
1942
	    tabbed = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1943

Chris Allegretta's avatar
Chris Allegretta committed
1944
	switch (kbinput) {
1945
#ifndef DISABLE_MOUSE
1946
	case KEY_MOUSE:
1947
1948
1949
1950
	    {
		int mouse_x, mouse_y;
		get_mouseinput(&mouse_x, &mouse_y, TRUE);
	    }
1951
	    break;
1952
#endif
1953
1954
1955
	case NANO_REFRESH_KEY:
	    total_refresh();
	    break;
1956
	case NANO_HOME_KEY:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1957
1958
#ifndef NANO_SMALL
	    if (ISSET(SMART_HOME)) {
1959
		size_t old_x = x;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1960
1961
1962
1963
1964
1965
1966
1967
1968

		for (x = 0; isblank(answer[x]) && x < xend; x++)
		    ;

		if (x == old_x || x == xend)
		    x = 0;
	    } else
#endif
		x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1969
	    break;
1970
	case NANO_END_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1971
	    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
1972
	    break;
1973
	case NANO_FORWARD_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1974
1975
1976
	    if (x < xend)
		x++;
	    break;
1977
	case NANO_DELETE_KEY:
1978
1979
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable Delete. */
1980
1981
1982
1983
1984
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		if (x < xend) {
		    charmove(answer + x, answer + x + 1, xend - x);
		    xend--;
		}
Chris Allegretta's avatar
Chris Allegretta committed
1985
1986
	    }
	    break;
1987
	case NANO_CUT_KEY:
1988
	    /* If we're using restricted mode, the filename isn't blank,
1989
	     * and we're at the "Write File" prompt, disable Cut. */
1990
1991
1992
1993
1994
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		null_at(&answer, 0);
		xend = 0;
		x = 0;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1995
	    break;
1996
	case NANO_BACKSPACE_KEY:
1997
1998
1999
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable
	     * Backspace. */
2000
2001
2002
2003
2004
2005
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' || s != writefile_list) {
		if (x > 0) {
		    charmove(answer + x - 1, answer + x, xend - x + 1);
		    x--;
		    xend--;
		}
Chris Allegretta's avatar
Chris Allegretta committed
2006
	    }
Chris Allegretta's avatar
Chris Allegretta committed
2007
	    break;
2008
	case NANO_TAB_KEY:
2009
2010
#ifndef NANO_SMALL
	    /* tab history completion */
Chris Allegretta's avatar
Chris Allegretta committed
2011
	    if (history_list != NULL) {
2012
		if (!complete || last_kbinput != NANO_TAB_KEY) {
2013
2014
2015
		    history_list->current = (historytype *)history_list;
		    history_list->len = strlen(answer);
		}
Chris Allegretta's avatar
Chris Allegretta committed
2016

Chris Allegretta's avatar
Chris Allegretta committed
2017
		if (history_list->len > 0) {
2018
2019
		    complete = get_history_completion(history_list, answer);
		    xend = strlen(complete);
Chris Allegretta's avatar
Chris Allegretta committed
2020
		    x = xend;
2021
2022
		    answer = mallocstrcpy(answer, complete);
		}
2023
	    }
2024
#ifndef DISABLE_TABCOMP
2025
2026
	    else
#endif
2027
2028
#endif
#ifndef DISABLE_TABCOMP
2029
	    if (allow_tabs) {
2030
2031
2032
2033
2034
2035
2036
		int shift = 0;

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
		x += shift;
		if (x > xend)
		    x = xend;
2037
2038
2039
	    }
#endif
	    break;
2040
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
2041
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2042
2043
		x--;
	    break;
2044
	case NANO_PREVLINE_KEY:
2045
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2046
	    if (history_list != NULL) {
2047

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2048
2049
2050
2051
2052
2053
		/* if currentbuf is NULL, or if use_cb is 1, currentbuf
		   isn't NULL, and currentbuf is different from answer,
		   it means that we're scrolling up at the top of the
		   search history, and we need to save the current
		   answer in currentbuf; do this and reset use_cb to
		   0 */
2054
2055
		if (currentbuf == NULL || (use_cb == 1 &&
			strcmp(currentbuf, answer) != 0)) {
2056
		    currentbuf = mallocstrcpy(currentbuf, answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2057
		    use_cb = 0;
2058
2059
		}

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2060
2061
2062
2063
2064
2065
		/* if currentbuf isn't NULL, use_cb is 2, and currentbuf 
		   is different from answer, it means that we're
		   scrolling up at the bottom of the search history, and
		   we need to make the string in currentbuf the current
		   answer; do this, blow away currentbuf since we don't
		   need it anymore, and reset use_cb to 0 */
2066
2067
		if (currentbuf != NULL && use_cb == 2 &&
			strcmp(currentbuf, answer) != 0) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
		    answer = mallocstrcpy(answer, currentbuf);
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
		    use_cb = 0;

		/* else get older search from the history list and save
		   it in answer; if there is no older search, blank out 
		   answer */
		} else if ((history = get_history_older(history_list)) != NULL) {
2078
2079
2080
2081
2082
2083
2084
2085
2086
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
2087
	    break;
2088
	case NANO_NEXTLINE_KEY:
2089
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2090
	    if (history_list != NULL) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2091
2092
2093

		/* get newer search from the history list and save it 
		   in answer */
Chris Allegretta's avatar
Chris Allegretta committed
2094
		if ((history = get_history_newer(history_list)) != NULL) {
2095
2096
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
2097

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2098
2099
2100
2101
2102
2103
2104
2105
2106
		/* if there is no newer search, we're here */
		
		/* if currentbuf isn't NULL and use_cb isn't 2, it means 
		   that we're scrolling down at the bottom of the search
		   history and we need to make the string in currentbuf
		   the current answer; do this, blow away currentbuf
		   since we don't need it anymore, and set use_cb to
		   1 */
		} else if (currentbuf != NULL && use_cb != 2) {
2107
		    answer = mallocstrcpy(answer, currentbuf);
Chris Allegretta's avatar
Chris Allegretta committed
2108
2109
2110
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2111
2112
2113
2114
		    use_cb = 1;

		/* otherwise, if currentbuf is NULL and use_cb isn't 2, 
		   it means that we're scrolling down at the bottom of
2115
2116
2117
2118
		   the search history and the current answer (if it's
		   not blank) needs to be saved in currentbuf; do this,
		   blank out answer (if necessary), and set use_cb to
		   2 */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2119
		} else if (use_cb != 2) {
2120
2121
2122
2123
		    if (answer[0] != '\0') {
			currentbuf = mallocstrcpy(currentbuf, answer);
			answer = mallocstrcpy(answer, "");
		    }
2124
		    xend = 0;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2125
		    use_cb = 2;
2126
2127
2128
2129
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2130
	    break;
2131
2132
	    default:

2133
		for (t = s; t != NULL; t = t->next) {
2134
#ifdef DEBUG
2135
		    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
Chris Allegretta's avatar
Chris Allegretta committed
2136
			    kbinput);
2137
#endif
2138
		    if (meta_key && (kbinput == t->metaval || kbinput == t->miscval))
2139
			/* We hit a meta key.  Do like above.  We don't
2140
			 * just put back the letter and let it get
2141
2142
2143
			 * caught above cause that screws the
			 * keypad... */
			return kbinput;
2144
		}
Chris Allegretta's avatar
Chris Allegretta committed
2145

2146
2147
2148
2149
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, act as though the
	     * unhandled character we got is a control character and
	     * throw it away. */
2150
	    if (is_cntrl_char(kbinput) || (ISSET(RESTRICTED) && filename[0] != '\0' && s == writefile_list))
Chris Allegretta's avatar
Chris Allegretta committed
2151
		break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2152
	    answer = charealloc(answer, xend + 2);
2153
	    charmove(answer + x + 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2154
2155
	    xend++;
	    answer[x] = kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2156
2157
2158
	    x++;

#ifdef DEBUG
2159
	    fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
2160
#endif
2161
	} /* switch (kbinput) */
2162
#ifndef NANO_SMALL
2163
	last_kbinput = kbinput;
2164
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2165
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
2166
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
2167
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
2168

Chris Allegretta's avatar
Chris Allegretta committed
2169
    /* We finished putting in an answer; reset x */
2170
    x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
2171

2172
    return kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2173
2174
}

2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
/* Ask a question on the statusbar.  Answer will be stored in answer
 * global.  Returns -1 on aborted enter, -2 on a blank string, and 0
 * otherwise, the valid shortcut key caught.  def is any editable text
 * we want to put up by default.
 *
 * New arg tabs tells whether or not to allow tab completion. */
int statusq(bool allow_tabs, const shortcut *s, const char *def,
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
		const char *msg, ...)
{
    va_list ap;
    char *foo = charalloc(COLS - 3);
    int ret;
#ifndef DISABLE_TABCOMP
    bool list = FALSE;
#endif

    bottombars(s);

    va_start(ap, msg);
    vsnprintf(foo, COLS - 4, msg, ap);
    va_end(ap);
    foo[COLS - 4] = '\0';

    ret = nanogetstr(allow_tabs, foo, def,
#ifndef NANO_SMALL
		which_history,
#endif
		s
#ifndef DISABLE_TABCOMP
		, &list
#endif
		);
    free(foo);
    resetstatuspos = FALSE;

    switch (ret) {
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
	case NANO_FIRSTLINE_KEY:
	case NANO_FIRSTLINE_FKEY:
	    do_first_line();
	    resetstatuspos = TRUE;
	    break;
	case NANO_LASTLINE_KEY:
	case NANO_LASTLINE_FKEY:
	    do_last_line();
	    resetstatuspos = TRUE;
	    break;
2224
#ifndef DISABLE_JUSTIFY
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
	case NANO_PARABEGIN_KEY:
	case NANO_PARABEGIN_ALTKEY1:
	case NANO_PARABEGIN_ALTKEY2:
	    do_para_begin();
	    resetstatuspos = TRUE;
	    break;
	case NANO_PARAEND_KEY:
	case NANO_PARAEND_ALTKEY1:
	case NANO_PARAEND_ALTKEY2:
	    do_para_end();
	    resetstatuspos = TRUE;
	    break;
	case NANO_FULLJUSTIFY_KEY:
	case NANO_FULLJUSTIFY_ALTKEY:
	    if (!ISSET(VIEW_MODE))
		do_full_justify();
	    resetstatuspos = TRUE;
	    break;
2243
#endif
2244
2245
2246
2247
2248
2249
2250
2251
	case NANO_CANCEL_KEY:
	    ret = -1;
	    resetstatuspos = TRUE;
	    break;
	case NANO_ENTER_KEY:
	    ret = (answer[0] == '\0') ? -2 : 0;
	    resetstatuspos = TRUE;
	    break;
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
    }
    blank_statusbar();

#ifdef DEBUG
    fprintf(stderr, "I got \"%s\"\n", answer);
#endif

#ifndef DISABLE_TABCOMP
	/* if we've done tab completion, there might be a list of
	   filename matches on the edit window at this point; make sure
	   they're cleared off. */
	if (list)
	    edit_refresh();
#endif

    return ret;
}

void statusq_abort(void)
{
    resetstatuspos = TRUE;
}

2275
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2276
{
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
    size_t space;
	/* The space we have available for display. */
    size_t verlen = strlen(VERMSG) + 1;
	/* The length of the version message. */
    const char *prefix;
	/* "File:", "Dir:", or "New Buffer".  Goes before filename. */
    size_t prefixlen;
	/* strlen(prefix) + 1. */
    const char *state;
	/* "Modified", "View", or spaces the length of "Modified".
	 * Tells the state of this buffer. */
    size_t statelen = 0;
	/* strlen(state) + 1. */
    char *exppath = NULL;
	/* The file name, expanded for display. */
    size_t explen = 0;
	/* strlen(exppath) + 1. */
    int newbuffer = FALSE;
	/* Do we say "New Buffer"? */
    int dots = FALSE;
	/* Do we put an ellipsis before the path? */

    assert(path != NULL || filename != NULL);
    assert(COLS >= 0);
Chris Allegretta's avatar
Chris Allegretta committed
2301
2302

    wattron(topwin, A_REVERSE);
2303

2304
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2305

2306
2307
2308
2309
    if (COLS <= 5 || COLS - 5 < verlen)
	space = 0;
    else {
	space = COLS - 5 - verlen;
2310
2311
2312
2313
	/* Reserve 2/3 of the screen plus one column for after the
	 * version message. */
	if (space < COLS - (COLS / 3) + 1)
	    space = COLS - (COLS / 3) + 1;
2314
    }
Chris Allegretta's avatar
Chris Allegretta committed
2315

2316
    if (COLS > 4) {
2317
2318
2319
	/* The version message should only take up 1/3 of the screen
	 * minus one column. */
	mvwaddnstr(topwin, 0, 2, VERMSG, (COLS / 3) - 3);
2320
2321
	waddstr(topwin, "  ");
    }
Chris Allegretta's avatar
Chris Allegretta committed
2322
2323

    if (ISSET(MODIFIED))
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
	state = _("Modified");
    else if (path == NULL && ISSET(VIEW_MODE))
	state = _("View");
    else {
	if (space > 0)
	    statelen = strnlen(_("Modified"), space - 1) + 1;
	state = &hblank[COLS - statelen];
    }
    statelen = strnlen(state, COLS);
    /* We need a space before state. */
    if ((ISSET(MODIFIED) || ISSET(VIEW_MODE)) && statelen < COLS)
	statelen++;

    assert(space >= 0);
    if (space == 0 || statelen >= space)
	goto the_end;

#ifndef DISABLE_BROWSER
    if (path != NULL)
	prefix = _("DIR:");
    else
#endif
    if (filename[0] == '\0') {
	prefix = _("New Buffer");
	newbuffer = TRUE;
    } else
	prefix = _("File:");
    assert(statelen < space);
    prefixlen = strnlen(prefix, space - statelen);
    /* If newbuffer is FALSE, we need a space after prefix. */
    if (!newbuffer && prefixlen + statelen < space)
	prefixlen++;

    if (path == NULL)
	path = filename;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2359
    space -= prefixlen + statelen;
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
	/* space is now the room we have for the file name. */
    if (!newbuffer) {
	size_t lenpt = strlenpt(path), start_col;

	if (lenpt > space)
	    start_col = actual_x(path, lenpt - space);
	else
	    start_col = 0;
	exppath = display_string(path, start_col, space);
	dots = (lenpt > space);
	explen = strlen(exppath);
    }

    if (!dots) {
	/* There is room for the whole filename, so we center it. */
	waddnstr(topwin, hblank, (space - explen) / 3);
	waddnstr(topwin, prefix, prefixlen);
	if (!newbuffer) {
	    assert(strlen(prefix) + 1 == prefixlen);
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    } else {
	/* We will say something like "File: ...ename". */
	waddnstr(topwin, prefix, prefixlen);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2385
	if (space <= 0 || newbuffer)
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
	    goto the_end;
	waddch(topwin, ' ');
	waddnstr(topwin, "...", space);
	if (space <= 3)
	    goto the_end;
	space -= 3;
	assert(explen = space + 3);
	waddnstr(topwin, exppath + 3, space);
    }

  the_end:

    free(exppath);

    if (COLS <= 1 || statelen >= COLS - 1)
	mvwaddnstr(topwin, 0, 0, state, COLS);
    else {
	assert(COLS - statelen - 2 >= 0);
	mvwaddch(topwin, 0, COLS - statelen - 2, ' ');
	mvwaddnstr(topwin, 0, COLS - statelen - 1, state, statelen);
    }
2407

Chris Allegretta's avatar
Chris Allegretta committed
2408
    wattroff(topwin, A_REVERSE);
2409

2410
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2411
    reset_cursor();
2412
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2413
2414
}

2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
/* If modified is not already set, set it and update titlebar. */
void set_modified(void)
{
    if (!ISSET(MODIFIED)) {
	SET(MODIFIED);
	titlebar(NULL);
    }
}

void statusbar(const char *msg, ...)
{
    va_list ap;

    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();

    if (COLS >= 4) {
	char *bar;
	char *foo;
	size_t start_x = 0, foo_len;
2445
2446
2447
2448
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	int old_whitespace = ISSET(WHITESPACE_DISPLAY);
	UNSET(WHITESPACE_DISPLAY);
#endif
2449
2450
2451
2452
	bar = charalloc(COLS - 3);
	vsnprintf(bar, COLS - 3, msg, ap);
	va_end(ap);
	foo = display_string(bar, 0, COLS - 4);
2453
2454
2455
2456
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	if (old_whitespace)
	    SET(WHITESPACE_DISPLAY);
#endif
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
	free(bar);
	foo_len = strlen(foo);
	start_x = (COLS - foo_len - 4) / 2;

	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();
	wrefresh(edit);
	    /* Leave the cursor at its position in the edit window, not
	     * in the statusbar. */
    }

    SET(DISABLE_CURPOS);
2477
    statusblank = 26;
2478
2479
}

2480
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
2481
{
2482
    size_t i, colwidth, slen;
2483

Chris Allegretta's avatar
Chris Allegretta committed
2484
2485
2486
    if (ISSET(NO_HELP))
	return;

2487
2488
    if (s == main_list) {
	slen = MAIN_VISIBLE;
2489
	assert(slen <= length_of_list(s));
2490
    } else {
2491
2492
	slen = length_of_list(s);

2493
	/* Don't show any more shortcuts than the main list does. */
2494
2495
2496
2497
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

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

2502
    blank_bottombars();
2503

2504
    for (i = 0; i < slen; i++, s = s->next) {
2505
	const char *keystr;
2506

2507
	/* Yucky sentinel values we can't handle a better way. */
2508
#ifndef NANO_SMALL
2509
	if (s->ctrlval == NANO_HISTORY_KEY)
2510
2511
	    keystr = _("Up");
	else {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2512
#endif
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
	    char foo[4];

	    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));
2525

2526
2527
2528
2529
2530
2531
	    keystr = foo;
#ifndef NANO_SMALL
	}
#endif

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

2535
2536
2537
    wnoutrefresh(bottomwin);
    reset_cursor();
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2538
2539
}

2540
2541
2542
2543
2544
2545
/* 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
2546
{
2547
    assert(keystroke != NULL && desc != NULL && len >= 0);
2548
2549
2550
    wattron(bottomwin, A_REVERSE);
    waddnstr(bottomwin, keystroke, len);
    wattroff(bottomwin, A_REVERSE);
2551
    len -= strlen(keystroke) + 1;
2552
2553
2554
    if (len > 0) {
	waddch(bottomwin, ' ');
	waddnstr(bottomwin, desc, len);
Chris Allegretta's avatar
Chris Allegretta committed
2555
2556
2557
    }
}

2558
/* And so start the display update routines. */
Chris Allegretta's avatar
Chris Allegretta committed
2559

2560
2561
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
2562
{
2563
2564
    int check_line = 0;
    const filestruct *filetmp;
2565

2566
2567
2568
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
2569
}
2570
#endif
2571

2572
2573
2574
2575
2576
/* nano scrolls horizontally within a line in chunks.  This function
 * returns the column number of the first character displayed in the
 * window when the cursor is at the given column.  Note that
 * 0 <= column - get_page_start(column) < COLS. */
size_t get_page_start(size_t column)
Chris Allegretta's avatar
Chris Allegretta committed
2577
{
2578
2579
2580
2581
    assert(COLS > 0);
    if (column == 0 || column < COLS - 1)
	return 0;
    else if (COLS > 9)
2582
	return column - 7 - (column - 7) % (COLS - 8);
2583
2584
2585
2586
2587
    else if (COLS > 2)
	return column - (COLS - 2);
    else
	return column - (COLS - 1);
		/* The parentheses are necessary to avoid overflow. */
Chris Allegretta's avatar
Chris Allegretta committed
2588
2589
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2590
/* Resets current_y, based on the position of current, and puts the
2591
 * cursor in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2592
2593
void reset_cursor(void)
{
2594
2595
2596
2597
    /* If we haven't opened any files yet, put the cursor in the top
     * left corner of the edit window and get out. */
    if (edittop == NULL || current == NULL) {
	wmove(edit, 0, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2598
	return;
2599
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2600

2601
2602
2603
2604
2605
    current_y = current->lineno - edittop->lineno;
    if (current_y < editwinrows) {
	size_t x = xplustabs();
	wmove(edit, current_y, x - get_page_start(x));
     }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2606
}
Chris Allegretta's avatar
Chris Allegretta committed
2607

2608
2609
2610
2611
/* edit_add() takes care of the job of actually painting a line into the
 * edit window.  fileptr is the line to be painted, at row yval of the
 * window.  converted is the actual string to be written to the window,
 * with tabs and control characters replaced by strings of regular
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2612
2613
 * characters.  start is the column number of the first character of
 * this page.  That is, the first character of converted corresponds to
2614
 * character number actual_x(fileptr->data, start) of the line. */
2615
2616
void edit_add(const filestruct *fileptr, const char *converted, int
	yval, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2617
{
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
#if defined(ENABLE_COLOR) || !defined(NANO_SMALL)
    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. */
2628
2629
#endif

2630
2631
2632
    assert(fileptr != NULL && converted != NULL);
    assert(strlen(converted) <= COLS);

2633
    /* Just paint the string in any case (we'll add color or reverse on
2634
2635
     * just the text that needs it). */
    mvwaddstr(edit, yval, 0, converted);
2636

Chris Allegretta's avatar
Chris Allegretta committed
2637
#ifdef ENABLE_COLOR
2638
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
2639
2640
2641
2642
2643
2644
	const colortype *tmpcolor = colorstrings;

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
2645
		/* Number of chars to paint on this line.  There are COLS
2646
		 * characters on a whole line. */
2647
2648
	    regmatch_t startmatch;	/* match position for start_regexp */
	    regmatch_t endmatch;	/* match position for end_regexp */
2649
2650
2651
2652

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2653
2654
	    /* Two notes about regexec().  Return value 0 means there is
	     * a match.  Also, rm_eo is the first non-matching character
2655
2656
2657
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
2658
	    if (tmpcolor->end == NULL) {
2659
2660
2661
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
2662
2663
2664
2665
2666
2667
2668
2669
		 * 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. */
2670
		    if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
2671
			&startmatch, k == 0 ? 0 : REG_NOTBOL) == REG_NOMATCH)
2672
			break;
2673
2674
2675
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
2676
2677
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
2678
			statusbar(_("Refusing 0 length regex match"));
2679
2680
2681
		    } else if (startmatch.rm_so < endpos &&
				startmatch.rm_eo > startpos) {
			if (startmatch.rm_so <= startpos)
2682
			    x_start = 0;
2683
			else
2684
2685
2686
2687
			    x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
			paintlen = strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start;
2688
2689
2690
2691
2692
2693
			if (paintlen > COLS - x_start)
			    paintlen = COLS - x_start;

			assert(0 <= x_start && 0 < paintlen &&
				x_start + paintlen <= COLS);
			mvwaddnstr(edit, yval, x_start,
2694
2695
				converted + x_start, paintlen);
		    }
2696
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2697
		}
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
	    } else {
		/* This is a multi-line regexp.  There are two steps. 
		 * 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
		 * 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. */

		const filestruct *start_line = fileptr->prev;
2711
		    /* the first line before fileptr matching start */
2712
2713
2714
2715
2716
		regoff_t start_col;
		    /* where it starts in that line */
		const filestruct *end_line;

		while (start_line != NULL &&
2717
			regexec(&tmpcolor->start, start_line->data, 1,
2718
			&startmatch, 0) == REG_NOMATCH) {
2719
2720
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
2721
2722
		    if (regexec(tmpcolor->end, start_line->data, 0,
			NULL, 0) == 0)
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
			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;
2734
		while (TRUE) {
2735
2736
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
2737
 		    if (regexec(tmpcolor->end,
2738
			start_line->data + start_col + startmatch.rm_eo,
2739
2740
2741
			0, NULL, start_col + startmatch.rm_eo == 0 ? 0 :
			REG_NOTBOL) == REG_NOMATCH)
			/* No end found after this start. */
2742
			break;
2743
		    start_col++;
2744
		    if (regexec(&tmpcolor->start,
2745
2746
			start_line->data + start_col, 1,
			&startmatch, REG_NOTBOL) == REG_NOMATCH)
2747
2748
			/* No later start on this line. */
			goto step_two;
2749
		}
2750
2751
		/* Indeed, there is a start not followed on this line by
		 * an end. */
2752
2753
2754

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

		/* No end found, or it is too early. */
2764
2765
		if (end_line == NULL ||
			(end_line == fileptr && endmatch.rm_eo <= startpos))
2766
2767
2768
		    goto step_two;

		/* Now paint the start of fileptr. */
2769
2770
		paintlen = end_line != fileptr ? COLS :
			strnlenpt(fileptr->data, endmatch.rm_eo) - start;
2771
2772
2773
2774
		if (paintlen > COLS)
		    paintlen = COLS;

		assert(0 < paintlen && paintlen <= COLS);
2775
		mvwaddnstr(edit, yval, 0, converted, paintlen);
2776
2777
2778
2779
2780

		/* We have already painted the whole line. */
		if (paintlen == COLS)
		    goto skip_step_two;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2781
2782
  step_two:
		/* Second step, we look for starts on this line. */
2783
		start_col = 0;
2784
		while (start_col < endpos) {
2785
2786
2787
2788
		    if (regexec(&tmpcolor->start,
			fileptr->data + start_col, 1, &startmatch,
			start_col == 0 ? 0 : REG_NOTBOL) == REG_NOMATCH ||
			start_col + startmatch.rm_so >= endpos)
2789
2790
2791
2792
2793
2794
2795
			/* 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;

2796
		    if (startmatch.rm_so <= startpos)
2797
			x_start = 0;
2798
		    else
2799
2800
2801
2802
2803
			x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
		    if (regexec(tmpcolor->end,
			fileptr->data + startmatch.rm_eo, 1, &endmatch,
			startmatch.rm_eo == 0 ? 0 : REG_NOTBOL) == 0) {
2804
			/* Translate the end match to be relative to the
2805
			 * beginning of the line. */
2806
2807
2808
			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
2809
2810
			 * appear on this page, and is the match more
			 * than zero characters long? */
2811
			if (endmatch.rm_eo > startpos &&
2812
				endmatch.rm_eo > startmatch.rm_so) {
2813
2814
			    paintlen = strnlenpt(fileptr->data,
				endmatch.rm_eo) - start - x_start;
2815
2816
2817
2818
			    if (x_start + paintlen > COLS)
				paintlen = COLS - x_start;

			    assert(0 <= x_start && 0 < paintlen &&
2819
				x_start + paintlen <= COLS);
2820
			    mvwaddnstr(edit, yval, x_start,
2821
				converted + x_start, paintlen);
2822
			}
2823
		    } else {
2824
2825
2826
			/* There is no end on this line.  But we haven't
			 * yet looked for one on later lines. */
			end_line = fileptr->next;
2827
2828
2829
			while (end_line != NULL &&
				regexec(tmpcolor->end, end_line->data, 0,
				NULL, 0) == REG_NOMATCH)
2830
2831
2832
2833
			    end_line = end_line->next;
			if (end_line != NULL) {
			    assert(0 <= x_start && x_start < COLS);
			    mvwaddnstr(edit, yval, x_start,
2834
				converted + x_start, COLS - x_start);
2835
2836
2837
			    /* We painted to the end of the line, so
			     * don't bother checking any more starts. */
			    break;
2838
2839
			}
		    }
2840
		    start_col = startmatch.rm_so + 1;
2841
		} /* while start_col < endpos */
2842
	    } /* if (tmp_color->end != NULL) */
2843

2844
  skip_step_two:
2845
2846
2847
2848
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
2849
#endif				/* ENABLE_COLOR */
2850

2851
2852
2853
2854
2855
2856
2857
2858
#ifndef NANO_SMALL
    if (ISSET(MARK_ISSET)
	    && (fileptr->lineno <= mark_beginbuf->lineno
		|| fileptr->lineno <= current->lineno)
	    && (fileptr->lineno >= mark_beginbuf->lineno
		|| fileptr->lineno >= current->lineno)) {
	/* fileptr is at least partially selected. */

2859
2860
2861
2862
2863
2864
	const filestruct *top;
	    /* Either current or mark_beginbuf, whichever is first. */
	size_t top_x;
	    /* current_x or mark_beginx, corresponding to top. */
	const filestruct *bot;
	size_t bot_x;
2865
2866
2867
	int x_start;
	    /* Starting column for mvwaddnstr.  Zero-based. */
	int paintlen;
2868
	    /* Number of chars to paint on this line.  There are COLS
2869
2870
	     * characters on a whole line. */

2871
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2872
2873
2874
2875
2876

	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
2877

2878
	/* The selected bit of fileptr is on this page. */
2879
2880
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2881
2882
2883
2884

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

	    if (bot_x >= endpos)
2887
2888
2889
2890
		/* If the end of the mark is off the page, paintlen is
		 * -1, meaning that everything on the line gets
		 * painted. */
		paintlen = -1;
2891
	    else
2892
2893
2894
		/* Otherwise, paintlen is the expanded location of the
		 * end of the mark minus the expanded location of the
		 * beginning of the mark. */
2895
2896
		paintlen = strnlenpt(fileptr->data, bot_x)
			- (x_start + start);
2897
2898
2899
2900
2901
2902
2903
2904

	    /* 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;
	    }
2905
2906
2907

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

2908
	    wattron(edit, A_REVERSE);
2909
	    mvwaddnstr(edit, yval, x_start, converted + x_start, paintlen);
2910
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
2911
	}
2912
    }
2913
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2914
2915
}

2916
/* Just update one line in the edit buffer.  Basically a wrapper for
2917
2918
 * edit_add().
 *
2919
2920
2921
 * If fileptr != current, then index is considered 0.  The line will be
 * displayed starting with fileptr->data[index].  Likely args are
 * current_x or 0. */
2922
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2923
{
2924
2925
2926
2927
2928
2929
    int line;
	/* line in the edit window for CURSES calls */
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
2930

2931
    assert(fileptr != NULL);
2932

2933
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2934

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

2938
2939
    if (line < 0 || line >= editwinrows)
	return;
2940

2941
2942
2943
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

2944
2945
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2946
    index = (fileptr == current) ? strnlenpt(fileptr->data, index) : 0;
2947
    page_start = get_page_start(index);
2948

2949
2950
2951
    /* Expand the line, replacing Tab by spaces, and control characters
     * by their display form. */
    converted = display_string(fileptr->data, page_start, COLS);
Chris Allegretta's avatar
Chris Allegretta committed
2952

Chris Allegretta's avatar
Chris Allegretta committed
2953
    /* Now, paint the line */
2954
    edit_add(fileptr, converted, line, page_start);
2955
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2956

2957
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2958
	mvwaddch(edit, line, 0, '$');
2959
    if (strlenpt(fileptr->data) > page_start + COLS)
2960
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
2961
2962
}

2963
2964
/* Return a nonzero value if we need an update after moving
 * horizontally.  We need one if the mark is on or if old_pww and
2965
 * placewewant are on different pages. */
2966
int need_horizontal_update(size_t old_pww)
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
{
    return
#ifndef NANO_SMALL
	ISSET(MARK_ISSET) ||
#endif
	get_page_start(old_pww) != get_page_start(placewewant);
}

/* Return a nonzero value if we need an update after moving vertically.
 * We need one if the mark is on or if old_pww and placewewant
2977
 * are on different pages. */
2978
int need_vertical_update(size_t old_pww)
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
{
    return
#ifndef NANO_SMALL
	ISSET(MARK_ISSET) ||
#endif
	get_page_start(old_pww) != get_page_start(placewewant);
}

/* 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,
 * and nlines is the number of lines to scroll.  Don't redraw the old
 * topmost or bottommost line (where we assume current is) before
 * scrolling or draw the new topmost or bottommost line after scrolling
 * (where we assume current will be), since we don't know where we are
2994
 * on the page or whether we'll stay there. */
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
void edit_scroll(updown direction, int nlines)
{
    filestruct *foo;
    int i, scroll_rows = 0;

    /* Scrolling less than one line or more than editwinrows lines is
     * redundant, so don't allow it. */
    if (nlines < 1 || nlines > editwinrows)
	return;

    /* Move the top line of the edit window up or down (depending on the
     * value of direction) nlines lines.  If there are fewer lines of
     * text than that left, move it to the top or bottom line of the
     * file (depending on the value of direction).  Keep track of
     * how many lines we moved in scroll_rows. */
    for (i = nlines; i > 0; i--) {
	if (direction == UP) {
	    if (edittop->prev == NULL)
		break;
	    edittop = edittop->prev;
	    scroll_rows--;
	} else {
	    if (edittop->next == NULL)
		break;
	    edittop = edittop->next;
	    scroll_rows++;
	}
    }

    /* Scroll the text on the screen up or down scroll_rows lines,
     * depending on the value of direction. */
    scrollok(edit, TRUE);
    wscrl(edit, scroll_rows);
    scrollok(edit, FALSE);

    foo = edittop;
    if (direction != UP) {
	int slines = editwinrows - nlines;
	for (; slines > 0 && foo != NULL; slines--)
	    foo = foo->next;
    }

    /* And draw new lines on the blank top or bottom lines of the edit
     * window, depending on the value of direction.  Don't draw the new
     * topmost or new bottommost line. */
    while (scroll_rows != 0 && foo != NULL) {
	if (foo->next != NULL)
	    update_line(foo, 0);
	if (direction == UP)
	    scroll_rows++;
	else
	    scroll_rows--;
	foo = foo->next;
    }
}

/* Update any lines between old_current and current that need to be
3052
 * updated. */
3053
void edit_redraw(const filestruct *old_current, size_t old_pww)
3054
{
3055
3056
    int do_refresh = need_vertical_update(0) ||
	need_vertical_update(old_pww);
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
    const filestruct *foo;

    /* If either old_current or current is offscreen, refresh the screen
     * and get out. */
    if (old_current->lineno < edittop->lineno || old_current->lineno >=
	edittop->lineno + editwinrows || current->lineno <
	edittop->lineno || current->lineno >= edittop->lineno +
	editwinrows) {
	edit_refresh();
	return;
    }

3069
3070
3071
    /* 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. */
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
    foo = old_current;
    while (foo != current) {
	if (do_refresh)
	    update_line(foo, 0);
#ifndef NANO_SMALL
	if (!ISSET(MARK_ISSET))
#endif
	    break;
	if (foo->lineno > current->lineno)
	    foo = foo->prev;
	else
	    foo = foo->next;
    }
    if (do_refresh)
	update_line(current, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
3089
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
3090
3091
void edit_refresh(void)
{
3092
3093
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
3094
3095
3096
3097
3098
3099
3100
3101
	/* Note that edit_update() changes edittop so that it's in range
	 * of current.  Thus, when it then calls edit_refresh(), there
	 * is no danger of getting an infinite loop. */
	edit_update(
#ifndef NANO_SMALL
		ISSET(SMOOTHSCROLL) ? NONE :
#endif
		CENTER);
3102
3103
    else {
	int nlines = 0;
3104
	const filestruct *foo = edittop;
Chris Allegretta's avatar
Chris Allegretta committed
3105

3106
#ifdef DEBUG
3107
	fprintf(stderr, "edit_refresh(): edittop->lineno = %d\n", edittop->lineno);
3108
#endif
3109

3110
	while (nlines < editwinrows) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3111
	    update_line(foo, foo == current ? current_x : 0);
3112
	    nlines++;
3113
	    if (foo->next == NULL)
3114
		break;
3115
	    foo = foo->next;
3116
3117
3118
3119
3120
	}
	while (nlines < editwinrows) {
	    mvwaddstr(edit, nlines, 0, hblank);
	    nlines++;
	}
3121
	reset_cursor();
3122
3123
	wrefresh(edit);
    }
Chris Allegretta's avatar
Chris Allegretta committed
3124
3125
}

3126
3127
/* A nice generic routine to update the edit buffer.  We keep current in
 * the same place and move edittop to put it in range of current. */
3128
void edit_update(topmidnone location)
Chris Allegretta's avatar
Chris Allegretta committed
3129
{
3130
3131
    filestruct *foo = current;

Chris Allegretta's avatar
Chris Allegretta committed
3132
    if (location != TOP) {
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
	/* 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. */
	int goal;

	if (location == CENTER)
	    goal = editwinrows / 2;
	else {
	    goal = current_y;

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

3153
3154
	for (; goal > 0 && foo->prev != NULL; goal--)
	    foo = foo->prev;
Chris Allegretta's avatar
Chris Allegretta committed
3155
    }
3156

3157
    edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
3158
3159
3160
    edit_refresh();
}

3161
3162
3163
/* Ask a simple yes/no question, specified in msg, on the statusbar.
 * Return 1 for Y, 0 for N, 2 for All (if all is TRUE when passed in)
 * and -1 for abort (^C). */
3164
int do_yesno(bool all, const char *msg)
Chris Allegretta's avatar
Chris Allegretta committed
3165
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3166
    int ok = -2, width = 16;
3167
3168
    const char *yesstr;		/* String of yes characters accepted. */
    const char *nostr;		/* Same for no. */
Chris Allegretta's avatar
Chris Allegretta committed
3169
    const char *allstr;		/* And all, surprise! */
Chris Allegretta's avatar
Chris Allegretta committed
3170

3171
3172
3173
3174
3175
3176
    /* yesstr, nostr, and allstr are strings of any length.  Each string
     * consists of all characters accepted as a valid character for that
     * value.  The first value will be the one displayed in the
     * shortcuts.  Translators: if possible, specify both the shortcuts
     * for your language and English.  For example, in French: "OoYy"
     * for "Oui". */
3177
3178
3179
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
3180
3181

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

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3184
3185
3186
	if (COLS < 32)
	    width = COLS / 2;

3187
	/* Write the bottom of the screen. */
3188
	blank_bottombars();
3189

3190
	sprintf(shortstr, " %c", yesstr[0]);
3191
	wmove(bottomwin, 1, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3192
	onekey(shortstr, _("Yes"), width);
3193
3194

	if (all) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3195
	    wmove(bottomwin, 1, width);
3196
	    shortstr[1] = allstr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3197
	    onekey(shortstr, _("All"), width);
3198
3199
	}

3200
	wmove(bottomwin, 2, 0);
3201
	shortstr[1] = nostr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3202
	onekey(shortstr, _("No"), width);
3203

3204
	wmove(bottomwin, 2, 16);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3205
	onekey("^C", _("Cancel"), width);
Chris Allegretta's avatar
Chris Allegretta committed
3206
    }
3207

Chris Allegretta's avatar
Chris Allegretta committed
3208
    wattron(bottomwin, A_REVERSE);
3209
3210

    blank_statusbar();
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3211
    mvwaddnstr(bottomwin, 0, 0, msg, COLS - 1);
3212

Chris Allegretta's avatar
Chris Allegretta committed
3213
    wattroff(bottomwin, A_REVERSE);
3214

Chris Allegretta's avatar
Chris Allegretta committed
3215
3216
    wrefresh(bottomwin);

3217
    do {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3218
	int kbinput;
3219
	bool meta_key, func_key;
3220
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3221
	int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
3222
#endif
3223

3224
	kbinput = get_kbinput(edit, &meta_key, &func_key);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3225
3226

	if (kbinput == NANO_CANCEL_KEY)
3227
	    ok = -1;
3228
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3229
	/* Look ma!  We get to duplicate lots of code from
3230
	 * get_edit_mouse()!! */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3231
	else if (kbinput == KEY_MOUSE) {
3232
	    get_mouseinput(&mouse_x, &mouse_y, FALSE);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251

	    if (mouse_x != -1 && mouse_y != -1 && !ISSET(NO_HELP) &&
		wenclose(bottomwin, mouse_y, mouse_x) && mouse_x <
		(width * 2) && mouse_y >= editwinrows + 3) {
		int x = mouse_x / width;
		    /* Did we click in the first column of shortcuts, or
		     * the second? */
		int y = mouse_y - editwinrows - 3;
		    /* Did we click in the first row of shortcuts? */

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

		/* x = 0 means they clicked Yes or No.
		 * y = 0 means Yes or All. */
		ok = -2 * x * y + x - y + 1;

		if (ok == 2 && !all)
		    ok = -2;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
3252
	}
3253
#endif
3254
	/* Look for the kbinput in the yes, no and (optionally) all
3255
	 * strings. */
3256
3257
3258
3259
3260
3261
3262
3263
3264
	else if (strchr(yesstr, kbinput) != NULL)
	    ok = 1;
	else if (strchr(nostr, kbinput) != NULL)
	    ok = 0;
	else if (all && strchr(allstr, kbinput) != NULL)
	    ok = 2;
    } while (ok == -2);

    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
3265
3266
}

3267
void total_refresh(void)
3268
3269
{
    clearok(topwin, TRUE);
3270
    clearok(edit, TRUE);
3271
3272
    clearok(bottomwin, TRUE);
    wnoutrefresh(topwin);
3273
    wnoutrefresh(edit);
3274
3275
3276
    wnoutrefresh(bottomwin);
    doupdate();
    clearok(topwin, FALSE);
3277
    clearok(edit, FALSE);
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
    clearok(bottomwin, FALSE);
    edit_refresh();
    titlebar(NULL);
}

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

3288
3289
3290
/* If constant is FALSE, the user typed Ctrl-C, so we unconditionally
 * display the cursor position.  Otherwise, we display it only if the
 * character position changed and DISABLE_CURPOS is not set.
3291
 *
3292
3293
3294
 * If constant is TRUE and DISABLE_CURPOS is set, we unset it and update
 * old_i and old_totsize.  That way, we leave the current statusbar
 * alone, but next time we will display. */
3295
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3296
{
3297
    const filestruct *fileptr;
3298
3299
    size_t i = 0;
    static size_t old_i = 0;
3300
    static long old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
3301

3302
    assert(current != NULL && fileage != NULL && totlines != 0);
3303
3304
3305
3306

    if (old_totsize == -1)
	old_totsize = totsize;

3307
3308
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
3309
	i += strlen(fileptr->data) + 1;
3310
    }
3311
    i += current_x;
3312

3313
3314
3315
3316
    /* Check whether totsize is correct.  Else there is a bug
     * somewhere. */
    assert(current != filebot || i == totsize);

3317
3318
3319
3320
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
3321
	return;
3322
    }
Chris Allegretta's avatar
Chris Allegretta committed
3323

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3324
    /* If constant is FALSE, display the position on the statusbar
3325
3326
     * unconditionally; otherwise, only display the position when the
     * character values have changed. */
3327
    if (!constant || old_i != i || old_totsize != totsize) {
3328
3329
	size_t xpt = xplustabs() + 1;
	size_t cur_len = strlenpt(current->data) + 1;
3330
3331
3332
3333
3334
3335
3336
	int linepct = 100 * current->lineno / totlines;
	int colpct = 100 * xpt / cur_len;
	int bytepct = totsize == 0 ? 0 : 100 * i / totsize;

	statusbar(
	    _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
		    current->lineno, totlines, linepct,
3337
		    (unsigned long)xpt, (unsigned long)cur_len, colpct,
3338
		    (unsigned long)i, totsize, bytepct);
3339
	UNSET(DISABLE_CURPOS);
3340
3341
3342
3343
    }

    old_i = i;
    old_totsize = totsize;
Chris Allegretta's avatar
Chris Allegretta committed
3344
3345
}

3346
void do_cursorpos_void(void)
3347
{
3348
    do_cursorpos(FALSE);
3349
3350
}

3351
#ifndef DISABLE_HELP
3352
/* Calculate the next line of help_text, starting at ptr. */
3353
int help_line_len(const char *ptr)
3354
{
3355
    int j = 0;
3356
3357
3358
3359
3360
3361
3362
3363
3364

    while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
	ptr++;
	j++;
    }
    if (j == COLS - 5) {
	/* Don't wrap at the first of two spaces following a period. */
	if (*ptr == ' ' && *(ptr + 1) == ' ')
	    j++;
3365
	/* Don't print half a word if we've run out of space. */
3366
3367
3368
3369
	while (*ptr != ' ' && j > 0) {
	    ptr--;
	    j--;
	}
3370
	/* Word longer than COLS - 5 chars just gets broken. */
3371
3372
3373
3374
3375
3376
3377
	if (j == 0)
	    j = COLS - 5;
    }
    assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
    return j;
}

3378
/* Our dynamic, shortcut-list-compliant help function. */
3379
void do_help(void)
Chris Allegretta's avatar
Chris Allegretta committed
3380
{
3381
3382
3383
    int line = 0;
	/* The line number in help_text of the first displayed help line.
	 * This variable is zero-based. */
3384
    bool no_more = FALSE;
3385
3386
	/* no_more means the end of the help text is shown, so don't go
	 * down any more. */
3387
    int kbinput = ERR;
3388
    bool meta_key, func_key;
3389

3390
    bool old_no_help = ISSET(NO_HELP);
3391
3392
3393
#ifndef DISABLE_MOUSE
    const shortcut *oldshortcut = currshortcut;
	/* We will set currshortcut to allow clicking on the help
3394
	 * screen's shortcut list. */
3395
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3396

3397
    curs_set(0);
Chris Allegretta's avatar
Chris Allegretta committed
3398
    blank_edit();
3399
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
3400
3401
    blank_statusbar();

3402
    /* Set help_text as the string to display. */
3403
    help_init();
3404
    assert(help_text != NULL);
3405

3406
#ifndef DISABLE_MOUSE
3407
    /* Set currshortcut to allow clicking on the help screen's shortcut
3408
     * list, AFTER help_init(). */
3409
    currshortcut = help_list;
3410
#endif
3411

Chris Allegretta's avatar
Chris Allegretta committed
3412
    if (ISSET(NO_HELP)) {
3413
3414
	/* Make sure that the help screen's shortcut list will actually
	 * be displayed. */
Chris Allegretta's avatar
Chris Allegretta committed
3415
	UNSET(NO_HELP);
3416
	window_init();
3417
3418
    }
    bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
3419
3420

    do {
3421
3422
3423
	int i;
	int old_line = line;
	    /* We redisplay the help only if it moved. */
3424
	const char *ptr = help_text;
3425

Chris Allegretta's avatar
Chris Allegretta committed
3426
	switch (kbinput) {
3427
#ifndef DISABLE_MOUSE
3428
	    case KEY_MOUSE:
3429
3430
3431
3432
		{
		    int mouse_x, mouse_y;
		    get_mouseinput(&mouse_x, &mouse_y, TRUE);
		}
3433
		break;
3434
#endif
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
	    case NANO_NEXTPAGE_KEY:
	    case NANO_NEXTPAGE_FKEY:
		if (!no_more)
		    line += editwinrows - 2;
		break;
	    case NANO_PREVPAGE_KEY:
	    case NANO_PREVPAGE_FKEY:
		if (line > 0) {
		    line -= editwinrows - 2;
		    if (line < 0)
			line = 0;
		}
		break;
	    case NANO_PREVLINE_KEY:
		if (line > 0)
		    line--;
		break;
	    case NANO_NEXTLINE_KEY:
		if (!no_more)
		    line++;
		break;
Chris Allegretta's avatar
Chris Allegretta committed
3456
3457
	}

3458
3459
3460
3461
3462
3463
3464
	if (line == old_line && kbinput != ERR)
	    goto skip_redisplay;

	blank_edit();

	assert(COLS > 5);

3465
3466
	/* Calculate where in the text we should be, based on the
	 * page. */
3467
	for (i = 0; i < line; i++) {
3468
	    ptr += help_line_len(ptr);
3469
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
3470
3471
3472
		ptr++;
	}

3473
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
3474
	    int j = help_line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
3475
3476

	    mvwaddnstr(edit, i, 0, ptr, j);
3477
3478
3479
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
3480
	}
3481
	no_more = (*ptr == '\0');
3482

3483
  skip_redisplay:
3484
	kbinput = get_kbinput(edit, &meta_key, &func_key);
3485
3486
    } while (kbinput != NANO_CANCEL_KEY && kbinput != NANO_EXIT_KEY &&
	kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
3487

3488
#ifndef DISABLE_MOUSE
3489
    currshortcut = oldshortcut;
3490
#endif
3491

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3492
    if (old_no_help) {
3493
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
3494
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
3495
	SET(NO_HELP);
3496
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
3497
    } else
3498
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
3499

3500
    curs_set(1);
Chris Allegretta's avatar
Chris Allegretta committed
3501
    edit_refresh();
3502

3503
3504
3505
    /* The help_init() at the beginning allocated help_text.  Since 
     * help_text has now been written to the screen, we don't need it
     * anymore. */
3506
3507
    free(help_text);
    help_text = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
3508
}
3509
#endif /* !DISABLE_HELP */
Chris Allegretta's avatar
Chris Allegretta committed
3510

3511
3512
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
Chris Allegretta's avatar
Chris Allegretta committed
3513
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3514
{
3515
    size_t y = xplustabs();
3516
    size_t word_len = strlen(word);
Chris Allegretta's avatar
Chris Allegretta committed
3517

3518
3519
3520
    y = get_page_start(y) + COLS - y;
	/* Now y is the number of characters we can display on this
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3521
3522

    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
3523

Chris Allegretta's avatar
Chris Allegretta committed
3524
3525
3526
    if (highlight_flag)
	wattron(edit, A_REVERSE);

3527
#ifdef HAVE_REGEX_H
3528
3529
3530
3531
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
	waddstr(edit, " ");
    else
3532
#endif
3533
	waddnstr(edit, word, y - 1);
3534
3535
3536
3537
3538

    if (word_len > y)
	waddch(edit, '$');
    else if (word_len == y)
	waddch(edit, word[word_len - 1]);
Chris Allegretta's avatar
Chris Allegretta committed
3539
3540
3541
3542
3543

    if (highlight_flag)
	wattroff(edit, A_REVERSE);
}

3544
#ifdef DEBUG
3545
3546
3547
/* Dump the passed-in file structure to stderr. */
void dump_buffer(const filestruct *inptr)
{
3548
    if (inptr == fileage)
3549
	fprintf(stderr, "Dumping file buffer to stderr...\n");
3550
    else if (inptr == cutbuffer)
3551
	fprintf(stderr, "Dumping cutbuffer to stderr...\n");
3552
    else
3553
	fprintf(stderr, "Dumping a buffer to stderr...\n");
3554
3555
3556
3557
3558
3559
3560

    while (inptr != NULL) {
	fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data);
	inptr = inptr->next;
    }
}

3561
/* Dump the file structure to stderr in reverse. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3562
3563
void dump_buffer_reverse(void)
{
3564
3565
3566
    const filestruct *fileptr = filebot;

    while (fileptr != NULL) {
3567
	fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
3568
3569
3570
3571
3572
	fileptr = fileptr->prev;
    }
}
#endif /* DEBUG */

3573
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3574
#define CREDIT_LEN 53
3575
3576
#define XLCREDIT_LEN 8

3577
/* Easter egg: Display credits.  Assume nodelay(edit) is FALSE. */
3578
3579
void do_credits(void)
{
3580
3581
3582
3583
    int crpos = 0, xlpos = 0;
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3584
3585
	VERSION,
	"",
3586
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3587
3588
3589
3590
3591
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3592
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3593
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
	"Ken Tyler",
	"Sven Guckes",
	"Florian König",
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
3609
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
3610
3611
3612
3613
3614
3615
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3616
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3617
	"Linus Torvalds",
3618
	NULL,				/* "For ncurses:" */
3619
3620
3621
3622
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3623
3624
3625
3626
3627
3628
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3629
	"(c) 1999-2004 Chris Allegretta",
3630
3631
3632
3633
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3634
	"http://www.nano-editor.org/"
3635
3636
    };

3637
    const char *xlcredits[XLCREDIT_LEN] = {
3638
3639
3640
3641
3642
3643
3644
3645
	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!")
3646
    };
3647

3648
3649
    curs_set(0);
    nodelay(edit, TRUE);
3650
3651
    scrollok(edit, TRUE);
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
3652
    blank_edit();
3653
3654
3655
    blank_statusbar();
    blank_bottombars();
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3656
    wrefresh(edit);
3657
3658
    wrefresh(bottomwin);

3659
3660
3661
3662
3663
3664
3665
3666
3667
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
	if (wgetch(edit) != ERR)
	    break;
	if (crpos < CREDIT_LEN) {
	    const char *what = credits[crpos];
	    size_t start_x;

	    if (what == NULL) {
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3668
		what = _(xlcredits[xlpos]);
3669
		xlpos++;
Chris Allegretta's avatar
Chris Allegretta committed
3670
	    }
3671
3672
	    start_x = COLS / 2 - strlen(what) / 2 - 1;
	    mvwaddstr(edit, editwinrows - 1 - editwinrows % 2, start_x, what);
3673
	}
3674
3675
3676
3677
	napms(700);
	scroll(edit);
	wrefresh(edit);
	if (wgetch(edit) != ERR)
3678
	    break;
3679
3680
3681
	napms(700);
	scroll(edit);
	wrefresh(edit);
3682
3683
    }

3684
    scrollok(edit, FALSE);
3685
3686
3687
3688
    nodelay(edit, FALSE);
    curs_set(1);
    display_main_list();
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3689
}
3690
#endif