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
180
181
182
183
	/* If we got a one-character sequence and it's outside the ASCII
	 * range, set func_key to TRUE. */
	if (seq == NO_SEQ) {
	    if (retval > 255)
		*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;
184

185
186
	    sequence = get_verbatim_kbinput(win, kbinput, sequence,
		&seq_len, FALSE);
187

188
189
190
191
192
193
194
195
196
	    /* 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
197
		 * long, set func_key to TRUE, translate the escape
198
199
200
201
		 * sequence into the corresponding key value, and save
		 * that as the result. */
		} else if (seq_len > 1) {
		    bool ignore_seq;
202

203
		    *func_key = TRUE;
204
		    retval = get_escape_seq_kbinput(sequence, seq_len,
205
206
			&ignore_seq);

207
208
209
210
		    if (retval == ERR && !ignore_seq) {
			/* This escape sequence is unrecognized.  Send
			 * it back. */
			for (; seq_len > 1; seq_len--)
211
212
			    unget_kbinput(sequence[seq_len - 1], FALSE,
				FALSE);
213
214
			retval = sequence[0];
		    }
215
		}
216
217
	    /* Handle UTF-8 sequences. */
	    } else if (seq == UTF8_SEQ) {
218
219
220
		/* If we have a UTF-8 sequence, translate the UTF-8
		 * sequence into the corresponding wide character value,
		 * and save that as the result. */
221
222
223
224
225
226
227
228
229
230
231
232
		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--)
233
234
			unget_kbinput(sequence[seq_len - 1], FALSE,
				FALSE);
235
236
237
		    retval = sequence[0];
		} else
		    retval = wc;
238
		free(s);
239
	    }
240
	    free(sequence);
241
242
243
	}
    }

244
#ifdef DEBUG
245
    fprintf(stderr, "get_kbinput(): kbinput = %d, meta_key = %d, func_key = %d\n", kbinput, (int)*meta_key, (int)*func_key);
246
#endif
247

248
249
250
251
#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif

252
    return retval;
253
254
}

255
256
257
258
259
/* 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
260
#ifndef NANO_SMALL
261
	, bool reset
262
263
#endif
	)
264
{
265
266
    static int escapes = 0;
    static size_t ascii_digits = 0;
267
    int retval = ERR;
268

269
270
271
272
273
#ifndef NANO_SMALL
    if (reset) {
	escapes = 0;
	ascii_digits = 0;
	return ERR;
274
    }
275
#endif
276

277
    *seq = NO_SEQ;
278
279

    switch (kbinput) {
280
281
282
283
284
285
286
287
288
289
	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. */
290
291
		    break;
		default:
292
293
294
		    /* More than two escapes: reset the escape counter
		     * and wait for more input. */
		    escapes = 0;
295
296
	    }
	    break;
297
298
299
300
301
#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:
302
	    break;
303
304
305
306
307
308
309
310
#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:
311
	    break;
312
#endif
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
	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;
333
#ifdef KEY_HOME
334
335
336
337
			/* HP-UX 10 and 11 don't support KEY_HOME. */
			case KEY_HOME:
			    retval = NANO_HOME_KEY;
			    break;
338
#endif
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
			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;
358
359
360
361
362
363
364
365
			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;
366
			case KEY_B2:	/* Center (5) on numeric keypad
367
368
369
370
371
372
373
374
375
376
					 * 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;
377
#ifdef KEY_BEG
378
379
			/* Slang doesn't support KEY_BEG. */
			case KEY_BEG:	/* Center (5) on numeric keypad
380
					 * with NumLock off. */
381
			    break;
382
#endif
383
#ifdef KEY_END
384
385
386
387
			/* HP-UX 10 and 11 don't support KEY_END. */
			case KEY_END:
			    retval = NANO_END_KEY;
			    break;
388
389
#endif
#ifdef KEY_SUSPEND
390
391
392
393
			/* Slang doesn't support KEY_SUSPEND. */
			case KEY_SUSPEND:
			    retval = NANO_SUSPEND_KEY;
			    break;
394
395
#endif
#ifdef KEY_SLEFT
396
397
398
399
			/* Slang doesn't support KEY_SLEFT. */
			case KEY_SLEFT:
			    retval = NANO_BACK_KEY;
			    break;
400
401
#endif
#ifdef KEY_SRIGHT
402
403
404
405
			/* Slang doesn't support KEY_SRIGHT. */
			case KEY_SRIGHT:
			    retval = NANO_FORWARD_KEY;
			    break;
406
#endif
407
408
409
410
411
412
413
414
			default:
			    retval = kbinput;
			    break;
		    }
		    break;
		case 1:
		    /* One escape followed by a non-escape: escape
		     * sequence mode.  Reset the escape counter and set
415
		     * seq to ESCAPE_SEQ. */
416
		    escapes = 0;
417
		    *seq = ESCAPE_SEQ;
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
		    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;
				}
			    }
461
			    break;
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
			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;
			    }
		    }
	    }
485
    }
486

487
488
489
490
491
    /* 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;

492
#ifdef DEBUG
493
    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);
494
#endif
495
496
497

    /* Return the result. */
    return retval;
498
499
}

