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

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

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

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

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

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

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

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

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

139
    input = wgetch(win);
140

141
142
143
144
145
    /* If we get ERR when using blocking input, it means that the input
     * source that we were using is gone, so die gracefully. */
    if (input == ERR)
	handle_hupterm(0);

146
147
148
#ifndef NANO_SMALL
    allow_pending_sigwinch(FALSE);
#endif
149

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

157
158
159
160
    /* Read in the remaining characters using non-blocking input. */
    nodelay(win, TRUE);

    while (TRUE) {
161
#ifndef NANO_SMALL
162
	allow_pending_sigwinch(TRUE);
163
#endif
164

165
	input = wgetch(win);
166

167
	/* If there aren't any more characters, stop reading. */
168
	if (input == ERR)
169
170
171
172
	    break;

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

#ifndef NANO_SMALL
	allow_pending_sigwinch(FALSE);
#endif
183
184
185
186
    }

    /* Switch back to non-blocking input. */
    nodelay(win, FALSE);
187
188
189
190

#ifdef DEBUG
    fprintf(stderr, "get_buffer(): key_buffer_len = %lu\n", (unsigned long)key_buffer_len);
#endif
191
}
192

193
194
195
196
197
198
199
200
/* Return the length of the default keystroke buffer. */
size_t get_buffer_len(void)
{
    return key_buffer_len;
}

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

208
    /* If input is empty, get out. */
209
    if (input_len == 0)
210
211
212
213
214
	return;

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

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

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

    /* Copy input to the beginning of the default keystroke buffer. */
233
    memcpy(key_buffer, input, input_len * sizeof(int));
234
235
}

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

245
    unget_input(&kbinput, 1);
246
247

    if (meta_key) {
248
249
	kbinput = NANO_CONTROL_3;
	unget_input(&kbinput, 1);
250
251
252
253
254
255
256
257
    }
}

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

262
#ifndef NANO_SMALL
263
    allow_pending_sigwinch(TRUE);
264
265
266
    allow_pending_sigwinch(FALSE);
#endif

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    if (key_buffer_len == 0) {
	if (win != NULL)
	    get_buffer(win);

	if (key_buffer_len == 0)
	    return NULL;
    }

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

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

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

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

    return input;
306
307
}

308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/* Read in a single character.  If it's ignored, swallow it and go on.
 * Otherwise, try to translate it from ASCII, meta key sequences, escape
 * sequences, and/or extended keypad values.  Set meta_key to TRUE when
 * we get a meta key sequence, and set func_key to TRUE when we get an
 * extended keypad value.  Supported extended keypad values consist of
 * [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace,
 * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown),
 * the function keypad (F1-F16), and the numeric keypad with NumLock
 * off.  Assume nodelay(win) is FALSE. */
int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
{
    int kbinput;

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

    return kbinput;
}

/* Translate ASCII characters, extended keypad values, and escape
 * sequences into their corresponding key values.  Set meta_key to TRUE
 * when we get a meta key sequence, and set func_key to TRUE when we get
 * a function key.  Assume nodelay(win) is FALSE. */
int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key
337
#ifndef NANO_SMALL
338
	, bool reset
339
340
#endif
	)
341

342
{
343
    static int escapes = 0, byte_digits = 0;
344
    int *kbinput, retval = ERR;
345

346
#ifndef NANO_SMALL
347
348
    if (reset) {
	escapes = 0;
349
	byte_digits = 0;
350
	return ERR;
351
    }
352
#endif
353

354
355
    *meta_key = FALSE;
    *func_key = FALSE;
356

357
358
359
    /* Read in a character. */
    while ((kbinput = get_input(win, 1)) == NULL);

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
    switch (*kbinput) {
	case ERR:
	    break;
	case NANO_CONTROL_3:
	    /* Increment the escape counter. */
	    escapes++;
	    switch (escapes) {
		case 1:
		    /* One escape: wait for more input. */
		case 2:
		    /* Two escapes: wait for more input. */
		    break;
		default:
		    /* More than two escapes: reset the escape counter
		     * and wait for more input. */
		    escapes = 0;
	    }
	    break;
378
#if !defined(NANO_SMALL) && defined(KEY_RESIZE)
379
380
381
382
383
	/* Since we don't change the default SIGWINCH handler when
	 * NANO_SMALL is defined, KEY_RESIZE is never generated.  Also,
	 * Slang and SunOS 5.7-5.9 don't support KEY_RESIZE. */
	case KEY_RESIZE:
	    break;
384
385
#endif
#ifdef PDCURSES
386
387
388
389
390
391
392
	case KEY_SHIFT_L:
	case KEY_SHIFT_R:
	case KEY_CONTROL_L:
	case KEY_CONTROL_R:
	case KEY_ALT_L:
	case KEY_ALT_R:
	    break;
393
#endif
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
	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;
414
#ifdef KEY_HOME
415
416
417
418
			/* HP-UX 10 and 11 don't support KEY_HOME. */
			case KEY_HOME:
			    retval = NANO_HOME_KEY;
			    break;
419
#endif
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
			case KEY_BACKSPACE:
			    retval = NANO_BACKSPACE_KEY;
			    break;
			case KEY_DC:
			    retval = ISSET(REBIND_DELETE) ?
				NANO_BACKSPACE_KEY : NANO_DELETE_KEY;
			    break;
			case KEY_IC:
			    retval = NANO_INSERTFILE_KEY;
			    break;
			case KEY_NPAGE:
			    retval = NANO_NEXTPAGE_KEY;
			    break;
			case KEY_PPAGE:
			    retval = NANO_PREVPAGE_KEY;
			    break;
			case KEY_ENTER:
			    retval = NANO_ENTER_KEY;
			    break;
			case KEY_A1:	/* Home (7) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_HOME_KEY;
			    break;
			case KEY_A3:	/* PageUp (9) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_PREVPAGE_KEY;
			    break;
			case KEY_B2:	/* Center (5) on numeric keypad
					 * with NumLock off. */
			    break;
			case KEY_C1:	/* End (1) on numeric keypad
					 * with NumLock off. */
			    retval = NANO_END_KEY;
			    break;
			case KEY_C3:	/* PageDown (4) on numeric
					 * keypad with NumLock off. */
			    retval = NANO_NEXTPAGE_KEY;
			    break;
458
#ifdef KEY_BEG
459
460
461
462
			/* Slang doesn't support KEY_BEG. */
			case KEY_BEG:	/* Center (5) on numeric keypad
					 * with NumLock off. */
			    break;
463
#endif
464
#ifdef KEY_END
465
466
467
468
			/* HP-UX 10 and 11 don't support KEY_END. */
			case KEY_END:
			    retval = NANO_END_KEY;
			    break;
469
470
#endif
#ifdef KEY_SUSPEND
471
472
473
474
			/* Slang doesn't support KEY_SUSPEND. */
			case KEY_SUSPEND:
			    retval = NANO_SUSPEND_KEY;
			    break;
475
476
#endif
#ifdef KEY_SLEFT
477
478
479
480
			/* Slang doesn't support KEY_SLEFT. */
			case KEY_SLEFT:
			    retval = NANO_BACK_KEY;
			    break;
481
482
#endif
#ifdef KEY_SRIGHT
483
484
485
486
			/* Slang doesn't support KEY_SRIGHT. */
			case KEY_SRIGHT:
			    retval = NANO_FORWARD_KEY;
			    break;
487
#endif
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
			default:
			    retval = *kbinput;
			    break;
		    }
		    break;
		case 1:
		    /* One escape followed by a non-escape: escape
		     * sequence mode.  Reset the escape counter.  If
		     * there aren't any other keys waiting, we have a
		     * meta key sequence, so set meta_key to TRUE and
		     * save the lowercase version of the non-escape
		     * character as the result.  If there are other keys
		     * waiting, we have a true escape sequence, so
		     * interpret it. */
		    escapes = 0;
		    if (get_buffer_len() == 0) {
			*meta_key = TRUE;
			retval = tolower(*kbinput);
		    } else {
			int *seq;
			size_t seq_len;
			bool ignore_seq;

			/* Put back the non-escape character, get the
			 * complete escape sequence, translate the
			 * sequence into its corresponding key value,
			 * and save that as the result. */
			unget_input(kbinput, 1);
			seq_len = get_buffer_len();
			seq = get_input(NULL, seq_len);
			retval = get_escape_seq_kbinput(seq, seq_len,
				&ignore_seq);

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

			free(seq);
		    }
		    break;
		case 2:
		    /* Two escapes followed by one or more decimal
		     * digits: byte sequence mode.  If the word
		     * sequence's range is limited to 2XX (the first
		     * digit is in the '0' to '2' range and it's the
		     * first digit, or it's in the '0' to '9' range and
		     * it's not the first digit), increment the byte
		     * sequence counter and interpret the digit.  If the
		     * byte sequence's range is not limited to 2XX, fall
		     * through. */
		    if (('0' <= *kbinput && *kbinput <= '6' &&
			byte_digits == 0) || ('0' <= *kbinput &&
			*kbinput <= '9' && byte_digits > 0)) {
			int byte;

			byte_digits++;
			byte = get_byte_kbinput(*kbinput
547
#ifndef NANO_SMALL
548
				, FALSE
549
#endif
550
551
				);

552
553
554
555
556
557
			if (byte != ERR) {
			    /* If we've read in a complete byte
			     * sequence, reset the byte sequence counter
			     * and the escape counter, and put back the
			     * corresponding byte value. */
			    byte_digits = 0;
558
			    escapes = 0;
559

560
561
			    /* Put back the byte value. */
			    unget_input(&byte, 1);
562
			}
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
		    } else {
			/* Reset the escape counter. */
			escapes = 0;
			if (byte_digits == 0)
			    /* Two escapes followed by a non-decimal
			     * digit or a decimal digit that would
			     * create a byte sequence greater than 2XX,
			     * and we're not in the middle of a byte
			     * sequence: control character sequence
			     * mode.  Interpret the control sequence and
			     * save the corresponding control character
			     * as the result. */
			    retval = get_control_kbinput(*kbinput);
			else {
			    /* If we're in the middle of a byte
			     * sequence, reset the byte sequence counter
			     * and save the character we got as the
			     * result. */
			    byte_digits = 0;
			    retval = *kbinput;
			}
		    }
		    break;
	    }
    }
588

589
590
591
592
    /* If we have a result and it's an extended keypad value (i.e, a
     * value outside of byte range), set func_key to TRUE. */
    if (retval != ERR)
	*func_key = !is_byte(retval);
593
594

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

598
    /* Return the result. */
599
600
601
    return retval;
}

602
/* Translate escape sequences, most of which correspond to extended
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
603
 * keypad values, into their corresponding key values.  These sequences
604
605
606
607
608
 * 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. */
609
int get_escape_seq_kbinput(const int *seq, size_t seq_len, bool
610
	*ignore_seq)
611
{
612
    int retval = ERR;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
613

614
615
    *ignore_seq = FALSE;

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

1104
#ifdef DEBUG
1105
    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d, ignore_seq = %d\n", retval, (int)*ignore_seq);
1106
#endif
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1107

1108
    return retval;
1109
1110
}

1111
/* Return the equivalent arrow key value for the case-insensitive
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1112
 * letters A (up), B (down), C (right), and D (left).  These are common
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
 * 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;
    }
}

1130
1131
1132
/* Translate a byte sequence: turn a three-digit decimal number from
 * 000 to 255 into its corresponding byte value. */
int get_byte_kbinput(int kbinput
1133
1134
1135
1136
#ifndef NANO_SMALL
	, bool reset
#endif
	)
1137
{
1138
    static int byte_digits = 0, byte = 0;
1139
    int retval = ERR;
1140
1141

#ifndef NANO_SMALL
1142
    if (reset) {
1143
	byte_digits = 0;
1144
	byte = 0;
1145
1146
	return ERR;
    }
1147
1148
#endif

1149
1150
    /* Increment the byte digit counter. */
    byte_digits++;
1151

1152
    switch (byte_digits) {
1153
	case 1:
1154
1155
	    /* One digit: reset the byte sequence holder and add the
	     * digit we got to the 100's position of the byte sequence
1156
	     * holder. */
1157
	    byte = 0;
1158
	    if ('0' <= kbinput && kbinput <= '2')
1159
		byte += (kbinput - '0') * 100;
1160
1161
	    else
		/* If the character we got isn't a decimal digit, or if
1162
		 * it is and it would put the byte sequence out of byte
1163
1164
1165
1166
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 2:
1167
1168
	    /* Two digits: add the digit we got to the 10's position of
	     * the byte sequence holder. */
1169
1170
1171
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
		'6' <= kbinput && kbinput <= '9'))
		byte += (kbinput - '0') * 10;