500
501
502
503
/* 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
504
	, bool reset
505
506
#endif
	)
507
{
508
509
    static int ascii_kbinput = 0;
    int retval = ERR;
510

511
512
513
514
#ifndef NANO_SMALL
    if (reset) {
	ascii_kbinput = 0;
	return ERR;
515
    }
516
#endif
517

518
519
520
521
522
523
524
525
526
527
528
529
530
531
    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;
    	    }
532
	    break;
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
	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;
556
557
	    }
	    break;
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
	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;
584
	    }
585
	    break;
586
587
588
    }

#ifdef DEBUG
589
    fprintf(stderr, "get_ascii_kbinput(): kbinput = %d, ascii_digits = %lu, ascii_kbinput = %d, retval = %d\n", kbinput, (unsigned long)ascii_digits, ascii_kbinput, retval);
590
#endif
591
592
593
594
595
596
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

    /* 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
628
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
629
630
#endif

631
632
633
    return retval;
}

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

646
647
    *ignore_seq = FALSE;

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

1136
#ifdef DEBUG
1137
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d, ignore_seq = %d\n", retval, (int)*ignore_seq);
1138
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1139

1140
    return retval;
1141
1142
}

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

1162
/* Read in a string of input characters (e.g. an escape sequence)
1163
1164
1165
1166
1167
 * 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)
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
{
    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);

1186
1187
1188
    /* 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
1189
     * v_len and save the character in v_kbinput. */
1190
1191
1192
1193
    if (first == ERR)
	kbinput = wgetch(win);
    else
	kbinput = first;
1194
1195
1196
    (*v_len)++;
    v_kbinput[0] = kbinput;
#ifdef DEBUG
1197
    fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len);
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
#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
1212
	fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len);
1213
1214
1215
1216
1217
1218
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
#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;
}

1278
int get_untranslated_kbinput(int kbinput, size_t position, bool
1279
1280
	allow_ascii
#ifndef NANO_SMALL
1281
	, bool reset
1282
1283
1284
1285
1286
1287
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
#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
1331
    fprintf(stderr, "get_untranslated_kbinput(): kbinput = %d, position = %lu, ascii_digits = %lu\n", kbinput, (unsigned long)position, (unsigned long)ascii_digits);
1332
1333
1334
1335
1336
#endif

    return retval;
}

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

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

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1355
	return FALSE;
1356
1357
1358
1359
1360

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

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

1380
1381
1382
1383
1384
1385
	    /* We don't show any more shortcuts than the main list
	     * does. */
	    if (currslen > MAIN_VISIBLE)
		currslen = MAIN_VISIBLE;
	}

1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
	/* 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)
1401
	    return FALSE;
1402
1403
	j = (*mouse_x / i) * 2 + j;
	if (j >= currslen)
1404
	    return FALSE;
1405
1406
1407
1408
1409
1410

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

1411
1412
1413
	/* 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. */
1414
	if (s->ctrlval != NANO_NO_KEY)
1415
	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1416
	else if (s->metaval != NANO_NO_KEY)
1417
	    unget_kbinput(s->metaval, TRUE, FALSE);
1418

1419
	return TRUE;
1420
    }
1421
    return FALSE;
1422
}
1423
1424
1425
#endif /* !DISABLE_MOUSE */