1172
1173
	    else
		/* If the character we got isn't a decimal digit, or if
1174
		 * it is and it would put the byte sequence out of byte
1175
1176
1177
1178
		 * range, save it as the result. */
		retval = kbinput;
	    break;
	case 3:
1179
1180
1181
	    /* Three digits: add the digit we got to the 1's position of
	     * the byte sequence holder, and save the corresponding word
	     * value as the result. */
1182
1183
1184
1185
	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
		'6' <= kbinput && kbinput <= '9')) {
		byte += (kbinput - '0');
		retval = byte;
1186
	    } else
1187
1188
1189
1190
1191
		/* If the character we got isn't a decimal digit, or if
		 * it is and it would put the word sequence out of word
		 * range, save it as the result. */
		retval = kbinput;
	    break;
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
	default:
	    /* More than three digits: save the character we got as the
	     * result. */
	    retval = kbinput;
	    break;
    }

    /* If we have a result, reset the byte digit counter and the byte
     * sequence holder. */
    if (retval != ERR) {
	byte_digits = 0;
1203
	byte = 0;
1204
1205
1206
    }

#ifdef DEBUG
1207
    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
#endif

    return retval;
}

/* Translate a word sequence: turn a four-digit hexadecimal number from
 * 0000 to ffff (case-insensitive) into its corresponding word value. */
int get_word_kbinput(int kbinput
#ifndef NANO_SMALL
	, bool reset
#endif
	)
{
1221
    static int word_digits = 0, word = 0;
1222
1223
1224
1225
1226
    int retval = ERR;

#ifndef NANO_SMALL
    if (reset) {
	word_digits = 0;
1227
	word = 0;
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
	return ERR;
    }
#endif

    /* Increment the word digit counter. */
    word_digits++;

    switch (word_digits) {
	case 1:
	    /* One digit: reset the word sequence holder and add the
	     * digit we got to the 4096's position of the word sequence
	     * holder. */
1240
	    word = 0;
1241
	    if ('0' <= kbinput && kbinput <= '9')
1242
		word += (kbinput - '0') * 4096;
1243
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1244
		word += (tolower(kbinput) + 10 - 'a') * 4096;
1245
1246
1247
1248
1249
1250
1251
1252
	    else
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the word sequence out of
		 * word range, save it as the result. */
		retval = kbinput;
	    break;
	case 2:
	    /* Two digits: add the digit we got to the 256's position of
1253
	     * the word sequence holder. */
1254
	    if ('0' <= kbinput && kbinput <= '9')
1255
		word += (kbinput - '0') * 256;
1256
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1257
		word += (tolower(kbinput) + 10 - 'a') * 256;
1258
	    else
1259
1260
1261
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the word sequence out of
		 * word range, save it as the result. */
1262
1263
		retval = kbinput;
	    break;
1264
1265
1266
1267
	case 3:
	    /* Three digits: add the digit we got to the 16's position
	     * of the word sequence holder. */
	    if ('0' <= kbinput && kbinput <= '9')
1268
		word += (kbinput - '0') * 16;
1269
	    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1270
		word += (tolower(kbinput) + 10 - 'a') * 16;
1271
1272
1273
1274
1275
1276
1277
1278
	    else
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the word sequence out of
		 * word range, save it as the result. */
		retval = kbinput;
	    break;
	case 4:
	    /* Four digits: add the digit we got to the 1's position of
1279
1280
	     * the word sequence holder, and save the corresponding word
	     * value as the result. */
1281
	    if ('0' <= kbinput && kbinput <= '9') {
1282
1283
		word += (kbinput - '0');
		retval = word;
1284
1285
	    } else if ('a' <= tolower(kbinput) &&
		tolower(kbinput) <= 'f') {
1286
1287
		word += (tolower(kbinput) + 10 - 'a');
		retval = word;
1288
	    } else
1289
1290
1291
		/* If the character we got isn't a hexadecimal digit, or
		 * if it is and it would put the word sequence out of
		 * word range, save it as the result. */
1292
1293
1294
		retval = kbinput;
	    break;
	default:
1295
	    /* More than four digits: save the character we got as the
1296
1297
1298
1299
	     * result. */
	    retval = kbinput;
	    break;
    }
1300

1301
1302
1303
1304
    /* If we have a result, reset the word digit counter and the word
     * sequence holder. */
    if (retval != ERR) {
	word_digits = 0;
1305
	word = 0;
1306
    }
1307

1308
#ifdef DEBUG
1309
    fprintf(stderr, "get_word_kbinput(): kbinput = %d, word_digits = %d, word = %d, retval = %d\n", kbinput, word_digits, word, retval);
1310
1311
#endif

1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
    return retval;
}

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

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

1340
#ifdef DEBUG
1341
    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1342
1343
#endif

1344
1345
    return retval;
}
1346

1347
1348
1349
1350
/* Put the output-formatted characters in output back into the default
 * keystroke buffer, so that they can be parsed and displayed as output
 * again. */
void unparse_kbinput(char *output, size_t output_len)
1351
{
1352
1353
    int *input;
    size_t i;
1354

1355
1356
1357
1358
1359
1360
1361
1362
    if (output_len == 0)
	return;

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

1365
/* Read in a stream of characters verbatim, and return the length of the
1366
1367
1368
1369
 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
{
    int *retval;
1370

1371
1372
1373
1374
1375
1376
1377
1378
1379
    /* 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. */
    if (ISSET(PRESERVE))
	disable_flow_control();
    keypad(win, FALSE);

    /* Read in a stream of characters and interpret it if possible. */
    retval = parse_verbatim_kbinput(win, kbinput_len);
1380
1381
1382
1383
1384
1385
1386

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

1387
    return retval;
1388
1389
}

1390
1391
1392
1393
/* Read in a stream of all available characters, and return the length
 * of the string in kbinput_len.  Translate the first few characters of
 * the input into the corresponding word value if possible.  After that,
 * leave the input as-is. */ 
1394
int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1395
{
1396
    int *kbinput, word, *retval;
1397

1398
1399
1400
    /* Read in the first keystroke. */
    while ((kbinput = get_input(win, 1)) == NULL);

1401
    /* Check whether the first keystroke is a hexadecimal digit. */
1402
    word = get_word_kbinput(*kbinput
1403
#ifndef NANO_SMALL
1404
	, FALSE
1405
#endif
1406
1407
	);

1408
1409
    /* If the first keystroke isn't a hexadecimal digit, put back the
     * first keystroke. */
1410
1411
1412
1413
1414
    if (word != ERR)
	unget_input(kbinput, 1);
    /* Otherwise, read in keystrokes until we have a complete word
     * sequence, and put back the corresponding word value. */
    else {
1415
	char *word_mb;
1416
	int word_mb_len, *seq, i;
1417

1418
1419
	while (word == ERR) {
	    while ((kbinput = get_input(win, 1)) == NULL);
1420

1421
	    word = get_word_kbinput(*kbinput
1422
#ifndef NANO_SMALL
1423
		, FALSE
1424
#endif
1425
1426
		);
	}
1427

1428
	/* Put back the multibyte equivalent of the word value. */
1429
	word_mb = make_mbchar(word, &word_mb_len);
1430
1431
1432
1433
1434

	seq = (int *)nmalloc(word_mb_len * sizeof(int));

	for (i = 0; i < word_mb_len; i++)
	    seq[i] = (unsigned char)word_mb[i];
1435

1436
1437
1438
1439
	unget_input(seq, word_mb_len);

	free(seq);
	free(word_mb);
1440
1441
    }

1442
    /* Get the complete sequence, and save the characters in it as the
1443
1444
     * result. */
    *kbinput_len = get_buffer_len();
1445
    retval = get_input(NULL, *kbinput_len);
1446
1447
1448
1449

    return retval;
}

1450
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
1451
1452
/* 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,
1453
1454
 * assuming allow_shortcuts is FALSE, if the shortcut list on the
 * bottom two lines of the screen is visible and the mouse event took
1455
1456
1457
 * 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
1458
 * already been read in. */
1459
bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1460
1461
1462
1463
1464
1465
1466
1467
{
    MEVENT mevent;

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

    /* First, get the actual mouse event. */
    if (getmouse(&mevent) == ERR)
1468
	return FALSE;
1469
1470
1471
1472
1473

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

1474
1475
1476
    /* 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
1477
     * was clicked and put back the equivalent keystroke(s) for it. */
1478
1479
    if (allow_shortcuts && !ISSET(NO_HELP) && wenclose(bottomwin,
	*mouse_y, *mouse_x)) {
1480
	int i, j;
1481
	size_t currslen;
1482
1483
1484
1485
1486
1487
1488
1489
	    /* 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;
1490
	else {
1491
1492
	    currslen = length_of_list(currshortcut);

1493
1494
1495
1496
1497
1498
	    /* We don't show any more shortcuts than the main list
	     * does. */
	    if (currslen > MAIN_VISIBLE)
		currslen = MAIN_VISIBLE;
	}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1499
1500
	/* Calculate the width of each shortcut in the list.  It's the
	 * same for all of them. */
1501
1502
1503
1504
1505
	if (currslen < 2)
	    i = COLS / 6;
	else
	    i = COLS / ((currslen / 2) + (currslen % 2));

1506
1507
1508
	/* Calculate the y-coordinate relative to the beginning of
	 * bottomwin. */
	j = *mouse_y - ((2 - no_more_space()) + 1) - editwinrows;
1509
1510
1511
1512
1513

	/* 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)
1514
	    return FALSE;
1515
1516
	j = (*mouse_x / i) * 2 + j;
	if (j >= currslen)
1517
	    return FALSE;
1518
1519
1520
1521
1522
1523

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

1524
1525
1526
	/* 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. */
1527
	if (s->ctrlval != NANO_NO_KEY) {
1528
	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1529
1530
	    return TRUE;
	} else if (s->metaval != NANO_NO_KEY) {
1531
	    unget_kbinput(s->metaval, TRUE, FALSE);
1532
1533
	    return TRUE;
	}
1534
    }
1535
    return FALSE;
1536
}
1537
1538
#endif /* !DISABLE_MOUSE */

1539
const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool
1540
	*meta_key, bool *func_key)