const shortcut *get_shortcut(const shortcut *s_list, int kbinput, bool
1426
	*meta_key, bool *func_key)
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
{
    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.
1437
1438
1439
1440
1441
	 * 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. */

1442
	if (kbinput != NANO_NO_KEY && (kbinput == s->ctrlval ||
1443
1444
		(*meta_key == TRUE && (kbinput == s->metaval ||
		kbinput == s->miscval)) || (*func_key == TRUE &&
1445
		kbinput == s->funcval))) {
1446
1447
1448
1449
1450
1451
1452
1453
	    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
1454
     * key, an equivalent primary meta key sequence, or both. */
1455
1456
1457
1458
1459
1460
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
    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 */

1486
int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs)
1487
1488
1489
1490
1491
1492
1493
1494
{
    bool keyhandled = FALSE;
    int kbinput, retval;
    const shortcut *s;
#ifndef NANO_SMALL
    const toggle *t;
#endif

1495
    kbinput = get_kbinput(edit, meta_key, func_key);
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514

    /* 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()) {
1515
		kbinput = get_kbinput(edit, meta_key, func_key);
1516
1517
1518
1519
1520
1521
1522
		break;
	    } else
		return ERR;
#endif
    }

    /* Check for a shortcut in the main list. */
1523
    s = get_shortcut(main_list, kbinput, meta_key, func_key);
1524
1525
1526
1527
1528
1529
1530

    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) {
1531
1532
1533
1534
1535
1536
	    if (allow_funcs) {
		if (ISSET(VIEW_MODE) && !s->viewok)
		    print_view_warning();
		else
		    s->func();
	    }
1537
1538
1539
1540
1541
1542
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
	    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();
	    }
1614
1615
#endif

1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
	    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
1629
1630
1631
1632
/* 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
1633
{
Chris Allegretta's avatar
Chris Allegretta committed
1634
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
1635
1636
}

1637
1638
1639
1640
/* 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
1641
{
Chris Allegretta's avatar
Chris Allegretta committed
1642
    size_t i = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1643
	/* The position in str, returned. */
Chris Allegretta's avatar
Chris Allegretta committed
1644
    size_t length = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1645
	/* The screen display width to str[i]. */
Chris Allegretta's avatar
Chris Allegretta committed
1646

1647
    assert(str != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
1648

1649
1650
    for (; length < xplus && *str != '\0'; i++, str++) {
	if (*str == '\t')
1651
	    length += tabsize - (length % tabsize);
1652
	else if (is_cntrl_char(*str))
Chris Allegretta's avatar
Chris Allegretta committed
1653
1654
1655
1656
	    length += 2;
	else
	    length++;
    }
1657
1658
    assert(length == strnlenpt(str - i, i));
    assert(i <= strlen(str - i));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1659

Chris Allegretta's avatar
Chris Allegretta committed
1660
1661
    if (length > xplus)
	i--;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1662

Chris Allegretta's avatar
Chris Allegretta committed
1663
    return i;
1664
1665
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1666
/* A strlen() with tabs factored in, similar to xplustabs().  How many
1667
 * columns wide are the first size characters of buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1668
size_t strnlenpt(const char *buf, size_t size)
1669
{
Chris Allegretta's avatar
Chris Allegretta committed
1670
1671
    size_t length = 0;

1672
1673
1674
1675
    assert(buf != NULL);
    for (; *buf != '\0' && size != 0; size--, buf++) {
	if (*buf == '\t')
	    length += tabsize - (length % tabsize);
1676
	else if (is_cntrl_char(*buf))
1677
1678
1679
1680
	    length += 2;
	else
	    length++;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1681
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
1682
1683
}

1684
/* How many columns wide is buf? */
Chris Allegretta's avatar
Chris Allegretta committed
1685
size_t strlenpt(const char *buf)
1686
{
1687
    return strnlenpt(buf, (size_t)-1);
1688
1689
}

1690
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
1691
{
1692
    mvwaddstr(topwin, 0, 0, hblank);
1693
1694
}

Chris Allegretta's avatar
Chris Allegretta committed
1695
1696
void blank_edit(void)
{
1697
    int i;
1698
    for (i = 0; i < editwinrows; i++)
Chris Allegretta's avatar
Chris Allegretta committed
1699
1700
1701
1702
1703
1704
1705
1706
	mvwaddstr(edit, i, 0, hblank);
}

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

1707
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
1708
{
1709
1710
1711
1712
    if (statusblank > 1)
	statusblank--;
    else if (statusblank == 1 && !ISSET(CONSTUPDATE)) {
	statusblank = 0;
1713
1714
1715
1716
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
	wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
1717
1718
1719
    }
}

1720
1721
1722
1723
1724
1725
1726
1727
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
}

1728
1729
1730
1731
1732
/* 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. */
1733
char *display_string(const char *buf, size_t start_col, size_t len)
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
{
    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++) {
1763
1764
1765
1766
1767
1768
1769
	if (*buf == '\t') {
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[0] :
#endif
		' '; 
	    while ((column + index) % tabsize)
1770
		converted[index++] = ' ';
1771
	} else if (is_cntrl_char(*buf)) {
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
	    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;
1782
1783
1784
1785
1786
1787
1788
	} else if (*buf == ' ')
	    converted[index++] =
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
		ISSET(WHITESPACE_DISPLAY) ? whitespace[1] :
#endif
		' ';
	else
1789
1790
1791
1792
1793
1794
1795
1796
1797
	    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
1798
/* Repaint the statusbar when getting a character in nanogetstr().  buf
1799
 * should be no longer than max(0, COLS - 4).
Chris Allegretta's avatar
Chris Allegretta committed
1800
 *
Chris Allegretta's avatar
Chris Allegretta committed
1801
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
1802
 * off! */
1803
void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
1804
{
1805
1806
    size_t x_real = strnlenpt(inputbuf, x);
    int wid = COLS - strlen(buf) - 2;
1807

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

1810
    wattron(bottomwin, A_REVERSE);
1811
    blank_statusbar();
1812

Chris Allegretta's avatar
Chris Allegretta committed
1813
1814
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828

    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);
1829
    wattroff(bottomwin, A_REVERSE);
1830
1831
}

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

1854
1855
1856
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
1857
    char *currentbuf = NULL;
1858
    char *complete = NULL;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1859
1860
1861
1862
1863
1864
1865
1866
1867
    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;
1868
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1869
    xend = strlen(def);
Chris Allegretta's avatar
Chris Allegretta committed
1870

1871
    /* Only put x at the end of the string if it's uninitialized, if it
1872
1873
1874
1875
       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. */
1876
    if (x == (size_t)-1 || x > xend || resetstatuspos)
Chris Allegretta's avatar
Chris Allegretta committed
1877
1878
	x = xend;

Chris Allegretta's avatar
Chris Allegretta committed
1879
    answer = charealloc(answer, xend + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1880
1881
1882
1883
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1884

1885
#if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
1886
    currshortcut = s;
1887
1888
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1889
    /* Get the input! */
1890

Chris Allegretta's avatar
Chris Allegretta committed
1891
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
1892

1893
1894
    /* Make sure any editor screen updates are displayed before getting
       input */
1895
1896
    wnoutrefresh(edit);
    wrefresh(bottomwin);
1897

1898
1899
1900
1901
1902
    /* 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. */
1903
    while ((kbinput = get_kbinput(bottomwin, &meta_key, &func_key)) !=
1904
	NANO_CANCEL_KEY && kbinput != NANO_ENTER_KEY) {
1905
	for (t = s; t != NULL; t = t->next) {
1906
#ifdef DEBUG
1907
	    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
1908
1909
#endif

1910
1911
1912
	    /* Temporary hack to interpret NANO_HELP_FKEY correctly. */
	    if (kbinput == t->funcval)
		kbinput = t->ctrlval;
1913

1914
	    if (kbinput == t->ctrlval && is_cntrl_char(kbinput)) {
1915

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

1930
		return t->ctrlval;
Chris Allegretta's avatar
Chris Allegretta committed
1931
1932
	    }
	}
1933
	assert(x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
1934

Chris Allegretta's avatar
Chris Allegretta committed
1935
	if (kbinput != '\t')
1936
	    tabbed = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
1937

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

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

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

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
		x += shift;
		if (x > xend)
		    x = xend;
2031
2032
2033
	    }
#endif
	    break;
2034
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
2035
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2036
2037
		x--;
	    break;
2038
	case NANO_PREVLINE_KEY:
2039
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2040
	    if (history_list != NULL) {
2041

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2042
2043
2044
2045
2046
2047
		/* 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 */
2048
2049
		if (currentbuf == NULL || (use_cb == 1 &&
			strcmp(currentbuf, answer) != 0)) {
2050
		    currentbuf = mallocstrcpy(currentbuf, answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2051
		    use_cb = 0;
2052
2053
		}

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2054
2055
2056
2057
2058
2059
		/* 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 */
2060
2061
		if (currentbuf != NULL && use_cb == 2 &&
			strcmp(currentbuf, answer) != 0) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
		    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) {
2072
2073
2074
2075
2076
2077
2078
2079
2080
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
2081
	    break;
2082
	case NANO_NEXTLINE_KEY:
2083
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
2084
	    if (history_list != NULL) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2085
2086
2087

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

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2092
2093
2094
2095
2096
2097
2098
2099
2100
		/* 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) {
2101
		    answer = mallocstrcpy(answer, currentbuf);
Chris Allegretta's avatar
Chris Allegretta committed
2102
2103
2104
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2105
2106
2107
2108
		    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
2109
2110
2111
2112
		   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
2113
		} else if (use_cb != 2) {
2114
2115
2116
2117
		    if (answer[0] != '\0') {
			currentbuf = mallocstrcpy(currentbuf, answer);
			answer = mallocstrcpy(answer, "");
		    }
2118
		    xend = 0;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2119
		    use_cb = 2;
2120
2121
2122
2123
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2124
	    break;
2125
2126
	    default:

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

2140
2141
2142
2143
	    /* 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. */
2144
	    if (is_cntrl_char(kbinput) || (ISSET(RESTRICTED) && filename[0] != '\0' && s == writefile_list))
Chris Allegretta's avatar
Chris Allegretta committed
2145
		break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2146
	    answer = charealloc(answer, xend + 2);
2147
	    charmove(answer + x + 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
2148
2149
	    xend++;
	    answer[x] = kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2150
2151
2152
	    x++;

#ifdef DEBUG
2153
	    fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
2154
#endif
2155
	} /* switch (kbinput) */
2156
#ifndef NANO_SMALL
2157
	last_kbinput = kbinput;
2158
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2159
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
2160
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
2161
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
2162

Chris Allegretta's avatar
Chris Allegretta committed
2163
    /* We finished putting in an answer; reset x */
2164
    x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
2165

2166
    return kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2167
2168
}

2169
2170
2171
2172
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
/* 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) {
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
	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;
2218
#ifndef DISABLE_JUSTIFY
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
	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;
2237
#endif
2238
2239
2240
2241
2242
2243
2244
2245
	case NANO_CANCEL_KEY:
	    ret = -1;
	    resetstatuspos = TRUE;
	    break;
	case NANO_ENTER_KEY:
	    ret = (answer[0] == '\0') ? -2 : 0;
	    resetstatuspos = TRUE;
	    break;
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
    }
    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;
}

2269
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2270
{
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
    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
2295
2296

    wattron(topwin, A_REVERSE);
2297

2298
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2299

2300
2301
2302
2303
    if (COLS <= 5 || COLS - 5 < verlen)
	space = 0;
    else {
	space = COLS - 5 - verlen;
2304
2305
2306
2307
	/* 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;
2308
    }
Chris Allegretta's avatar
Chris Allegretta committed
2309

2310
    if (COLS > 4) {
2311
2312
2313
	/* The version message should only take up 1/3 of the screen
	 * minus one column. */
	mvwaddnstr(topwin, 0, 2, VERMSG, (COLS / 3) - 3);
2314
2315
	waddstr(topwin, "  ");
    }
Chris Allegretta's avatar
Chris Allegretta committed
2316
2317

    if (ISSET(MODIFIED))
2318
2319
2320
2321
2322
2323
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
	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
2353
    space -= prefixlen + statelen;
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
	/* 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
2379
	if (space <= 0 || newbuffer)
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
	    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);
    }
2401

Chris Allegretta's avatar
Chris Allegretta committed
2402
    wattroff(topwin, A_REVERSE);
2403

2404
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2405
    reset_cursor();
2406
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2407
2408
}

2409
2410
2411
2412
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
/* 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;
2439
2440
2441
2442
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	int old_whitespace = ISSET(WHITESPACE_DISPLAY);
	UNSET(WHITESPACE_DISPLAY);
#endif
2443
2444
2445
2446
	bar = charalloc(COLS - 3);
	vsnprintf(bar, COLS - 3, msg, ap);
	va_end(ap);
	foo = display_string(bar, 0, COLS - 4);
2447
2448
2449
2450
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	if (old_whitespace)
	    SET(WHITESPACE_DISPLAY);
#endif
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
	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);
2471
    statusblank = 26;
2472
2473
}

2474
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
2475
{
2476
    size_t i, colwidth, slen;
2477

Chris Allegretta's avatar
Chris Allegretta committed
2478
2479
2480
    if (ISSET(NO_HELP))
	return;

2481
2482
    if (s == main_list) {
	slen = MAIN_VISIBLE;
2483
	assert(slen <= length_of_list(s));
2484
    } else {
2485
2486
	slen = length_of_list(s);

2487
	/* Don't show any more shortcuts than the main list does. */
2488
2489
2490
2491
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

2492
2493
2494
    /* 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
2495

2496
    blank_bottombars();
2497

2498
    for (i = 0; i < slen; i++, s = s->next) {
2499
	const char *keystr;
2500

2501
	/* Yucky sentinel values we can't handle a better way. */
2502
#ifndef NANO_SMALL
2503
	if (s->ctrlval == NANO_HISTORY_KEY)
2504
2505
	    keystr = _("Up");
	else {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
2506
#endif
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
	    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));
2519

2520
2521
2522
2523
2524
2525
	    keystr = foo;
#ifndef NANO_SMALL
	}
#endif

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

2529
2530
2531
    wnoutrefresh(bottomwin);
    reset_cursor();
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2532
2533
}

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

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

2554
2555
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
2556
{
2557
2558
    int check_line = 0;
    const filestruct *filetmp;
2559

2560
2561
2562
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
2563
}
2564
#endif
2565

2566
2567
2568
2569
2570
/* 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
2571
{
2572
2573
2574
2575
    assert(COLS > 0);
    if (column == 0 || column < COLS - 1)
	return 0;
    else if (COLS > 9)
2576
	return column - 7 - (column - 7) % (COLS - 8);
2577
2578
2579
2580
2581
    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
2582
2583
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2584
/* Resets current_y, based on the position of current, and puts the
2585
 * cursor in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2586
2587
void reset_cursor(void)
{
2588
2589
2590
2591
    /* 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
2592
	return;
2593
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2594

2595
2596
2597
2598
2599
    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
2600
}
Chris Allegretta's avatar
Chris Allegretta committed
2601

2602
2603
2604
2605
/* 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
2606
2607
 * characters.  start is the column number of the first character of
 * this page.  That is, the first character of converted corresponds to
2608
 * character number actual_x(fileptr->data, start) of the line. */
2609
2610
void edit_add(const filestruct *fileptr, const char *converted, int
	yval, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
2611
{
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
#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. */
2622
2623
#endif

2624
2625
2626
    assert(fileptr != NULL && converted != NULL);
    assert(strlen(converted) <= COLS);

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

Chris Allegretta's avatar
Chris Allegretta committed
2631
#ifdef ENABLE_COLOR
2632
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
2633
2634
2635
2636
2637
2638
	const colortype *tmpcolor = colorstrings;

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
2639
		/* Number of chars to paint on this line.  There are COLS
2640
		 * characters on a whole line. */
2641
2642
	    regmatch_t startmatch;	/* match position for start_regexp */
	    regmatch_t endmatch;	/* match position for end_regexp */
2643
2644
2645
2646

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2647
2648
	    /* Two notes about regexec().  Return value 0 means there is
	     * a match.  Also, rm_eo is the first non-matching character
2649
2650
2651
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
2652
	    if (tmpcolor->end == NULL) {
2653
2654
2655
		size_t k = 0;

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

			assert(0 <= x_start && 0 < paintlen &&
				x_start + paintlen <= COLS);
			mvwaddnstr(edit, yval, x_start,
2688
2689
				converted + x_start, paintlen);
		    }
2690
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
2691
		}
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
	    } 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;
2705
		    /* the first line before fileptr matching start */
2706
2707
2708
2709
2710
		regoff_t start_col;
		    /* where it starts in that line */
		const filestruct *end_line;

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

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

		/* No end found, or it is too early. */
2758
2759
		if (end_line == NULL ||
			(end_line == fileptr && endmatch.rm_eo <= startpos))
2760
2761
2762
		    goto step_two;

		/* Now paint the start of fileptr. */
2763
2764
		paintlen = end_line != fileptr ? COLS :
			strnlenpt(fileptr->data, endmatch.rm_eo) - start;
2765
2766
2767
2768
		if (paintlen > COLS)
		    paintlen = COLS;

		assert(0 < paintlen && paintlen <= COLS);
2769
		mvwaddnstr(edit, yval, 0, converted, paintlen);
2770
2771
2772
2773
2774

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

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

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

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

2838
  skip_step_two:
2839
2840
2841
2842
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
2843
#endif				/* ENABLE_COLOR */
2844

2845
2846
2847
2848
2849
2850
2851
2852
#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. */

2853
2854
2855
2856
2857
2858
	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;
2859
2860
2861
	int x_start;
	    /* Starting column for mvwaddnstr.  Zero-based. */
	int paintlen;
2862
	    /* Number of chars to paint on this line.  There are COLS
2863
2864
	     * characters on a whole line. */

2865
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2866
2867
2868
2869
2870

	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
2871

2872
	/* The selected bit of fileptr is on this page. */
2873
2874
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
2875
2876
2877
2878

	    /* 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;
2879
2880

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

	    /* 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;
	    }
2899
2900
2901

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

2902
	    wattron(edit, A_REVERSE);
2903
	    mvwaddnstr(edit, yval, x_start, converted + x_start, paintlen);
2904
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
2905
	}
2906
    }
2907
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2908
2909
}

2910
/* Just update one line in the edit buffer.  Basically a wrapper for
2911
2912
 * edit_add().
 *
2913
2914
2915
 * 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. */
2916
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
2917
{
2918
2919
2920
2921
2922
2923
    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
2924

2925
    assert(fileptr != NULL);
2926

2927
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
2928

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

2932
2933
    if (line < 0 || line >= editwinrows)
	return;
2934

2935
2936
2937
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

2938
2939
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
2940
    index = (fileptr == current) ? strnlenpt(fileptr->data, index) : 0;
2941
    page_start = get_page_start(index);
2942

2943
2944
2945
    /* 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
2946

Chris Allegretta's avatar
Chris Allegretta committed
2947
    /* Now, paint the line */
2948
    edit_add(fileptr, converted, line, page_start);
2949
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
2950

2951
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
2952
	mvwaddch(edit, line, 0, '$');
2953
    if (strlenpt(fileptr->data) > page_start + COLS)
2954
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
2955
2956
}

2957
2958
/* 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
2959
 * placewewant are on different pages. */
2960
int need_horizontal_update(size_t old_pww)
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
{
    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
2971
 * are on different pages. */
2972
int need_vertical_update(size_t old_pww)
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
{
    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
2988
 * on the page or whether we'll stay there. */
2989
2990
2991
2992
2993
2994
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
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
3046
 * updated. */
3047
void edit_redraw(const filestruct *old_current, size_t old_pww)
3048
{
3049
3050
    int do_refresh = need_vertical_update(0) ||
	need_vertical_update(old_pww);
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
    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;
    }

3063
3064
3065
    /* 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. */
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
    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
3083
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
3084
3085
void edit_refresh(void)
{
3086
3087
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
3088
3089
3090
3091
3092
3093
3094
3095
	/* 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);
3096
3097
    else {
	int nlines = 0;
3098
	const filestruct *foo = edittop;
Chris Allegretta's avatar
Chris Allegretta committed
3099

3100
#ifdef DEBUG
3101
	fprintf(stderr, "edit_refresh(): edittop->lineno = %d\n", edittop->lineno);
3102
#endif
3103

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

3120
3121
/* 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. */
3122
void edit_update(topmidnone location)
Chris Allegretta's avatar
Chris Allegretta committed
3123
{
3124
3125
    filestruct *foo = current;

Chris Allegretta's avatar
Chris Allegretta committed
3126
    if (location != TOP) {
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
	/* 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
3146

3147
3148
	for (; goal > 0 && foo->prev != NULL; goal--)
	    foo = foo->prev;
Chris Allegretta's avatar
Chris Allegretta committed
3149
    }
3150

3151
    edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
3152
3153
3154
    edit_refresh();
}

3155
3156
3157
/* 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). */
3158
int do_yesno(bool all, const char *msg)
Chris Allegretta's avatar
Chris Allegretta committed
3159
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3160
    int ok = -2, width = 16;
3161
3162
    const char *yesstr;		/* String of yes characters accepted. */
    const char *nostr;		/* Same for no. */
Chris Allegretta's avatar
Chris Allegretta committed
3163
    const char *allstr;		/* And all, surprise! */
Chris Allegretta's avatar
Chris Allegretta committed
3164

3165
3166
3167
3168
3169
3170
    /* 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". */
3171
3172
3173
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
3174
3175

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

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3178
3179
3180
	if (COLS < 32)
	    width = COLS / 2;

3181
	/* Write the bottom of the screen. */
3182
	blank_bottombars();
3183

3184
	sprintf(shortstr, " %c", yesstr[0]);
3185
	wmove(bottomwin, 1, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3186
	onekey(shortstr, _("Yes"), width);
3187
3188

	if (all) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3189
	    wmove(bottomwin, 1, width);
3190
	    shortstr[1] = allstr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3191
	    onekey(shortstr, _("All"), width);
3192
3193
	}

3194
	wmove(bottomwin, 2, 0);
3195
	shortstr[1] = nostr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3196
	onekey(shortstr, _("No"), width);
3197

3198
	wmove(bottomwin, 2, 16);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3199
	onekey("^C", _("Cancel"), width);
Chris Allegretta's avatar
Chris Allegretta committed
3200
    }
3201

Chris Allegretta's avatar
Chris Allegretta committed
3202
    wattron(bottomwin, A_REVERSE);
3203
3204

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

Chris Allegretta's avatar
Chris Allegretta committed
3207
    wattroff(bottomwin, A_REVERSE);
3208

Chris Allegretta's avatar
Chris Allegretta committed
3209
3210
    wrefresh(bottomwin);

3211
    do {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3212
	int kbinput;
3213
	bool meta_key, func_key;
3214
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3215
	int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
3216
#endif
3217

3218
	kbinput = get_kbinput(edit, &meta_key, &func_key);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3219
3220

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

	    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
3246
	}
3247
#endif
3248
	/* Look for the kbinput in the yes, no and (optionally) all
3249
	 * strings. */
3250
3251
3252
3253
3254
3255
3256
3257
3258
	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
3259
3260
}

3261
void total_refresh(void)
3262
3263
{
    clearok(topwin, TRUE);
3264
    clearok(edit, TRUE);
3265
3266
    clearok(bottomwin, TRUE);
    wnoutrefresh(topwin);
3267
    wnoutrefresh(edit);
3268
3269
3270
    wnoutrefresh(bottomwin);
    doupdate();
    clearok(topwin, FALSE);
3271
    clearok(edit, FALSE);
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
    clearok(bottomwin, FALSE);
    edit_refresh();
    titlebar(NULL);
}

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

3282
3283
3284
/* 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.
3285
 *
3286
3287
3288
 * 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. */
3289
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3290
{
3291
    const filestruct *fileptr;
3292
3293
    size_t i = 0;
    static size_t old_i = 0;
3294
    static long old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
3295

3296
    assert(current != NULL && fileage != NULL && totlines != 0);
3297
3298
3299
3300

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

3301
3302
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
3303
	i += strlen(fileptr->data) + 1;
3304
    }
3305
    i += current_x;
3306

3307
3308
3309
3310
    /* Check whether totsize is correct.  Else there is a bug
     * somewhere. */
    assert(current != filebot || i == totsize);

3311
3312
3313
3314
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
3315
	return;
3316
    }
Chris Allegretta's avatar
Chris Allegretta committed
3317

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3318
    /* If constant is FALSE, display the position on the statusbar
3319
3320
     * unconditionally; otherwise, only display the position when the
     * character values have changed. */
3321
    if (!constant || old_i != i || old_totsize != totsize) {
3322
3323
	size_t xpt = xplustabs() + 1;
	size_t cur_len = strlenpt(current->data) + 1;
3324
3325
3326
3327
3328
3329
3330
	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,
3331
		    (unsigned long)xpt, (unsigned long)cur_len, colpct,
3332
		    (unsigned long)i, totsize, bytepct);
3333
	UNSET(DISABLE_CURPOS);
3334
3335
3336
3337
    }

    old_i = i;
    old_totsize = totsize;
Chris Allegretta's avatar
Chris Allegretta committed
3338
3339
}

3340
void do_cursorpos_void(void)
3341
{
3342
    do_cursorpos(FALSE);
3343
3344
}

3345
#ifndef DISABLE_HELP
3346
/* Calculate the next line of help_text, starting at ptr. */
3347
int help_line_len(const char *ptr)
3348
{
3349
    int j = 0;
3350
3351
3352
3353
3354
3355
3356
3357
3358

    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++;
3359
	/* Don't print half a word if we've run out of space. */
3360
3361
3362
3363
	while (*ptr != ' ' && j > 0) {
	    ptr--;
	    j--;
	}
3364
	/* Word longer than COLS - 5 chars just gets broken. */
3365
3366
3367
3368
3369
3370
3371
	if (j == 0)
	    j = COLS - 5;
    }
    assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
    return j;
}

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

3384
    bool old_no_help = ISSET(NO_HELP);
3385
3386
3387
#ifndef DISABLE_MOUSE
    const shortcut *oldshortcut = currshortcut;
	/* We will set currshortcut to allow clicking on the help
3388
	 * screen's shortcut list. */
3389
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3390

3391
    curs_set(0);
Chris Allegretta's avatar
Chris Allegretta committed
3392
    blank_edit();
3393
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
3394
3395
    blank_statusbar();

3396
    /* Set help_text as the string to display. */
3397
    help_init();
3398
    assert(help_text != NULL);
3399

3400
#ifndef DISABLE_MOUSE
3401
    /* Set currshortcut to allow clicking on the help screen's shortcut
3402
     * list, AFTER help_init(). */
3403
    currshortcut = help_list;
3404
#endif
3405

Chris Allegretta's avatar
Chris Allegretta committed
3406
    if (ISSET(NO_HELP)) {
3407
3408
	/* Make sure that the help screen's shortcut list will actually
	 * be displayed. */
Chris Allegretta's avatar
Chris Allegretta committed
3409
	UNSET(NO_HELP);
3410
	window_init();
3411
3412
    }
    bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
3413
3414

    do {
3415
3416
3417
	int i;
	int old_line = line;
	    /* We redisplay the help only if it moved. */
3418
	const char *ptr = help_text;
3419

Chris Allegretta's avatar
Chris Allegretta committed
3420
	switch (kbinput) {
3421
#ifndef DISABLE_MOUSE
3422
	    case KEY_MOUSE:
3423
3424
3425
3426
		{
		    int mouse_x, mouse_y;
		    get_mouseinput(&mouse_x, &mouse_y, TRUE);
		}
3427
		break;
3428
#endif
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
	    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
3450
3451
	}

3452
3453
3454
3455
3456
3457
3458
	if (line == old_line && kbinput != ERR)
	    goto skip_redisplay;

	blank_edit();

	assert(COLS > 5);

3459
3460
	/* Calculate where in the text we should be, based on the
	 * page. */
3461
	for (i = 0; i < line; i++) {
3462
	    ptr += help_line_len(ptr);
3463
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
3464
3465
3466
		ptr++;
	}

3467
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
3468
	    int j = help_line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
3469
3470

	    mvwaddnstr(edit, i, 0, ptr, j);
3471
3472
3473
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
3474
	}
3475
	no_more = (*ptr == '\0');
3476

3477
  skip_redisplay:
3478
	kbinput = get_kbinput(edit, &meta_key, &func_key);
3479
3480
    } while (kbinput != NANO_CANCEL_KEY && kbinput != NANO_EXIT_KEY &&
	kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
3481

3482
#ifndef DISABLE_MOUSE
3483
    currshortcut = oldshortcut;
3484
#endif
3485

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3486
    if (old_no_help) {
3487
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
3488
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
3489
	SET(NO_HELP);
3490
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
3491
    } else
3492
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
3493

3494
    curs_set(1);
Chris Allegretta's avatar
Chris Allegretta committed
3495
    edit_refresh();
3496

3497
3498
3499
    /* 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. */
3500
3501
    free(help_text);
    help_text = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
3502
}
3503
#endif /* !DISABLE_HELP */
Chris Allegretta's avatar
Chris Allegretta committed
3504