1541
1542
1543
1544
{
    const shortcut *s = s_list;
    size_t slen = length_of_list(s_list);

1545
#ifdef DEBUG
1546
    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %d, func_key = %d\n", *kbinput, (int)*meta_key, (int)*func_key);
1547
1548
#endif

1549
1550
1551
1552
1553
1554
    /* 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.
1555
1556
1557
1558
1559
	 * 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. */

1560
1561
1562
1563
	if (*kbinput != NANO_NO_KEY && (*kbinput == s->ctrlval ||
		(*meta_key == TRUE && (*kbinput == s->metaval ||
		*kbinput == s->miscval)) || (*func_key == TRUE &&
		*kbinput == s->funcval))) {
1564
1565
1566
1567
1568
1569
1570
1571
	    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
1572
     * key, an equivalent primary meta key sequence, or both. */
1573
1574
1575
    if (slen > 0) {
	if (s->ctrlval != NANO_NO_KEY) {
	    *meta_key = FALSE;
1576
	    *func_key = FALSE;
1577
	    *kbinput = s->ctrlval;
1578
	    return s;
1579
1580
	} else if (s->metaval != NANO_NO_KEY) {
	    *meta_key = TRUE;
1581
	    *func_key = FALSE;
1582
	    *kbinput = s->metaval;
1583
	    return s;
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
	}
    }

    return NULL;
}

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

1595
1596
1597
1598
#ifdef DEBUG
    fprintf(stderr, "get_toggle(): kbinput = %d, meta_key = %d\n", kbinput, (int)meta_key);
#endif

1599
1600
1601
    /* Check for toggles. */
    for (; t != NULL; t = t->next) {
	/* We've found a toggle if meta_key is TRUE and the key is in
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1602
	 * the meta key toggle list. */
1603
1604
1605
1606
1607
1608
1609
1610
	if (meta_key && kbinput == t->val)
	    break;
    }

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

1611
int do_statusbar_input(bool *meta_key, bool *func_key, bool *s_or_t,
1612
	bool *ran_func, bool *finished, bool allow_funcs)
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
{
    int input;
	/* The character we read in. */
    static int *kbinput = NULL;
	/* The input buffer. */
    static size_t kbinput_len = 0;
	/* The length of the input buffer. */
    const shortcut *s;
    bool have_shortcut;

    *s_or_t = FALSE;
1624
    *ran_func = FALSE;
1625
1626
1627
1628
1629
1630
1631
1632
    *finished = FALSE;

    /* Read in a character. */
    input = get_kbinput(bottomwin, meta_key, func_key);

#ifndef DISABLE_MOUSE
    /* If we got a mouse click and it was on a shortcut, read in the
     * shortcut character. */
1633
    if (allow_funcs && *func_key == TRUE && input == KEY_MOUSE) {
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
	if (do_mouse())
	    input = get_kbinput(bottomwin, meta_key, func_key);
	else
	    input = ERR;
    }
#endif

    /* Check for a shortcut in the current list. */
    s = get_shortcut(currshortcut, &input, meta_key, func_key);

    /* If we got a shortcut from the current list, or a "universal"
     * statusbar prompt shortcut, set have_shortcut to TRUE. */
    have_shortcut = (s != NULL || input == NANO_REFRESH_KEY ||
	input == NANO_HOME_KEY || input == NANO_END_KEY ||
	input == NANO_FORWARD_KEY || input == NANO_BACK_KEY ||
	input == NANO_BACKSPACE_KEY || input == NANO_DELETE_KEY ||
1650
1651
1652
1653
1654
1655
1656
1657
1658
	input == NANO_CUT_KEY ||
#ifndef NANO_SMALL
		input == NANO_NEXTWORD_KEY ||
#endif
		(*meta_key == TRUE && (
#ifndef NANO_SMALL
		input == NANO_PREVWORD_KEY ||
#endif
		input == NANO_VERBATIM_KEY)));
1659
1660
1661
1662
1663

    /* Set s_or_t to TRUE if we got a shortcut. */
    *s_or_t = have_shortcut;

    if (allow_funcs) {
1664
1665
1666
1667
1668
	/* If we got a character, and it isn't a shortcut or toggle,
	 * it's a normal text character.  Display the warning if we're
	 * in view mode, or add the character to the input buffer if
	 * we're not. */
	if (input != ERR && *s_or_t == FALSE) {
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable text
	     * input. */
	    if (!ISSET(RESTRICTED) || filename[0] == '\0' ||
		currshortcut != writefile_list) {
		kbinput_len++;
		kbinput = (int *)nrealloc(kbinput, kbinput_len *
			sizeof(int));
		kbinput[kbinput_len - 1] = input;
	    }
	}

	/* If we got a shortcut, or if there aren't any other characters
	 * waiting after the one we read in, we need to display all the
	 * characters in the input buffer if it isn't empty. */
	 if (*s_or_t == TRUE || get_buffer_len() == 0) {
	    if (kbinput != NULL) {
1686

1687
		/* Display all the characters in the input buffer at
1688
		 * once, filtering out control characters. */
1689
1690
1691
1692
1693
1694
1695
1696
1697
		char *output = charalloc(kbinput_len + 1);
		size_t i;
		bool got_enter;
			/* Whether we got the Enter key. */

		for (i = 0; i < kbinput_len; i++)
		    output[i] = (char)kbinput[i];
		output[i] = '\0';

1698
1699
		do_statusbar_output(output, kbinput_len, &got_enter,
			FALSE);
1700
1701

		free(output);
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751

		/* Empty the input buffer. */
		kbinput_len = 0;
		free(kbinput);
		kbinput = NULL;
	    }
	}

	if (have_shortcut) {
	    switch (input) {
		/* Handle the "universal" statusbar prompt shortcuts. */
		case NANO_REFRESH_KEY:
		    total_refresh();
		    break;
		case NANO_HOME_KEY:
		    do_statusbar_home();
		    break;
		case NANO_END_KEY:
		    do_statusbar_end();
		    break;
		case NANO_FORWARD_KEY:
		    do_statusbar_right();
		    break;
		case NANO_BACK_KEY:
		    do_statusbar_left();
		    break;
		case NANO_BACKSPACE_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Backspace. */
		    if (!ISSET(RESTRICTED) || filename[0] == '\0' ||
			currshortcut != writefile_list)
			do_statusbar_backspace();
		    break;
		case NANO_DELETE_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Delete. */
		    if (!ISSET(RESTRICTED) || filename[0] == '\0' ||
			currshortcut != writefile_list)
			do_statusbar_delete();
		    break;
		case NANO_CUT_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Cut. */
		    if (!ISSET(RESTRICTED) || filename[0] == '\0' ||
			currshortcut != writefile_list)
			do_statusbar_cut_text();
		    break;
1752
1753
1754
1755
1756
1757
1758
1759
1760
#ifndef NANO_SMALL
		case NANO_NEXTWORD_KEY:
		    do_statusbar_next_word();
		    break;
		case NANO_PREVWORD_KEY:
		    if (*meta_key == TRUE)
			do_statusbar_prev_word();
		    break;
#endif
1761
1762
1763
1764
1765
1766
		case NANO_VERBATIM_KEY:
		    if (*meta_key == TRUE) {
			/* If we're using restricted mode, the filename
			 * isn't blank, and we're at the "Write File"
			 * prompt, disable verbatim input. */
			if (!ISSET(RESTRICTED) || filename[0] == '\0' ||
1767
1768
1769
1770
1771
1772
				currshortcut != writefile_list) {
			    bool got_enter;
				/* Whether we got the Enter key. */

			    do_statusbar_verbatim_input(&got_enter);

1773
1774
			    /* If we got the Enter key, set input to the
			     * key value for Enter, and set finished to
1775
			     * TRUE to indicate that we're done. */
1776
1777
			    if (got_enter) {
				input = NANO_ENTER_KEY;
1778
				*finished = TRUE;
1779
			    }
1780
			}
1781
1782
			break;
		    }
1783
		/* Handle the normal statusbar prompt shortcuts, setting
1784
1785
1786
		 * ran_func to TRUE if we try to run their associated
		 * functions and setting finished to TRUE to indicate
		 * that we're done after trying to run their associated
1787
1788
1789
		 * functions. */
		default:
		    if (s->func != NULL) {
1790
1791
			*ran_func = TRUE;
			if (!ISSET(VIEW_MODE) || s->viewok)
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
			    s->func();
		    }
		    *finished = TRUE;
	    }
	}
    }

    return input;
}

#ifndef DISABLE_MOUSE
bool do_statusbar_mouse(void)
{
    /* 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 this function. */
    int mouse_x, mouse_y;
    return get_mouseinput(&mouse_x, &mouse_y, TRUE);
}
#endif

void do_statusbar_home(void)
{
#ifndef NANO_SMALL
    if (ISSET(SMART_HOME)) {
	size_t statusbar_x_save = statusbar_x;
1818

1819
	statusbar_x = indent_length(answer);
1820

1821
	if (statusbar_x == statusbar_x_save ||
1822
		statusbar_x == strlen(answer))
1823
1824
1825
1826
1827
1828
1829
1830
	    statusbar_x = 0;
    } else
#endif
	statusbar_x = 0;
}

void do_statusbar_end(void)
{
1831
    statusbar_x = strlen(answer);
1832
1833
1834
1835
}

void do_statusbar_right(void)
{
1836
    if (statusbar_x < strlen(answer))
1837
	statusbar_x = move_mbright(answer, statusbar_x);
1838
1839
1840
1841
1842
}

void do_statusbar_left(void)
{
    if (statusbar_x > 0)
1843
	statusbar_x = move_mbleft(answer, statusbar_x);
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
}

void do_statusbar_backspace(void)
{
    if (statusbar_x > 0) {
	do_statusbar_left();
	do_statusbar_delete();
    }
}

void do_statusbar_delete(void)
{
1856
    if (answer[statusbar_x] != '\0') {
1857
1858
	int char_buf_len = parse_mbchar(answer + statusbar_x, NULL,
		NULL, NULL);
1859
1860
1861
	size_t line_len = strlen(answer + statusbar_x);

	assert(statusbar_x < strlen(answer));
1862

1863
	charmove(answer + statusbar_x, answer + statusbar_x +
1864
		char_buf_len, strlen(answer) - statusbar_x -
1865
		char_buf_len + 1);
1866
1867

	null_at(&answer, statusbar_x + line_len - char_buf_len);
1868
1869
1870
1871
1872
    }
}

void do_statusbar_cut_text(void)
{
1873
1874
    assert(answer != NULL);

1875
#ifndef NANO_SMALL
1876
    if (ISSET(CUT_TO_END))
1877
	null_at(&answer, statusbar_x);
1878
    else {
1879
#endif
1880
1881
	null_at(&answer, 0);
	statusbar_x = 0;
1882
#ifndef NANO_SMALL
1883
    }
1884
#endif
1885
1886
}

1887
1888
1889
#ifndef NANO_SMALL
void do_statusbar_next_word(void)
{
1890
    char *char_mb;
1891
1892
1893
1894
    int char_mb_len;

    assert(answer != NULL);

1895
1896
    char_mb = charalloc(mb_cur_max());

1897
1898
    /* Move forward until we find the character after the last letter of
     * the current word. */
1899
    while (answer[statusbar_x] != '\0') {
1900
1901
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL,
		NULL);
1902
1903
1904

	/* If we've found it, stop moving forward through the current
	 * line. */
1905
	if (!is_word_mbchar(char_mb))
1906
1907
1908
1909
1910
1911
	    break;

	statusbar_x += char_mb_len;
    }

    /* Move forward until we find the first letter of the next word. */
1912
1913
    if (answer[statusbar_x] != '\0')
	statusbar_x += char_mb_len;
1914

1915
    while (answer[statusbar_x] != '\0') {
1916
1917
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL,
		NULL);
1918
1919
1920

	/* If we've found it, stop moving forward through the current
	 * line. */
1921
	if (is_word_mbchar(char_mb))
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
	    break;

	statusbar_x += char_mb_len;
    }

    free(char_mb);
}

void do_statusbar_prev_word(void)
{
1932
    char *char_mb;
1933
1934
1935
1936
1937
    int char_mb_len;
    bool begin_line = FALSE;

    assert(answer != NULL);

1938
1939
    char_mb = charalloc(mb_cur_max());

1940
1941
1942
    /* Move backward until we find the character before the first letter
     * of the current word. */
    while (!begin_line) {
1943
1944
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL,
		NULL);
1945
1946
1947

	/* If we've found it, stop moving backward through the current
	 * line. */
1948
	if (!is_word_mbchar(char_mb))
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
	    break;

	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);
    }

    /* Move backward until we find the last letter of the previous
     * word. */
    if (statusbar_x == 0)
	begin_line = TRUE;
    else
	statusbar_x = move_mbleft(answer, statusbar_x);

    while (!begin_line) {
1965
1966
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL,
		NULL);
1967
1968
1969

	/* If we've found it, stop moving backward through the current
	 * line. */
1970
	if (is_word_mbchar(char_mb))
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
	    break;

	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);
    }

    /* If we've found it, move backward until we find the character
     * before the first letter of the previous word. */
    if (!begin_line) {
	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);

	while (!begin_line) {
1988
1989
	    char_mb_len = parse_mbchar(answer + statusbar_x, char_mb,
		NULL, NULL);
1990
1991
1992

	    /* If we've found it, stop moving backward through the
	     * current line. */
1993
	    if (!is_word_mbchar(char_mb))
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
		break;

	    if (statusbar_x == 0)
		begin_line = TRUE;
	    else
		statusbar_x = move_mbleft(answer, statusbar_x);
	}

	/* If we've found it, move forward to the first letter of the
	 * previous word. */
	if (!begin_line)
	    statusbar_x += char_mb_len;
    }

    free(char_mb);
}
#endif

2012
void do_statusbar_verbatim_input(bool *got_enter)
2013
{
2014
2015
2016
    int *kbinput;
    size_t kbinput_len, i;
    char *output;
2017

2018
2019
    *got_enter = FALSE;

2020
2021
2022
    /* Read in all the verbatim characters. */
    kbinput = get_verbatim_kbinput(bottomwin, &kbinput_len);

2023
2024
    /* Display all the verbatim characters at once, not filtering out
     * control characters. */
2025
2026
2027
2028
2029
    output = charalloc(kbinput_len + 1);

    for (i = 0; i < kbinput_len; i++)
	output[i] = (char)kbinput[i];
    output[i] = '\0';
2030

2031
    do_statusbar_output(output, kbinput_len, got_enter, TRUE);
2032
2033

    free(output);
2034
2035
}

2036
void do_statusbar_output(char *output, size_t output_len, bool
2037
	*got_enter, bool allow_cntrls)
2038
{
2039
    size_t answer_len, i = 0;
2040
2041
    char *char_buf = charalloc(mb_cur_max());
    int char_buf_len;
2042
2043
2044

    assert(answer != NULL);

2045
    answer_len = strlen(answer);
2046
2047
    *got_enter = FALSE;

2048
    while (i < output_len) {
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
	/* If allow_cntrls is FALSE, filter out nulls and newlines,
	 * since they're control characters. */
	if (allow_cntrls) {
	    /* Null to newline, if needed. */
	    if (output[i] == '\0')
		output[i] = '\n';
	    /* Newline to Enter, if needed. */
	    else if (output[i] == '\n') {
		/* Set got_enter to TRUE to indicate that we got the
		 * Enter key, put back the rest of the characters in
		 * output so that they can be parsed and output again,
		 * and get out. */
		*got_enter = TRUE;
		unparse_kbinput(output + i, output_len - i);
		return;
	    }
2065
	}
2066

2067
2068
2069
	/* Interpret the next multibyte character.  If it's an invalid
	 * multibyte character, interpret it as though it's a byte
	 * character. */
2070
	char_buf_len = parse_mbchar(output + i, char_buf, NULL, NULL);
2071
2072

	i += char_buf_len;
2073

2074
2075
2076
2077
	/* If allow_cntrls is FALSE, filter out a control character. */
	if (!allow_cntrls && is_cntrl_mbchar(output + i - char_buf_len))
	    continue;

2078
	/* More dangerousness fun =) */
2079
	answer = charealloc(answer, answer_len + (char_buf_len * 2));
2080

2081
	assert(statusbar_x <= answer_len);
2082

2083
	charmove(&answer[statusbar_x + char_buf_len],
2084
		&answer[statusbar_x], answer_len - statusbar_x +
2085
2086
		char_buf_len);
	charcpy(&answer[statusbar_x], char_buf, char_buf_len);
2087
	answer_len += char_buf_len;
2088
2089
2090
2091

	do_statusbar_right();
    }

2092
    free(char_buf);
2093
2094
}

2095
2096
2097
/* Return the placewewant associated with current_x, i.e, the zero-based
 * column position of the cursor.  The value will be no smaller than
 * current_x. */
Chris Allegretta's avatar
Chris Allegretta committed
2098
size_t xplustabs(void)
Chris Allegretta's avatar
Chris Allegretta committed
2099
{
Chris Allegretta's avatar
Chris Allegretta committed
2100
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
2101
2102
}

2103
2104
2105
2106
/* 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
2107
{
Chris Allegretta's avatar
Chris Allegretta committed
2108
    size_t i = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2109
	/* The position in str, returned. */
Chris Allegretta's avatar
Chris Allegretta committed
2110
    size_t length = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2111
	/* The screen display width to str[i]. */
Chris Allegretta's avatar
Chris Allegretta committed
2112

2113
    assert(str != NULL);
Chris Allegretta's avatar
Chris Allegretta committed
2114

2115
    while (*str != '\0') {
2116
	int str_len = parse_mbchar(str, NULL, NULL, &length);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2117

2118
2119
2120
2121
2122
2123
	if (length > xplus)
	    break;

	i += str_len;
	str += str_len;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2124

Chris Allegretta's avatar
Chris Allegretta committed
2125
    return i;
2126
2127
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2128
/* A strlen() with tabs factored in, similar to xplustabs().  How many
2129
2130
 * columns wide are the first size characters of str? */
size_t strnlenpt(const char *str, size_t size)
2131
{
Chris Allegretta's avatar
Chris Allegretta committed
2132
    size_t length = 0;
2133
	/* The screen display width to str[i]. */
Chris Allegretta's avatar
Chris Allegretta committed
2134

2135
2136
2137
2138
2139
2140
    if (size == 0)
	return 0;

    assert(str != NULL);

    while (*str != '\0') {
2141
	int str_len = parse_mbchar(str, NULL, NULL, &length);
2142
2143
2144
2145
2146
2147
2148

	str += str_len;

	if (size <= str_len)
	    break;

	size -= str_len;
2149
    }
2150

Chris Allegretta's avatar
Chris Allegretta committed
2151
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
2152
2153
}

2154
/* How many columns wide is buf? */
Chris Allegretta's avatar
Chris Allegretta committed
2155
size_t strlenpt(const char *buf)
2156
{
2157
    return strnlenpt(buf, (size_t)-1);
2158
2159
}

2160
void blank_titlebar(void)
Chris Allegretta's avatar
Chris Allegretta committed
2161
{
2162
    mvwaddstr(topwin, 0, 0, hblank);
2163
2164
}

2165
2166
2167
2168
2169
2170
void blank_topbar(void)
{
    if (!ISSET(MORE_SPACE))
	mvwaddstr(topwin, 1, 0, hblank);
}

Chris Allegretta's avatar
Chris Allegretta committed
2171
2172
void blank_edit(void)
{
2173
    int i;
2174
    for (i = 0; i < editwinrows; i++)
Chris Allegretta's avatar
Chris Allegretta committed
2175
2176
2177
2178
2179
2180
2181
2182
	mvwaddstr(edit, i, 0, hblank);
}

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

2183
2184
2185
2186
2187
2188
2189
2190
void blank_bottombars(void)
{
    if (!ISSET(NO_HELP)) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
}

2191
void check_statusblank(void)
Chris Allegretta's avatar
Chris Allegretta committed
2192
{
2193
2194
2195
2196
    if (statusblank > 1)
	statusblank--;
    else if (statusblank == 1 && !ISSET(CONSTUPDATE)) {
	statusblank = 0;
2197
2198
2199
2200
	blank_statusbar();
	wnoutrefresh(bottomwin);
	reset_cursor();
	wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2201
2202
2203
    }
}

2204
2205
2206
2207
/* 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
2208
2209
2210
2211
2212
 * string is dynamically allocated, and should be freed.  If dollars is
 * TRUE, the caller might put "$" at the beginning or end of the line if
 * it's too long. */
char *display_string(const char *buf, size_t start_col, size_t len, bool
	dollars)
2213
2214
{
    size_t start_index;
2215
	/* Index in buf of the first character shown. */
2216
    size_t column;
2217
	/* Screen column that start_index corresponds to. */
2218
2219
2220
2221
2222
2223
2224
    size_t alloc_len;
	/* The length of memory allocated for converted. */
    char *converted;
	/* The string we return. */
    size_t index;
	/* Current position in converted. */

2225
2226
2227
2228
    char *buf_mb = charalloc(mb_cur_max());
    int buf_mb_len;
    bool bad_char;

2229
2230
2231
2232
2233
    /* If dollars is TRUE, make room for the "$" at the end of the
     * line. */
    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
	len--;

2234
2235
2236
2237
2238
    if (len == 0)
	return mallocstrcpy(NULL, "");

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

2240
    assert(column <= start_col);
2241

2242
2243
2244
    /* Allocate enough space for the entire line, accounting for a
     * trailing multibyte character and/or tab. */
    alloc_len = (mb_cur_max() * (len + 1)) + tabsize;
2245

2246
2247
2248
    converted = charalloc(alloc_len + 1);
    index = 0;

2249
    if (column < start_col || (dollars && column > 0 &&
2250
	buf[start_index] != '\t')) {
2251
2252
	/* We don't display all of buf[start_index] since it starts to
	 * the left of the screen. */
2253
2254
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL,
		NULL);
2255

2256
	if (is_cntrl_mbchar(buf_mb)) {
2257
	    if (column < start_col) {
2258
2259
		char *ctrl_buf_mb = charalloc(mb_cur_max());
		int ctrl_buf_mb_len, i;
2260

2261
2262
		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
			&ctrl_buf_mb_len);
2263

2264
2265
		for (i = 0; i < ctrl_buf_mb_len; i++)
		    converted[index++] = ctrl_buf_mb[i];
2266

2267
		start_col += mbwidth(ctrl_buf_mb);
2268

2269
		free(ctrl_buf_mb);
2270

2271
		start_index += buf_mb_len;
2272
	    }
2273
2274
	}
#ifdef NANO_WIDE
2275
	else if (!ISSET(NO_UTF8) && mbwidth(buf_mb) > 1) {
2276
	    converted[index++] = ' ';
2277
	    start_col++;
2278
2279

	    start_index += buf_mb_len;
2280
	}
2281
#endif
2282
2283
    }

2284
    while (index < alloc_len - 1 && buf[start_index] != '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2285
2286
	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, &bad_char,
		NULL);
2287

2288
	if (*buf_mb == '\t') {
2289
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2290
2291
2292
2293
2294
2295
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = 0; i < whitespace_len[0]; i++)
		    converted[index++] = whitespace[i];
	    } else
2296
#endif
2297
		converted[index++] = ' '; 
2298
	    start_col++;
2299
	    while (start_col % tabsize != 0) {
2300
		converted[index++] = ' ';
2301
2302
		start_col++;
	    }
2303
2304
2305
	/* If buf contains a control character, interpret it.  If it
	 * contains an invalid multibyte control character, interpret
	 * that character as though it's a normal control character. */
2306
2307
2308
	} else if (is_cntrl_mbchar(buf_mb)) {
	    char *ctrl_buf_mb = charalloc(mb_cur_max());
	    int ctrl_buf_mb_len, i;
2309

2310
	    converted[index++] = '^';
2311
2312
	    start_col++;

2313
2314
	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
		&ctrl_buf_mb_len);
2315

2316
2317
	    for (i = 0; i < ctrl_buf_mb_len; i++)
		converted[index++] = ctrl_buf_mb[i];
2318

2319
	    start_col += mbwidth(ctrl_buf_mb);
2320

2321
2322
	    free(ctrl_buf_mb);
	} else if (*buf_mb == ' ') {
2323
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2324
2325
2326
2327
2328
2329
2330
	    if (ISSET(WHITESPACE_DISPLAY)) {
		int i;

		for (i = whitespace_len[0]; i < whitespace_len[0] +
			whitespace_len[1]; i++)
		    converted[index++] = whitespace[i];
	    } else
2331
#endif
2332
		converted[index++] = ' '; 
2333
2334
2335
2336
	    start_col++;
	} else {
	    int i;

2337
2338
2339
2340
2341
#ifdef NANO_WIDE
	    /* If buf contains an invalid multibyte non-control
	     * character, interpret that character as though it's a
	     * normal non-control character. */
	    if (!ISSET(NO_UTF8) && bad_char) {
2342
		char *bad_buf_mb;
2343
2344
		int bad_buf_mb_len;

2345
		bad_buf_mb = make_mbchar((unsigned char)*buf_mb,
2346
			&bad_buf_mb_len);
2347

2348
2349
		for (i = 0; i < bad_buf_mb_len; i++)
		    converted[index++] = bad_buf_mb[i];
2350

2351
		start_col += mbwidth(bad_buf_mb);
2352

2353
		free(bad_buf_mb);
2354
2355
	    } else {
#endif
2356
		for (i = 0; i < buf_mb_len; i++)
2357
		    converted[index++] = buf[start_index + i];
2358
2359

		start_col += mbwidth(buf_mb);
2360
#ifdef NANO_WIDE
2361
	    }
2362
2363
2364
#endif
	}

2365
	start_index += buf_mb_len;
2366
2367
    }

2368
2369
    free(buf_mb);

2370
    if (index < alloc_len - 1)
2371
2372
2373
2374
	converted[index] = '\0';

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

2377
    return converted;