3505
3506
/* 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
3507
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3508
{
3509
    size_t y = xplustabs();
3510
    size_t word_len = strlen(word);
Chris Allegretta's avatar
Chris Allegretta committed
3511

3512
3513
3514
    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
3515
3516

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

Chris Allegretta's avatar
Chris Allegretta committed
3518
3519
3520
    if (highlight_flag)
	wattron(edit, A_REVERSE);

3521
#ifdef HAVE_REGEX_H
3522
3523
3524
3525
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
	waddstr(edit, " ");
    else
3526
#endif
3527
	waddnstr(edit, word, y - 1);
3528
3529
3530
3531
3532

    if (word_len > y)
	waddch(edit, '$');
    else if (word_len == y)
	waddch(edit, word[word_len - 1]);
Chris Allegretta's avatar
Chris Allegretta committed
3533
3534
3535
3536
3537

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

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

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

3555
/* Dump the file structure to stderr in reverse. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3556
3557
void dump_buffer_reverse(void)
{
3558
3559
3560
    const filestruct *fileptr = filebot;

    while (fileptr != NULL) {
3561
	fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
3562
3563
3564
3565
3566
	fileptr = fileptr->prev;
    }
}
#endif /* DEBUG */

3567
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3568
#define CREDIT_LEN 53
3569
3570
#define XLCREDIT_LEN 8