2378
2379
}

Chris Allegretta's avatar
Chris Allegretta committed
2380
/* Repaint the statusbar when getting a character in nanogetstr().  buf
2381
 * should be no longer than max(0, COLS - 4).
Chris Allegretta's avatar
Chris Allegretta committed
2382
 *
Chris Allegretta's avatar
Chris Allegretta committed
2383
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
2384
 * off! */
2385
void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
2386
{
2387
    size_t x_real = strnlenpt(inputbuf, x);
2388
    int wid = COLS - strlenpt(buf) - 2;
2389

2390
    assert(x <= strlen(inputbuf));
Chris Allegretta's avatar
Chris Allegretta committed
2391

2392
    wattron(bottomwin, A_REVERSE);
2393
    blank_statusbar();
2394

Chris Allegretta's avatar
Chris Allegretta committed
2395
2396
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
2397
2398
2399
2400
2401

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

	assert(wid > 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2406
	assert(strlenpt(expanded) <= wid);
2407

2408
2409
2410
2411
2412
	waddstr(bottomwin, expanded);
	free(expanded);
	wmove(bottomwin, 0, COLS - wid + x_real - page_start);
    } else
	wmove(bottomwin, 0, COLS - 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2413

2414
    wattroff(bottomwin, A_REVERSE);
2415
2416
}

2417
/* Get the input from the keyboard; this should only be called from
Chris Allegretta's avatar
Chris Allegretta committed
2418
 * statusq(). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2419
int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
2420
#ifndef NANO_SMALL
2421
	filestruct **history_list,
2422
#endif
2423
	const shortcut *s
2424
#ifndef DISABLE_TABCOMP
2425
	, bool *list
2426
#endif
2427
	)
Chris Allegretta's avatar
Chris Allegretta committed
2428
2429
{
    int kbinput;
2430
    bool meta_key, func_key, s_or_t, ran_func, finished;
2431
    size_t curranswer_len;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2432
#ifndef DISABLE_TABCOMP
2433
    bool tabbed = FALSE;
2434
	/* Whether we've pressed Tab. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2435
#endif
2436
2437
2438
2439
2440
2441
#ifndef NANO_SMALL
    char *history = NULL;
	/* The current history string. */
    char *magichistory = NULL;
	/* The temporary string typed at the bottom of the history, if
	 * any. */
2442
#ifndef DISABLE_TABCOMP
2443
2444
    int last_kbinput = ERR;
	/* The key we pressed before the current key. */
2445
2446
2447
2448
    size_t complete_len = 0;
	/* The length of the original string that we're trying to
	 * tab complete, if any. */
#endif
2449
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
2450

2451
    answer = mallocstrcpy(answer, curranswer);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2452
    curranswer_len = strlen(answer);
2453

2454
    /* Only put statusbar_x at the end of the string if it's
2455
2456
2457
2458
2459
     * uninitialized, if it would be past the end of curranswer, 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. */
    if (statusbar_x == (size_t)-1 || statusbar_x > curranswer_len ||
2460
		resetstatuspos)
2461
	statusbar_x = curranswer_len;
Chris Allegretta's avatar
Chris Allegretta committed
2462

2463
    currshortcut = s;
2464

2465
    nanoget_repaint(buf, answer, statusbar_x);
Chris Allegretta's avatar
Chris Allegretta committed
2466

2467
2468
    /* Refresh the edit window and the statusbar before getting
     * input. */
2469
2470
    wnoutrefresh(edit);
    wrefresh(bottomwin);
2471

2472
2473
2474
2475
2476
    /* 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. */
2477
    while ((kbinput = do_statusbar_input(&meta_key, &func_key,
2478
	&s_or_t, &ran_func, &finished, TRUE)) != NANO_CANCEL_KEY &&
2479
	kbinput != NANO_ENTER_KEY) {
2480

2481
	assert(statusbar_x <= strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
2482

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2483
#ifndef DISABLE_TABCOMP
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2484
	if (kbinput != NANO_TAB_KEY)
2485
	    tabbed = FALSE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2486
#endif
Chris Allegretta's avatar
Chris Allegretta committed
2487

Chris Allegretta's avatar
Chris Allegretta committed
2488
	switch (kbinput) {
2489
	    case NANO_TAB_KEY:
2490
#ifndef DISABLE_TABCOMP
2491
#ifndef NANO_SMALL
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
		if (history_list != NULL) {
		    if (last_kbinput != NANO_TAB_KEY)
			complete_len = strlen(answer);

		    if (complete_len > 0) {
			answer = mallocstrcpy(answer,
				get_history_completion(history_list,
				answer, complete_len));
			statusbar_x = strlen(answer);
		    }
		} else
2503
#endif /* !NANO_SMALL */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2504
		if (allow_tabs)
2505
2506
		    answer = input_tab(answer, &statusbar_x, &tabbed,
			list);
2507
#endif /* !DISABLE_TABCOMP */
2508
2509
		break;
	    case NANO_PREVLINE_KEY:
2510
#ifndef NANO_SMALL
2511
		if (history_list != NULL) {
2512
2513
2514
2515
		    /* If we're scrolling up at the bottom of the
		     * history list, answer isn't blank, and
		     * magichistory isn't set, save answer in
		     * magichistory. */
2516
		    if ((*history_list)->next == NULL &&
2517
2518
2519
2520
2521
2522
2523
			answer[0] != '\0' && magichistory == NULL)
			magichistory = mallocstrcpy(NULL, answer);

		    /* Get the older search from the history list and
		     * save it in answer.  If there is no older search,
		     * don't do anything. */
		    if ((history =
2524
			get_history_older(history_list)) != NULL) {
2525
			answer = mallocstrcpy(answer, history);
2526
			statusbar_x = strlen(answer);
2527
		    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2528
2529
2530
2531
2532
2533

		    /* This key has a shortcut list entry when it's used
		     * to move to an older search, which means that
		     * finished has been set to TRUE.  Set it back to
		     * FALSE here, so that we aren't kicked out of the
		     * statusbar prompt. */
2534
		    finished = FALSE;
2535
		}
2536
#endif /* !NANO_SMALL */
2537
2538
		break;
	    case NANO_NEXTLINE_KEY:
2539
#ifndef NANO_SMALL
2540
2541
		if (history_list != NULL) {
		    /* Get the newer search from the history list and
2542
2543
		     * save it in answer.  If there is no newer search,
		     * don't do anything. */
2544
		    if ((history =
2545
			get_history_newer(history_list)) != NULL) {
2546
			answer = mallocstrcpy(answer, history);
2547
			statusbar_x = strlen(answer);
2548
2549
2550
2551
2552
2553
		    }

		    /* If, after scrolling down, we're at the bottom of
		     * the history list, answer is blank, and
		     * magichistory is set, save magichistory in
		     * answer. */
2554
		    if ((*history_list)->next == NULL &&
2555
2556
			answer[0] == '\0' && magichistory != NULL) {
			answer = mallocstrcpy(answer, magichistory);
2557
			statusbar_x = strlen(answer);
2558
		    }
2559
		}
2560
#endif /* !NANO_SMALL */
2561
		break;
2562
	}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2563

2564
2565
2566
2567
2568
2569
	/* If we have a shortcut with an associated function, break out
	 * if we're finished after running or trying to run the
	 * function. */
	if (finished)
	    break;

2570
#if !defined(NANO_SMALL) && !defined(DISABLE_TABCOMP)
2571
2572
2573
	last_kbinput = kbinput;
#endif

2574
	nanoget_repaint(buf, answer, statusbar_x);
Chris Allegretta's avatar
Chris Allegretta committed
2575
	wrefresh(bottomwin);
2576
    }
Chris Allegretta's avatar
Chris Allegretta committed
2577

2578
#ifndef NANO_SMALL
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2579
    /* Free magichistory if we need to. */
2580
2581
2582
2583
    if (magichistory != NULL)
	free(magichistory);
#endif

2584
    /* We finished putting in an answer or ran a normal shortcut's
2585
     * associated function, so reset statusbar_x. */
2586
    if (kbinput == NANO_CANCEL_KEY || kbinput == NANO_ENTER_KEY ||
2587
	ran_func)
2588
	statusbar_x = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
2589

2590
    return kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
2591
2592
}

2593
2594
/* 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
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2595
2596
 * otherwise, the valid shortcut key caught.  curranswer is any editable
 * text that we want to put up by default.
2597
 *
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2598
2599
 * The allow_tabs parameter indicates whether we should allow tabs to be
 * interpreted. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2600
int statusq(bool allow_tabs, const shortcut *s, const char *curranswer,
2601
#ifndef NANO_SMALL
2602
	filestruct **history_list,
2603
#endif
2604
	const char *msg, ...)
2605
2606
{
    va_list ap;
2607
    char *foo = charalloc(((COLS - 4) * mb_cur_max()) + 1);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2608
    int retval;
2609
2610
2611
2612
2613
2614
2615
#ifndef DISABLE_TABCOMP
    bool list = FALSE;
#endif

    bottombars(s);

    va_start(ap, msg);
2616
    vsnprintf(foo, (COLS - 4) * mb_cur_max(), msg, ap);
2617
    va_end(ap);
2618
    null_at(&foo, actual_x(foo, COLS - 4));
2619

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2620
    retval = nanogetstr(allow_tabs, foo, curranswer,
2621
#ifndef NANO_SMALL
2622
		history_list,
2623
2624
2625
2626
2627
2628
2629
2630
2631
#endif
		s
#ifndef DISABLE_TABCOMP
		, &list
#endif
		);
    free(foo);
    resetstatuspos = FALSE;

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2632
    switch (retval) {
2633
	case NANO_CANCEL_KEY:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2634
	    retval = -1;
2635
2636
2637
	    resetstatuspos = TRUE;
	    break;
	case NANO_ENTER_KEY:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2638
	    retval = (answer[0] == '\0') ? -2 : 0;
2639
2640
	    resetstatuspos = TRUE;
	    break;
2641
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2642

2643
2644
2645
    blank_statusbar();

#ifdef DEBUG
2646
    fprintf(stderr, "answer = \"%s\"\n", answer);
2647
2648
2649
#endif

#ifndef DISABLE_TABCOMP
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2650
2651
2652
	/* If we've done tab completion, there might be a list of
	 * filename matches on the edit window at this point.  Make sure
	 * that they're cleared off. */
2653
2654
2655
2656
	if (list)
	    edit_refresh();
#endif

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2657
    return retval;
2658
2659
2660
2661
2662
2663
2664
}

void statusq_abort(void)
{
    resetstatuspos = TRUE;
}

2665
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
2666
{
2667
    int space;
2668
	/* The space we have available for display. */
2669
2670
    size_t verlen = strlenpt(VERMSG) + 1;
	/* The length of the version message in columns. */
2671
2672
2673
    const char *prefix;
	/* "File:", "Dir:", or "New Buffer".  Goes before filename. */
    size_t prefixlen;
2674
	/* The length of the prefix in columns, plus one. */
2675
2676
2677
2678
    const char *state;
	/* "Modified", "View", or spaces the length of "Modified".
	 * Tells the state of this buffer. */
    size_t statelen = 0;
2679
	/* The length of the state in columns, plus one. */
2680
2681
    char *exppath = NULL;
	/* The file name, expanded for display. */
2682
    bool newfie = FALSE;
2683
	/* Do we say "New Buffer"? */
2684
    bool dots = FALSE;
2685
2686
2687
2688
	/* Do we put an ellipsis before the path? */

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

    wattron(topwin, A_REVERSE);
2691
    blank_titlebar();
Chris Allegretta's avatar
Chris Allegretta committed
2692

2693
2694
2695
2696
    if (COLS <= 5 || COLS - 5 < verlen)
	space = 0;
    else {
	space = COLS - 5 - verlen;
2697
2698
2699
2700
	/* 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;
2701
    }
Chris Allegretta's avatar
Chris Allegretta committed
2702

2703
    if (COLS > 4) {
2704
2705
2706
	/* The version message should only take up 1/3 of the screen
	 * minus one column. */
	mvwaddnstr(topwin, 0, 2, VERMSG, (COLS / 3) - 3);
2707
2708
	waddstr(topwin, "  ");
    }
Chris Allegretta's avatar
Chris Allegretta committed
2709
2710

    if (ISSET(MODIFIED))
2711
	state = _("Modified");
2712
    else if (ISSET(VIEW_MODE))
2713
2714
2715
	state = _("View");
    else {
	if (space > 0)
2716
	    statelen = strnlenpt(_("Modified"), space - 1) + 1;
2717
2718
	state = &hblank[COLS - statelen];
    }
2719
    statelen = strnlenpt(state, COLS);
2720

2721
2722
2723
2724
2725
    /* We need a space before state. */
    if ((ISSET(MODIFIED) || ISSET(VIEW_MODE)) && statelen < COLS)
	statelen++;

    assert(space >= 0);
2726

2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
    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");
2737
	newfie = TRUE;
2738
2739
    } else
	prefix = _("File:");
2740

2741
    assert(statelen < space);
2742

2743
    prefixlen = strnlenpt(prefix, space - statelen);
2744

2745
2746
    /* If newfie is FALSE, we need a space after prefix. */
    if (!newfie && prefixlen + statelen < space)
2747
2748
2749
2750
	prefixlen++;

    if (path == NULL)
	path = filename;
2751
2752
2753
2754
    if (space >= prefixlen + statelen)
	space -= prefixlen + statelen;
    else
	space = 0;
2755
	/* space is now the room we have for the file name. */
2756

2757
    if (!newfie) {
2758
2759
2760
	size_t lenpt = strlenpt(path), start_col;

	dots = (lenpt > space);
2761
2762
2763
2764
2765
2766
2767
2768

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

	exppath = display_string(path, start_col, space, FALSE);
2769
2770
2771
    }

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

2775
	/* There is room for the whole filename, so we center it. */
2776
	waddnstr(topwin, hblank, (space - exppathlen) / 3);
2777
	waddnstr(topwin, prefix, actual_x(prefix, prefixlen));
2778
	if (!newfie) {
2779
2780
	    assert(strlenpt(prefix) + 1 == prefixlen);

2781
2782
2783
2784
2785
	    waddch(topwin, ' ');
	    waddstr(topwin, exppath);
	}
    } else {
	/* We will say something like "File: ...ename". */
2786
	waddnstr(topwin, prefix, actual_x(prefix, prefixlen));
2787
	if (space <= -3 || newfie)
2788
2789
	    goto the_end;
	waddch(topwin, ' ');
2790
2791
	waddnstr(topwin, "...", space + 3);
	if (space <= 0)
2792
	    goto the_end;
2793
	waddstr(topwin, exppath);
2794
2795
2796
2797
2798
2799
    }

  the_end:
    free(exppath);

    if (COLS <= 1 || statelen >= COLS - 1)
2800
	mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
2801
2802
    else {
	assert(COLS - statelen - 2 >= 0);
2803

2804
	mvwaddch(topwin, 0, COLS - statelen - 2, ' ');
2805
2806
	mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
		actual_x(state, statelen));
2807
    }
2808

Chris Allegretta's avatar
Chris Allegretta committed
2809
    wattroff(topwin, A_REVERSE);
2810

2811
    wnoutrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
2812
    reset_cursor();
2813
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2814
2815
}

2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
/* 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) {
2843
	char *bar, *foo;
2844
	size_t start_x = 0, foo_len;
2845
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
2846
	bool old_whitespace = ISSET(WHITESPACE_DISPLAY);
2847

2848
2849
	UNSET(WHITESPACE_DISPLAY);
#endif
2850
2851
	bar = charalloc(mb_cur_max() * (COLS - 3));
	vsnprintf(bar, mb_cur_max() * (COLS - 3), msg, ap);
2852
	va_end(ap);
2853
	foo = display_string(bar, 0, COLS - 4, FALSE);
2854
2855
2856
2857
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
	if (old_whitespace)
	    SET(WHITESPACE_DISPLAY);
#endif
2858
	free(bar);
2859
	foo_len = strlenpt(foo);
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
	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);
2878
    statusblank = 26;
2879
2880
}

2881
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
2882
{
2883
    size_t i, colwidth, slen;
2884

Chris Allegretta's avatar
Chris Allegretta committed
2885
2886
2887
    if (ISSET(NO_HELP))
	return;

2888
2889
    if (s == main_list) {
	slen = MAIN_VISIBLE;
2890

2891
	assert(slen <= length_of_list(s));
2892
    } else {
2893
2894
	slen = length_of_list(s);

2895
	/* Don't show any more shortcuts than the main list does. */
2896
2897
2898
2899
	if (slen > MAIN_VISIBLE)
	    slen = MAIN_VISIBLE;
    }

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

2904
    blank_bottombars();
2905

2906
    for (i = 0; i < slen; i++, s = s->next) {
2907
	const char *keystr;
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
	char foo[4] = "";

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

	keystr = foo;
2923
2924

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

2928
2929
2930
    wnoutrefresh(bottomwin);
    reset_cursor();
    wrefresh(edit);
Chris Allegretta's avatar
Chris Allegretta committed
2931
2932
}

2933
2934
2935
2936
2937
2938
/* 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
2939
{
2940
2941
    size_t keystroke_len = strlenpt(keystroke) + 1;

2942
2943
    assert(keystroke != NULL && desc != NULL);

2944
    wattron(bottomwin, A_REVERSE);
2945
    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2946
    wattroff(bottomwin, A_REVERSE);
2947
2948
2949
2950
2951
2952

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

2953
2954
    if (len > 0) {
	waddch(bottomwin, ' ');
2955
	waddnstr(bottomwin, desc, actual_x(desc, len));
Chris Allegretta's avatar
Chris Allegretta committed
2956
2957
2958
    }
}

2959
2960
2961
2962
2963
/* 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
2964
{
2965
2966
2967
2968
    assert(COLS > 0);
    if (column == 0 || column < COLS - 1)
	return 0;
    else if (COLS > 9)
2969
	return column - 7 - (column - 7) % (COLS - 8);
2970
2971
2972
2973
2974
    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
2975
2976
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2977
/* Resets current_y, based on the position of current, and puts the
2978
 * cursor in the edit window at (current_y, current_x). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2979
2980
void reset_cursor(void)
{
2981
2982
2983
2984
    /* 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
2985
	return;
2986
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
2987

2988
2989
2990
2991
2992
    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
2993
}
Chris Allegretta's avatar
Chris Allegretta committed
2994

2995
2996
2997
2998
/* 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
2999
3000
 * characters.  start is the column number of the first character of
 * this page.  That is, the first character of converted corresponds to
3001
 * character number actual_x(fileptr->data, start) of the line. */
3002
3003
void edit_add(const filestruct *fileptr, const char *converted, int
	yval, size_t start)
Chris Allegretta's avatar
Chris Allegretta committed
3004
{
3005
#if !defined(NANO_SMALL) || defined(ENABLE_COLOR)
3006
3007
3008
3009
3010
3011
3012
3013
3014
    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. */
3015
3016
#endif

3017
    assert(fileptr != NULL && converted != NULL);
3018
    assert(strlenpt(converted) <= COLS);
3019

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

Chris Allegretta's avatar
Chris Allegretta committed
3024
#ifdef ENABLE_COLOR
3025
    if (colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
3026
3027
3028
3029
3030
3031
	const colortype *tmpcolor = colorstrings;

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
3032
		/* Number of chars to paint on this line.  There are COLS
3033
		 * characters on a whole line. */
3034
	    size_t index;
3035
		/* Index in converted where we paint. */
3036
3037
	    regmatch_t startmatch;	/* match position for start_regexp */
	    regmatch_t endmatch;	/* match position for end_regexp */
3038
3039
3040
3041

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
3042
3043
	    /* Two notes about regexec().  Return value 0 means there is
	     * a match.  Also, rm_eo is the first non-matching character
3044
3045
3046
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
3047
	    if (tmpcolor->end == NULL) {
3048
3049
3050
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
3051
3052
3053
3054
3055
3056
3057
3058
		 * 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. */
3059
		    if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
3060
			&startmatch, (k == 0) ? 0 :
3061
			REG_NOTBOL) == REG_NOMATCH)
3062
			break;
3063
3064
		    /* Translate the match to the beginning of the
		     * line. */
3065
3066
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
3067
3068
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
3069
3070
			statusbar(
				_("Refusing zero-length regex match"));
3071
		    } else if (startmatch.rm_so < endpos &&
3072
			startmatch.rm_eo > startpos) {
3073
			if (startmatch.rm_so <= startpos)
3074
			    x_start = 0;
3075
			else
3076
3077
			    x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
3078

3079
3080
3081
			index = actual_x(converted, x_start);

			paintlen = actual_x(converted + index,
3082
3083
				strnlenpt(fileptr->data,
				startmatch.rm_eo) - start - x_start);
3084
3085
3086

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

3087
			mvwaddnstr(edit, yval, x_start,
3088
				converted + index, paintlen);
3089
		    }
3090
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
3091
		}
3092
	    } else {
3093
		/* This is a multi-line regexp.  There are two steps.
3094
3095
3096
3097
3098
3099
		 * 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
3100
3101
3102
3103
		 * 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. */
3104
		const filestruct *start_line = fileptr->prev;
3105
		    /* The first line before fileptr matching start. */
3106
		regoff_t start_col;
3107
		    /* Where it starts in that line. */
3108
3109
3110
		const filestruct *end_line;

		while (start_line != NULL &&
3111
			regexec(&tmpcolor->start, start_line->data, 1,
3112
			&startmatch, 0) == REG_NOMATCH) {
3113
3114
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
3115
3116
		    if (regexec(tmpcolor->end, start_line->data, 0,
			NULL, 0) == 0)
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
			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;
3127
		while (TRUE) {
3128
3129
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
3130
3131
 		    if (regexec(tmpcolor->end, start_line->data +
			start_col + startmatch.rm_eo, 0, NULL,
3132
			(start_col + startmatch.rm_eo == 0) ? 0 :
3133
3134
			REG_NOTBOL) == REG_NOMATCH)
			/* No end found after this start. */
3135
			break;
3136
		    start_col++;
3137
3138
3139
		    if (regexec(&tmpcolor->start, start_line->data +
			start_col, 1, &startmatch,
			REG_NOTBOL) == REG_NOMATCH)
3140
3141
			/* No later start on this line. */
			goto step_two;
3142
		}
3143
3144
		/* Indeed, there is a start not followed on this line by
		 * an end. */
3145
3146
3147

		/* We have already checked that there is no end before
		 * fileptr and after the start.  Is there an end after
3148
3149
		 * the start at all?  We don't paint unterminated
		 * starts. */
3150
		end_line = fileptr;
3151
		while (end_line != NULL &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3152
3153
			regexec(tmpcolor->end, end_line->data, 1,
			&endmatch, 0) == REG_NOMATCH)
3154
3155
3156
		    end_line = end_line->next;

		/* No end found, or it is too early. */
3157
3158
		if (end_line == NULL || (end_line == fileptr &&
			endmatch.rm_eo <= startpos))
3159
3160
3161
		    goto step_two;

		/* Now paint the start of fileptr. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
		if (end_line != fileptr)
		    /* If the start of fileptr is on a different line
		     * from the end, paintlen is -1, meaning that
		     * everything on the line gets painted. */
		    paintlen = -1;
		else
		    /* Otherwise, paintlen is the expanded location of
		     * the end of the match minus the expanded location
		     * of the beginning of the page. */
		    paintlen = actual_x(converted,
			strnlenpt(fileptr->data, endmatch.rm_eo) -
			start);
3174

3175
		mvwaddnstr(edit, yval, 0, converted, paintlen);
3176

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3177
3178
  step_two:
		/* Second step, we look for starts on this line. */
3179
		start_col = 0;
3180

3181
		while (start_col < endpos) {
3182
3183
		    if (regexec(&tmpcolor->start,
			fileptr->data + start_col, 1, &startmatch,
3184
3185
			(start_col == 0) ? 0 :
			REG_NOTBOL) == REG_NOMATCH ||
3186
			start_col + startmatch.rm_so >= endpos)
3187
3188
3189
3190
3191
3192
3193
			/* 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;

3194
		    if (startmatch.rm_so <= startpos)
3195
			x_start = 0;
3196
		    else
3197
3198
			x_start = strnlenpt(fileptr->data,
				startmatch.rm_so) - start;
3199

3200
		    index = actual_x(converted, x_start);
3201

3202
3203
		    if (regexec(tmpcolor->end,
			fileptr->data + startmatch.rm_eo, 1, &endmatch,
3204
3205
			(startmatch.rm_eo == 0) ? 0 :
			REG_NOTBOL) == 0) {
3206
			/* Translate the end match to be relative to the
3207
			 * beginning of the line. */
3208
3209
3210
			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
3211
3212
			 * appear on this page, and is the match more
			 * than zero characters long? */
3213
			if (endmatch.rm_eo > startpos &&
3214
				endmatch.rm_eo > startmatch.rm_so) {
3215
			    paintlen = actual_x(converted + index,
3216
3217
				strnlenpt(fileptr->data,
				endmatch.rm_eo) - start - x_start);
3218
3219

			    assert(0 <= x_start && x_start < COLS);
3220
3221

			    mvwaddnstr(edit, yval, x_start,
3222
				converted + index, paintlen);
3223
			}
3224
		    } else {
3225
3226
3227
			/* There is no end on this line.  But we haven't
			 * yet looked for one on later lines. */
			end_line = fileptr->next;
3228

3229
			while (end_line != NULL &&
3230
3231
				regexec(tmpcolor->end, end_line->data,
				0, NULL, 0) == REG_NOMATCH)
3232
			    end_line = end_line->next;
3233

3234
			if (end_line != NULL) {
3235
			    assert(0 <= x_start && x_start < COLS);
3236

3237
			    mvwaddnstr(edit, yval, x_start,
3238
				converted + index, -1);
3239
3240
3241
			    /* We painted to the end of the line, so
			     * don't bother checking any more starts. */
			    break;
3242
3243
			}
		    }
3244
		    start_col = startmatch.rm_so + 1;
3245
3246
		}
	    }