3571
/* Easter egg: Display credits.  Assume nodelay(edit) is FALSE. */
3572
3573
void do_credits(void)
{
3574
3575
3576
3577
    int crpos = 0, xlpos = 0;
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
3578
3579
	VERSION,
	"",
3580
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
3581
3582
3583
3584
3585
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
3586
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3587
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
	"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",
	"",
3603
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
3604
3605
3606
3607
3608
3609
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
3610
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
3611
	"Linus Torvalds",
3612
	NULL,				/* "For ncurses:" */
3613
3614
3615
3616
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
3617
3618
3619
3620
3621
3622
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
3623
	"(c) 1999-2004 Chris Allegretta",
3624
3625
3626
3627
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3628
	"http://www.nano-editor.org/"
3629
3630
    };

3631
    const char *xlcredits[XLCREDIT_LEN] = {
3632
3633
3634
3635
3636
3637
3638
3639
	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!")
3640
    };
3641

3642
3643
    curs_set(0);
    nodelay(edit, TRUE);
3644
3645
    scrollok(edit, TRUE);
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
3646
    blank_edit();
3647
3648
3649
    blank_statusbar();
    blank_bottombars();
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
3650
    wrefresh(edit);
3651
3652
    wrefresh(bottomwin);

3653
3654
3655
3656
3657
3658
3659
3660
3661
    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);
3662
		what = _(xlcredits[xlpos]);
3663
		xlpos++;
Chris Allegretta's avatar
Chris Allegretta committed
3664
	    }
3665
3666
	    start_x = COLS / 2 - strlen(what) / 2 - 1;
	    mvwaddstr(edit, editwinrows - 1 - editwinrows % 2, start_x, what);
3667
	}
3668
3669
3670
3671
	napms(700);
	scroll(edit);
	wrefresh(edit);
	if (wgetch(edit) != ERR)
3672
	    break;
3673
3674
3675
	napms(700);
	scroll(edit);
	wrefresh(edit);
3676
3677
    }

3678
    scrollok(edit, FALSE);
3679
3680
3681
3682
    nodelay(edit, FALSE);
    curs_set(1);
    display_main_list();
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
3683
}
3684
#endif