3247

3248
3249
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
3250
	}
3251
    }
3252
#endif /* ENABLE_COLOR */
3253

3254
3255
3256
3257
3258
3259
3260
3261
#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. */

3262
3263
3264
3265
3266
3267
	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;
3268
3269
3270
	int x_start;
	    /* Starting column for mvwaddnstr.  Zero-based. */
	int paintlen;
3271
	    /* Number of chars to paint on this line.  There are COLS
3272
	     * characters on a whole line. */
3273
	size_t index;
3274
	    /* Index in converted where we paint. */
3275

3276
	mark_order(&top, &top_x, &bot, &bot_x, NULL);
3277
3278
3279
3280
3281

	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
3282

3283
	/* The selected bit of fileptr is on this page. */
3284
3285
	if (top_x < endpos && bot_x > startpos) {
	    assert(startpos <= top_x);
3286
3287
3288
3289

	    /* 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;
3290
3291

	    if (bot_x >= endpos)
3292
3293
3294
3295
		/* If the end of the mark is off the page, paintlen is
		 * -1, meaning that everything on the line gets
		 * painted. */
		paintlen = -1;
3296
	    else
3297
3298
3299
		/* Otherwise, paintlen is the expanded location of the
		 * end of the mark minus the expanded location of the
		 * beginning of the mark. */
3300
3301
		paintlen = strnlenpt(fileptr->data, bot_x) -
			(x_start + start);
3302
3303
3304
3305
3306
3307
3308
3309

	    /* 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;
	    }
3310
3311
3312

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

3313
	    index = actual_x(converted, x_start);
3314

3315
3316
3317
	    if (paintlen > 0)
		paintlen = actual_x(converted + index, paintlen);

3318
	    wattron(edit, A_REVERSE);
3319
	    mvwaddnstr(edit, yval, x_start, converted + index,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3320
		paintlen);
3321
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
3322
	}
3323
    }
3324
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
3325
3326
}

3327
3328
/* Just update one line in the edit buffer.  This is basically a wrapper
 * for edit_add().
3329
 *
3330
3331
3332
 * 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. */
3333
void update_line(const filestruct *fileptr, size_t index)
Chris Allegretta's avatar
Chris Allegretta committed
3334
{
3335
    int line;
3336
	/* The line in the edit window that we want to update. */
3337
3338
3339
3340
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
3341

3342
    assert(fileptr != NULL);
3343

3344
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
3345

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

3349
3350
    if (line < 0 || line >= editwinrows)
	return;
3351

3352
    /* First, blank out the line. */
3353
3354
    mvwaddstr(edit, line, 0, hblank);

3355
3356
    /* Next, convert variables that index the line to their equivalent
     * positions in the expanded line. */
3357
    index = (fileptr == current) ? strnlenpt(fileptr->data, index) : 0;
3358
    page_start = get_page_start(index);
3359

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

3364
    /* Paint the line. */
3365
    edit_add(fileptr, converted, line, page_start);
3366
    free(converted);
Chris Allegretta's avatar
Chris Allegretta committed
3367

3368
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
3369
	mvwaddch(edit, line, 0, '$');
3370
    if (strlenpt(fileptr->data) > page_start + COLS)
3371
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
3372
3373
}

3374
3375
/* 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
3376
 * placewewant are on different pages. */
3377
int need_horizontal_update(size_t old_pww)
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
{
    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
3388
 * are on different pages. */
3389
int need_vertical_update(size_t old_pww)
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
{
    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
3405
 * on the page or whether we'll stay there. */
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
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
3463
 * updated. */
3464
void edit_redraw(const filestruct *old_current, size_t old_pww)
3465
{
3466
3467
    int do_refresh = need_vertical_update(0) ||
	need_vertical_update(old_pww);
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
    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;
    }

3480
3481
3482
    /* 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. */
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
    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
3500
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
3501
3502
void edit_refresh(void)
{
3503
3504
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
3505
3506
3507
3508
3509
3510
3511
3512
	/* 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);
3513
3514
    else {
	int nlines = 0;
3515
	const filestruct *foo = edittop;
Chris Allegretta's avatar
Chris Allegretta committed
3516

3517
#ifdef DEBUG
3518
	fprintf(stderr, "edit_refresh(): edittop->lineno = %d\n", edittop->lineno);
3519
#endif
3520

3521
	while (nlines < editwinrows) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3522
	    update_line(foo, foo == current ? current_x : 0);
3523
	    nlines++;
3524
	    if (foo->next == NULL)
3525
		break;
3526
	    foo = foo->next;
3527
3528
3529
3530
3531
	}
	while (nlines < editwinrows) {
	    mvwaddstr(edit, nlines, 0, hblank);
	    nlines++;
	}
3532
	reset_cursor();
3533
3534
	wrefresh(edit);
    }
Chris Allegretta's avatar
Chris Allegretta committed
3535
3536
}

3537
3538
/* 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. */
3539
void edit_update(topmidnone location)
Chris Allegretta's avatar
Chris Allegretta committed
3540
{
3541
3542
    filestruct *foo = current;

Chris Allegretta's avatar
Chris Allegretta committed
3543
    if (location != TOP) {
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
	/* 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
3563

3564
3565
	for (; goal > 0 && foo->prev != NULL; goal--)
	    foo = foo->prev;
Chris Allegretta's avatar
Chris Allegretta committed
3566
    }
3567

3568
    edittop = foo;
Chris Allegretta's avatar
Chris Allegretta committed
3569
3570
3571
    edit_refresh();
}

3572
3573
3574
/* 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). */
3575
int do_yesno(bool all, const char *msg)
Chris Allegretta's avatar
Chris Allegretta committed
3576
{
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3577
    int ok = -2, width = 16;
3578
3579
    const char *yesstr;		/* String of yes characters accepted. */
    const char *nostr;		/* Same for no. */
Chris Allegretta's avatar
Chris Allegretta committed
3580
    const char *allstr;		/* And all, surprise! */
Chris Allegretta's avatar
Chris Allegretta committed
3581

3582
    /* yesstr, nostr, and allstr are strings of any length.  Each string
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3583
3584
3585
3586
3587
     * consists of all single-byte characters accepted as valid
     * characters for that value.  The first value will be the one
     * displayed in the shortcuts.  Translators: if possible, specify
     * both the shortcuts for your language and English.  For example,
     * in French: "OoYy" for "Oui". */
3588
3589
3590
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
3591
3592

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

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3595
3596
3597
	if (COLS < 32)
	    width = COLS / 2;

3598
	/* Write the bottom of the screen. */
3599
	blank_bottombars();
3600

3601
	sprintf(shortstr, " %c", yesstr[0]);
3602
	wmove(bottomwin, 1, 0);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3603
	onekey(shortstr, _("Yes"), width);
3604
3605

	if (all) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3606
	    wmove(bottomwin, 1, width);
3607
	    shortstr[1] = allstr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3608
	    onekey(shortstr, _("All"), width);
3609
3610
	}

3611
	wmove(bottomwin, 2, 0);
3612
	shortstr[1] = nostr[0];
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3613
	onekey(shortstr, _("No"), width);
3614

3615
	wmove(bottomwin, 2, 16);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3616
	onekey("^C", _("Cancel"), width);
Chris Allegretta's avatar
Chris Allegretta committed
3617
    }
3618

Chris Allegretta's avatar
Chris Allegretta committed
3619
    wattron(bottomwin, A_REVERSE);
3620
3621

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

Chris Allegretta's avatar
Chris Allegretta committed
3624
    wattroff(bottomwin, A_REVERSE);
3625

Chris Allegretta's avatar
Chris Allegretta committed
3626
3627
    wrefresh(bottomwin);

3628
    do {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3629
	int kbinput;
3630
	bool meta_key, func_key;
3631
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3632
	int mouse_x, mouse_y;
Chris Allegretta's avatar
Chris Allegretta committed
3633
#endif
3634

3635
	kbinput = get_kbinput(edit, &meta_key, &func_key);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3636

3637
3638
3639
3640
	if (kbinput == NANO_REFRESH_KEY) {
	    total_update();
	    continue;
	} else if (kbinput == NANO_CANCEL_KEY)
3641
	    ok = -1;
3642
#ifndef DISABLE_MOUSE
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3643
	else if (kbinput == KEY_MOUSE) {
3644
	    get_mouseinput(&mouse_x, &mouse_y, FALSE);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3645
3646

	    if (mouse_x != -1 && mouse_y != -1 && !ISSET(NO_HELP) &&
3647
3648
		wenclose(bottomwin, mouse_y, mouse_x) &&
		mouse_x < (width * 2) && mouse_y >= editwinrows + 3) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
		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
3664
	}
3665
#endif
3666
	/* Look for the kbinput in the yes, no and (optionally) all
3667
	 * strings. */
3668
3669
3670
3671
3672
3673
3674
3675
3676
	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
3677
3678
}

3679
void total_update(void)
3680
{
3681
3682
    clearok(edit, TRUE);
    wrefresh(edit);
3683
3684
3685
3686
}

void total_refresh(void)
{
3687
    total_update();
3688
    titlebar(NULL);
3689
    edit_refresh();
3690
    bottombars(currshortcut);
3691
3692
3693
3694
3695
3696
3697
}

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

3698
3699
3700
/* 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.
3701
 *
3702
3703
3704
 * 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. */
3705
void do_cursorpos(bool constant)
Chris Allegretta's avatar
Chris Allegretta committed
3706
{
3707
3708
    char c;
    filestruct *f;
3709
    size_t i = 0;
3710
    static size_t old_i = 0, old_totsize = (size_t)-1;
Chris Allegretta's avatar
Chris Allegretta committed
3711

3712
    assert(current != NULL && fileage != NULL && totlines != 0);
3713

3714
    if (old_totsize == (size_t)-1)
3715
3716
	old_totsize = totsize;

3717
3718
3719
3720
3721
3722
3723
    c = current->data[current_x];
    f = current->next;
    current->data[current_x] = '\0';
    current->next = NULL;
    get_totals(fileage, current, NULL, &i);
    current->data[current_x] = c;
    current->next = f;
3724

3725
3726
3727
3728
    /* Check whether totsize is correct.  Else there is a bug
     * somewhere. */
    assert(current != filebot || i == totsize);

3729
3730
3731
3732
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
3733
	return;
3734
    }
Chris Allegretta's avatar
Chris Allegretta committed
3735

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3736
    /* If constant is FALSE, display the position on the statusbar
3737
3738
     * unconditionally; otherwise, only display the position when the
     * character values have changed. */
3739
    if (!constant || old_i != i || old_totsize != totsize) {
3740
3741
	size_t xpt = xplustabs() + 1;
	size_t cur_len = strlenpt(current->data) + 1;
3742
3743
	int linepct = 100 * current->lineno / totlines;
	int colpct = 100 * xpt / cur_len;
3744
	int bytepct = (totsize == 0) ? 0 : 100 * i / totsize;
3745
3746
3747
3748

	statusbar(
	    _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
		    current->lineno, totlines, linepct,
3749
		    (unsigned long)xpt, (unsigned long)cur_len, colpct,
3750
		    (unsigned long)i, (unsigned long)totsize, bytepct);
3751
	UNSET(DISABLE_CURPOS);
3752
3753
3754
3755
    }

    old_i = i;
    old_totsize = totsize;
Chris Allegretta's avatar
Chris Allegretta committed
3756
3757
}

3758
void do_cursorpos_void(void)
3759
{
3760
    do_cursorpos(FALSE);
3761
3762
}

3763
#ifndef DISABLE_HELP
3764
/* Calculate the next line of help_text, starting at ptr. */
3765
3766
size_t help_line_len(const char *ptr)
{
3767
    int help_cols = (COLS > 24) ? COLS - 8 : 24;
3768
3769

    /* Try to break the line at (COLS - 8) columns if we have more than
3770
     * 24 columns, and at 24 columns otherwise. */
3771
    size_t retval = break_line(ptr, help_cols, TRUE);
3772
    size_t retval_save = retval;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3773

3774
3775
3776
    /* Get the length of the entire line up to a null or a newline. */
    while (*(ptr + retval) != '\0' && *(ptr + retval) != '\n')
	retval += move_mbright(ptr + retval, 0);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3777

3778
3779
3780
3781
3782
3783
3784
    /* If the entire line doesn't go more than 8 columns beyond where we
     * tried to break it, we should display it as-is.  Otherwise, we
     * should display it only up to the break. */
    if (strnlenpt(ptr, retval) > help_cols + 8)
	retval = retval_save;

    return retval;
3785
3786
}

3787
/* Our dynamic, shortcut-list-compliant help function. */
3788
void do_help(void)
Chris Allegretta's avatar
Chris Allegretta committed
3789
{
3790
    int line = 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3791
3792
	/* The line number in help_text of the first displayed help
	 * line.  This variable is zero-based. */
3793
    bool no_more = FALSE;
3794
3795
	/* no_more means the end of the help text is shown, so don't go
	 * down any more. */
3796
    int kbinput = ERR;
3797
    bool meta_key, func_key;
3798

3799
    bool old_no_help = ISSET(NO_HELP);
3800
3801
3802
#ifndef DISABLE_MOUSE
    const shortcut *oldshortcut = currshortcut;
	/* We will set currshortcut to allow clicking on the help
3803
	 * screen's shortcut list. */
3804
#endif
Chris Allegretta's avatar
Chris Allegretta committed
3805

3806
    curs_set(0);
Chris Allegretta's avatar
Chris Allegretta committed
3807
    blank_edit();
3808
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
3809
3810
    blank_statusbar();

3811
    /* Set help_text as the string to display. */
3812
    help_init();
3813

3814
    assert(help_text != NULL);
3815

3816
#ifndef DISABLE_MOUSE
3817
    /* Set currshortcut to allow clicking on the help screen's shortcut
3818
     * list, AFTER help_init(). */
3819
    currshortcut = help_list;
3820
#endif
3821

Chris Allegretta's avatar
Chris Allegretta committed
3822
    if (ISSET(NO_HELP)) {
3823
3824
	/* Make sure that the help screen's shortcut list will actually
	 * be displayed. */
Chris Allegretta's avatar
Chris Allegretta committed
3825
	UNSET(NO_HELP);
3826
	window_init();
3827
    }
3828

3829
    bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
3830
3831

    do {
3832
3833
3834
	int i;
	int old_line = line;
	    /* We redisplay the help only if it moved. */
3835
	const char *ptr = help_text;
3836

Chris Allegretta's avatar
Chris Allegretta committed
3837
	switch (kbinput) {
3838
#ifndef DISABLE_MOUSE
3839
	    case KEY_MOUSE:
3840
3841
3842
3843
		{
		    int mouse_x, mouse_y;
		    get_mouseinput(&mouse_x, &mouse_y, TRUE);
		}
3844
		break;
3845
#endif
3846
3847
3848
3849
3850
3851
3852
3853
	    case NANO_PREVPAGE_KEY:
	    case NANO_PREVPAGE_FKEY:
		if (line > 0) {
		    line -= editwinrows - 2;
		    if (line < 0)
			line = 0;
		}
		break;
3854
3855
3856
3857
3858
	    case NANO_NEXTPAGE_KEY:
	    case NANO_NEXTPAGE_FKEY:
		if (!no_more)
		    line += editwinrows - 2;
		break;
3859
3860
3861
3862
3863
3864
3865
3866
	    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
3867
3868
	}

3869
	if (kbinput == NANO_REFRESH_KEY)
3870
	    total_update();
3871
3872
3873
	else {
	    if (line == old_line && kbinput != ERR)
		goto skip_redisplay;
3874

3875
3876
	    blank_edit();
	}
3877

3878
3879
	/* Calculate where in the text we should be, based on the
	 * page. */
3880
	for (i = 0; i < line; i++) {
3881
	    ptr += help_line_len(ptr);
3882
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
3883
3884
3885
		ptr++;
	}

3886
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
3887
	    size_t j = help_line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
3888
3889

	    mvwaddnstr(edit, i, 0, ptr, j);
3890
3891
3892
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
3893
	}
3894
	no_more = (*ptr == '\0');
3895

3896
  skip_redisplay:
3897
	kbinput = get_kbinput(edit, &meta_key, &func_key);
3898
    } while (kbinput != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
3899

3900
#ifndef DISABLE_MOUSE
3901
    currshortcut = oldshortcut;
3902
#endif
3903

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3904
    if (old_no_help) {
3905
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
3906
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
3907
	SET(NO_HELP);
3908
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
3909
    } else
3910
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
3911

3912
    curs_set(1);
Chris Allegretta's avatar
Chris Allegretta committed
3913
    edit_refresh();
3914

3915
3916
3917
    /* 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. */
3918
3919
    free(help_text);
    help_text = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
3920
}
3921
#endif /* !DISABLE_HELP */
Chris Allegretta's avatar
Chris Allegretta committed
3922

3923
3924
/* Highlight the current word being replaced or spell checked.  We
 * expect word to have tabs and control characters expanded. */
3925
void do_replace_highlight(bool highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
3926
{
3927
    size_t y = xplustabs();
3928
    size_t word_len = strlenpt(word);
Chris Allegretta's avatar
Chris Allegretta committed
3929

3930
    y = get_page_start(y) + COLS - y;
3931
	/* Now y is the number of columns that we can display on this
3932
	 * line. */
Chris Allegretta's avatar
Chris Allegretta committed
3933

3934
3935
3936
3937
3938
    assert(y > 0);

    if (word_len > y)
	y--;

Chris Allegretta's avatar
Chris Allegretta committed
3939
    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
3940

Chris Allegretta's avatar
Chris Allegretta committed
3941
3942
3943
    if (highlight_flag)
	wattron(edit, A_REVERSE);

3944
#ifdef HAVE_REGEX_H
3945
3946
    /* This is so we can show zero-length regexes. */
    if (word_len == 0)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3947
	waddch(edit, ' ');
3948
    else
3949
#endif
3950
	waddnstr(edit, word, actual_x(word, y));
3951
3952
3953

    if (word_len > y)
	waddch(edit, '$');
Chris Allegretta's avatar
Chris Allegretta committed
3954
3955
3956
3957
3958

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

3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
#ifndef NDEBUG
/* Return what the current line number should be, starting at edittop
 * and ending at fileptr. */
int check_linenumbers(const filestruct *fileptr)
{
    int check_line = 0;
    const filestruct *filetmp;

    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;

    return check_line;
}
#endif

3974
#ifdef DEBUG
3975
3976
3977
/* Dump the passed-in file structure to stderr. */
void dump_buffer(const filestruct *inptr)
{
3978
    if (inptr == fileage)
3979
	fprintf(stderr, "Dumping file buffer to stderr...\n");
3980
    else if (inptr == cutbuffer)
3981
	fprintf(stderr, "Dumping cutbuffer to stderr...\n");
3982
    else
3983
	fprintf(stderr, "Dumping a buffer to stderr...\n");
3984
3985
3986
3987
3988
3989
3990

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

3991
/* Dump the file structure to stderr in reverse. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
3992
3993
void dump_buffer_reverse(void)
{
3994
3995
3996
    const filestruct *fileptr = filebot;

    while (fileptr != NULL) {
3997
	fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
3998
3999
4000
4001
4002
	fileptr = fileptr->prev;
    }
}
#endif /* DEBUG */

4003
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
4004
#define CREDIT_LEN 53
4005
4006
#define XLCREDIT_LEN 8

4007
/* Easter egg: Display credits.  Assume nodelay(edit) is FALSE. */
4008
4009
void do_credits(void)
{
4010
    int kbinput = ERR, crpos = 0, xlpos = 0;
4011
4012
4013
    const char *credits[CREDIT_LEN] = {
	NULL,				/* "The nano text editor" */
	NULL,				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
4014
4015
	VERSION,
	"",
4016
	NULL,				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
4017
4018
4019
4020
4021
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
4022
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
4023
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
4024
4025
	"Ken Tyler",
	"Sven Guckes",
4026
	"Florian K\xF6nig",
Chris Allegretta's avatar
Chris Allegretta committed
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
4039
	NULL,				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
4040
4041
4042
4043
4044
4045
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
4046
	NULL,				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
4047
	"Linus Torvalds",
4048
	NULL,				/* "For ncurses:" */
4049
4050
4051
4052
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
4053
4054
4055
4056
4057
4058
	NULL,				/* "and anyone else we forgot..." */
	NULL,				/* "Thank you for using nano!" */
	"",
	"",
	"",
	"",
4059
	"(c) 1999-2005 Chris Allegretta",
4060
4061
4062
4063
	"",
	"",
	"",
	"",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
4064
	"http://www.nano-editor.org/"
4065
4066
    };

4067
    const char *xlcredits[XLCREDIT_LEN] = {
4068
4069
4070
4071
4072
4073
4074
4075
	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!")
4076
    };
4077

4078
4079
    curs_set(0);
    nodelay(edit, TRUE);
4080
4081
    scrollok(edit, TRUE);
    blank_titlebar();
4082
    blank_topbar();
Chris Allegretta's avatar
Chris Allegretta committed
4083
    blank_edit();
4084
4085
4086
    blank_statusbar();
    blank_bottombars();
    wrefresh(topwin);
Chris Allegretta's avatar
Chris Allegretta committed
4087
    wrefresh(edit);
4088
4089
    wrefresh(bottomwin);

4090
    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
4091
	if ((kbinput = wgetch(edit)) != ERR)
4092
	    break;
4093

4094
	if (crpos < CREDIT_LEN) {
4095
	    char *what;
4096
4097
	    size_t start_x;

4098
4099
4100
	    /* Make sure every credit is a valid multibyte string, since
	     * we can't dynamically set the credits to their multibyte
	     * equivalents when we need to.  Sigh... */
4101
	    if (credits[crpos] == NULL) {
4102
		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
4103
4104

		what = mallocstrcpy(NULL, _(xlcredits[xlpos]));
4105
		xlpos++;
4106
	    } else
4107
		what = make_valid_mbstring(credits[crpos]);
4108

4109
	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
4110
4111
4112
4113
	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
		start_x, what);

	    free(what);
4114
	}
4115

4116
4117
4118
	napms(700);
	scroll(edit);
	wrefresh(edit);
4119
	if ((kbinput = wgetch(edit)) != ERR)
4120
	    break;
4121
4122
4123
	napms(700);
	scroll(edit);
	wrefresh(edit);
4124
4125
    }

4126
4127
4128
4129
    if (kbinput != ERR)
	ungetch(kbinput);

    curs_set(1);
4130
    scrollok(edit, FALSE);
4131
4132
    nodelay(edit, FALSE);
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
4133
}
4134
#endif