1/* $Id: winio.c,v 1.582.2.4 2007/04/19 03:15:04 dolorous Exp $ */
2/**************************************************************************
3 *   winio.c                                                              *
4 *                                                                        *
5 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Chris Allegretta    *
6 *   Copyright (C) 2005, 2006, 2007 David Lawrence Ramsey                 *
7 *   This program is free software; you can redistribute it and/or modify *
8 *   it under the terms of the GNU General Public License as published by *
9 *   the Free Software Foundation; either version 2, or (at your option)  *
10 *   any later version.                                                   *
11 *                                                                        *
12 *   This program is distributed in the hope that it will be useful, but  *
13 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
15 *   General Public License for more details.                             *
16 *                                                                        *
17 *   You should have received a copy of the GNU General Public License    *
18 *   along with this program; if not, write to the Free Software          *
19 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
20 *   02110-1301, USA.                                                     *
21 *                                                                        *
22 **************************************************************************/
23
24#include "proto.h"
25
26#include <stdio.h>
27#include <stdarg.h>
28#include <string.h>
29#include <unistd.h>
30#include <ctype.h>
31
32static int *key_buffer = NULL;
33	/* The keystroke buffer, containing all the keystrokes we
34	 * haven't handled yet at a given point. */
35static size_t key_buffer_len = 0;
36	/* The length of the keystroke buffer. */
37static int statusblank = 0;
38	/* The number of keystrokes left after we call statusbar(),
39	 * before we actually blank the statusbar. */
40static bool disable_cursorpos = FALSE;
41	/* Should we temporarily disable constant cursor position
42	 * display? */
43
44/* Control character compatibility:
45 *
46 * - NANO_BACKSPACE_KEY is Ctrl-H, which is Backspace under ASCII, ANSI,
47 *   VT100, and VT220.
48 * - NANO_TAB_KEY is Ctrl-I, which is Tab under ASCII, ANSI, VT100,
49 *   VT220, and VT320.
50 * - NANO_ENTER_KEY is Ctrl-M, which is Enter under ASCII, ANSI, VT100,
51 *   VT220, and VT320.
52 * - NANO_XON_KEY is Ctrl-Q, which is XON under ASCII, ANSI, VT100,
53 *   VT220, and VT320.
54 * - NANO_XOFF_KEY is Ctrl-S, which is XOFF under ASCII, ANSI, VT100,
55 *   VT220, and VT320.
56 * - NANO_CONTROL_8 is Ctrl-8 (Ctrl-?), which is Delete under ASCII,
57 *   ANSI, VT100, and VT220, and which is Backspace under VT320.
58 *
59 * Note: VT220 and VT320 also generate Esc [ 3 ~ for Delete.  By
60 * default, xterm assumes it's running on a VT320 and generates Ctrl-8
61 * (Ctrl-?) for Backspace and Esc [ 3 ~ for Delete.  This causes
62 * problems for VT100-derived terminals such as the FreeBSD console,
63 * which expect Ctrl-H for Backspace and Ctrl-8 (Ctrl-?) for Delete, and
64 * on which the VT320 sequences are translated by the keypad to KEY_DC
65 * and [nothing].  We work around this conflict via the REBIND_DELETE
66 * flag: if it's not set, we assume VT320 compatibility, and if it is,
67 * we assume VT100 compatibility.  Thanks to Lee Nelson and Wouter van
68 * Hemel for helping work this conflict out.
69 *
70 * Escape sequence compatibility:
71 *
72 * We support escape sequences for ANSI, VT100, VT220, VT320, the Linux
73 * console, the FreeBSD console, the Mach console, xterm, rxvt, Eterm,
74 * and Terminal.  Among these, there are several conflicts and
75 * omissions, outlined as follows:
76 *
77 * - Tab on ANSI == PageUp on FreeBSD console; the former is omitted.
78 *   (Ctrl-I is also Tab on ANSI, which we already support.)
79 * - PageDown on FreeBSD console == Center (5) on numeric keypad with
80 *   NumLock off on Linux console; the latter is omitted.  (The editing
81 *   keypad key is more important to have working than the numeric
82 *   keypad key, because the latter has no value when NumLock is off.)
83 * - F1 on FreeBSD console == the mouse key on xterm/rxvt/Eterm; the
84 *   latter is omitted.  (Mouse input will only work properly if the
85 *   extended keypad value KEY_MOUSE is generated on mouse events
86 *   instead of the escape sequence.)
87 * - F9 on FreeBSD console == PageDown on Mach console; the former is
88 *   omitted.  (The editing keypad is more important to have working
89 *   than the function keys, because the functions of the former are not
90 *   arbitrary and the functions of the latter are.)
91 * - F10 on FreeBSD console == PageUp on Mach console; the former is
92 *   omitted.  (Same as above.)
93 * - F13 on FreeBSD console == End on Mach console; the former is
94 *   omitted.  (Same as above.)
95 * - F15 on FreeBSD console == Shift-Up on rxvt/Eterm; the former is
96 *   omitted.  (The arrow keys, with or without modifiers, are more
97 *   important to have working than the function keys, because the
98 *   functions of the former are not arbitrary and the functions of the
99 *   latter are.)
100 * - F16 on FreeBSD console == Shift-Down on rxvt/Eterm; the former is
101 *   omitted.  (Same as above.) */
102
103/* Read in a sequence of keystrokes from win and save them in the
104 * keystroke buffer.  This should only be called when the keystroke
105 * buffer is empty. */
106void get_key_buffer(WINDOW *win)
107{
108    int input;
109    size_t errcount;
110
111    /* If the keystroke buffer isn't empty, get out. */
112    if (key_buffer != NULL)
113	return;
114
115    /* Read in the first character using blocking input. */
116#ifndef NANO_TINY
117    allow_pending_sigwinch(TRUE);
118#endif
119
120    /* Just before reading in the first character, display any pending
121     * screen updates. */
122    doupdate();
123
124    errcount = 0;
125    while ((input = wgetch(win)) == ERR) {
126	errcount++;
127
128	/* If we've failed to get a character MAX_BUF_SIZE times in a
129	 * row, assume that the input source we were using is gone and
130	 * die gracefully.  We could check if errno is set to EIO
131	 * ("Input/output error") and die gracefully in that case, but
132	 * it's not always set properly.  Argh. */
133	if (errcount == MAX_BUF_SIZE)
134	    handle_hupterm(0);
135    }
136
137#ifndef NANO_TINY
138    allow_pending_sigwinch(FALSE);
139#endif
140
141    /* Increment the length of the keystroke buffer, save the value of
142     * the keystroke in key, and set key_code to TRUE if the keystroke
143     * is an extended keypad value or FALSE if it isn't. */
144    key_buffer_len++;
145    key_buffer = (int *)nmalloc(sizeof(int));
146    key_buffer[0] = input;
147
148    /* Read in the remaining characters using non-blocking input. */
149    nodelay(win, TRUE);
150
151    while (TRUE) {
152#ifndef NANO_TINY
153	allow_pending_sigwinch(TRUE);
154#endif
155
156	input = wgetch(win);
157
158	/* If there aren't any more characters, stop reading. */
159	if (input == ERR)
160	    break;
161
162	/* Otherwise, increment the length of the keystroke buffer, save
163	 * the value of the keystroke in key, and set key_code to TRUE
164	 * if the keystroke is an extended keypad value or FALSE if it
165	 * isn't. */
166	key_buffer_len++;
167	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
168		sizeof(int));
169	key_buffer[key_buffer_len - 1] = input;
170
171#ifndef NANO_TINY
172	allow_pending_sigwinch(FALSE);
173#endif
174    }
175
176    /* Switch back to non-blocking input. */
177    nodelay(win, FALSE);
178
179#ifdef DEBUG
180    fprintf(stderr, "get_key_buffer(): key_buffer_len = %lu\n", (unsigned long)key_buffer_len);
181#endif
182}
183
184/* Return the length of the keystroke buffer. */
185size_t get_key_buffer_len(void)
186{
187    return key_buffer_len;
188}
189
190/* Add the keystrokes in input to the keystroke buffer. */
191void unget_input(int *input, size_t input_len)
192{
193#ifndef NANO_TINY
194    allow_pending_sigwinch(TRUE);
195    allow_pending_sigwinch(FALSE);
196#endif
197
198    /* If input is empty, get out. */
199    if (input_len == 0)
200	return;
201
202    /* If adding input would put the keystroke buffer beyond maximum
203     * capacity, only add enough of input to put it at maximum
204     * capacity. */
205    if (key_buffer_len + input_len < key_buffer_len)
206	input_len = (size_t)-1 - key_buffer_len;
207
208    /* Add the length of input to the length of the keystroke buffer,
209     * and reallocate the keystroke buffer so that it has enough room
210     * for input. */
211    key_buffer_len += input_len;
212    key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
213	sizeof(int));
214
215    /* If the keystroke buffer wasn't empty before, move its beginning
216     * forward far enough so that we can add input to its beginning. */
217    if (key_buffer_len > input_len)
218	memmove(key_buffer + input_len, key_buffer,
219		(key_buffer_len - input_len) * sizeof(int));
220
221    /* Copy input to the beginning of the keystroke buffer. */
222    memcpy(key_buffer, input, input_len * sizeof(int));
223}
224
225/* Put back the character stored in kbinput, putting it in byte range
226 * beforehand.  If meta_key is TRUE, put back the Escape character after
227 * putting back kbinput.  If func_key is TRUE, put back the function key
228 * (a value outside byte range) without putting it in byte range. */
229void unget_kbinput(int kbinput, bool meta_key, bool func_key)
230{
231    if (!func_key)
232	kbinput = (char)kbinput;
233
234    unget_input(&kbinput, 1);
235
236    if (meta_key) {
237	kbinput = NANO_CONTROL_3;
238	unget_input(&kbinput, 1);
239    }
240}
241
242/* Try to read input_len characters from the keystroke buffer.  If the
243 * keystroke buffer is empty and win isn't NULL, try to read in more
244 * characters from win and add them to the keystroke buffer before doing
245 * anything else.  If the keystroke buffer is empty and win is NULL,
246 * return NULL. */
247int *get_input(WINDOW *win, size_t input_len)
248{
249    int *input;
250
251#ifndef NANO_TINY
252    allow_pending_sigwinch(TRUE);
253    allow_pending_sigwinch(FALSE);
254#endif
255
256    if (key_buffer_len == 0) {
257	if (win != NULL) {
258	    get_key_buffer(win);
259
260	    if (key_buffer_len == 0)
261		return NULL;
262	} else
263	    return NULL;
264    }
265
266    /* If input_len is greater than the length of the keystroke buffer,
267     * only read the number of characters in the keystroke buffer. */
268    if (input_len > key_buffer_len)
269	input_len = key_buffer_len;
270
271    /* Subtract input_len from the length of the keystroke buffer, and
272     * allocate input so that it has enough room for input_len
273     * keystrokes. */
274    key_buffer_len -= input_len;
275    input = (int *)nmalloc(input_len * sizeof(int));
276
277    /* Copy input_len keystrokes from the beginning of the keystroke
278     * buffer into input. */
279    memcpy(input, key_buffer, input_len * sizeof(int));
280
281    /* If the keystroke buffer is empty, mark it as such. */
282    if (key_buffer_len == 0) {
283	free(key_buffer);
284	key_buffer = NULL;
285    /* If the keystroke buffer isn't empty, move its beginning forward
286     * far enough so that the keystrokes in input are no longer at its
287     * beginning. */
288    } else {
289	memmove(key_buffer, key_buffer + input_len, key_buffer_len *
290		sizeof(int));
291	key_buffer = (int *)nrealloc(key_buffer, key_buffer_len *
292		sizeof(int));
293    }
294
295    return input;
296}
297
298/* Read in a single character.  If it's ignored, swallow it and go on.
299 * Otherwise, try to translate it from ASCII, meta key sequences, escape
300 * sequences, and/or extended keypad values.  Set meta_key to TRUE when
301 * we get a meta key sequence, and set func_key to TRUE when we get an
302 * extended keypad value.  Supported extended keypad values consist of
303 * [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace,
304 * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown),
305 * the function keypad (F1-F16), and the numeric keypad with NumLock
306 * off.  Assume nodelay(win) is FALSE. */
307int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
308{
309    int kbinput;
310
311    /* Read in a character and interpret it.  Continue doing this until
312     * we get a recognized value or sequence. */
313    while ((kbinput = parse_kbinput(win, meta_key, func_key)) == ERR);
314
315    /* If we read from the edit window, blank the statusbar if we need
316     * to. */
317    if (win == edit)
318	check_statusblank();
319
320    return kbinput;
321}
322
323/* Translate ASCII characters, extended keypad values, and escape
324 * sequences into their corresponding key values.  Set meta_key to TRUE
325 * when we get a meta key sequence, and set func_key to TRUE when we get
326 * a function key.  Assume nodelay(win) is FALSE. */
327int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key)
328{
329    static int escapes = 0, byte_digits = 0;
330    int *kbinput, retval = ERR;
331
332    *meta_key = FALSE;
333    *func_key = FALSE;
334
335    /* Read in a character. */
336    while ((kbinput = get_input(win, 1)) == NULL);
337
338    switch (*kbinput) {
339	case ERR:
340	    break;
341	case NANO_CONTROL_3:
342	    /* Increment the escape counter. */
343	    escapes++;
344	    switch (escapes) {
345		case 1:
346		    /* One escape: wait for more input. */
347		case 2:
348		    /* Two escapes: wait for more input. */
349		case 3:
350		    /* Three escapes: wait for more input. */
351		    break;
352		default:
353		    /* More than three escapes: limit the escape counter
354		     * to no more than two, and wait for more input. */
355		    escapes %= 3;
356	    }
357	    break;
358	default:
359	    switch (escapes) {
360		case 0:
361		    /* One non-escape: normal input mode.  Save the
362		     * non-escape character as the result. */
363		    retval = *kbinput;
364		    break;
365		case 1:
366		    /* Reset the escape counter. */
367		    escapes = 0;
368		    if (get_key_buffer_len() == 0) {
369			/* One escape followed by a non-escape, and
370			 * there aren't any other keystrokes waiting:
371			 * meta key sequence mode.  Set meta_key to
372			 * TRUE, and save the lowercase version of the
373			 * non-escape character as the result. */
374			*meta_key = TRUE;
375			retval = tolower(*kbinput);
376		    } else
377			/* One escape followed by a non-escape, and
378			 * there are other keystrokes waiting: escape
379			 * sequence mode.  Interpret the escape
380			 * sequence. */
381			retval = parse_escape_seq_kbinput(win,
382				*kbinput);
383		    break;
384		case 2:
385		    if (get_key_buffer_len() == 0) {
386			if (('0' <= *kbinput && *kbinput <= '2' &&
387				byte_digits == 0) || ('0' <= *kbinput &&
388				*kbinput <= '9' && byte_digits > 0)) {
389			    /* Two escapes followed by one or more
390			     * decimal digits, and there aren't any
391			     * other keystrokes waiting: byte sequence
392			     * mode.  If the byte sequence's range is
393			     * limited to 2XX (the first digit is in the
394			     * '0' to '2' range and it's the first
395			     * digit, or it's in the '0' to '9' range
396			     * and it's not the first digit), increment
397			     * the byte sequence counter and interpret
398			     * the digit.  If the byte sequence's range
399			     * is not limited to 2XX, fall through. */
400			    int byte;
401
402			    byte_digits++;
403			    byte = get_byte_kbinput(*kbinput);
404
405			    if (byte != ERR) {
406				char *byte_mb;
407				int byte_mb_len, *seq, i;
408
409				/* If we've read in a complete byte
410				 * sequence, reset the escape counter
411				 * and the byte sequence counter, and
412				 * put back the corresponding byte
413				 * value. */
414				escapes = 0;
415				byte_digits = 0;
416
417				/* Put back the multibyte equivalent of
418				 * the byte value. */
419				byte_mb = make_mbchar((long)byte,
420					&byte_mb_len);
421
422				seq = (int *)nmalloc(byte_mb_len *
423					sizeof(int));
424
425				for (i = 0; i < byte_mb_len; i++)
426				    seq[i] = (unsigned char)byte_mb[i];
427
428				unget_input(seq, byte_mb_len);
429
430				free(byte_mb);
431				free(seq);
432			    }
433			} else {
434			    /* Reset the escape counter. */
435			    escapes = 0;
436			    if (byte_digits == 0)
437				/* Two escapes followed by a non-decimal
438				 * digit or a decimal digit that would
439				 * create a byte sequence greater than
440				 * 2XX, we're not in the middle of a
441				 * byte sequence, and there aren't any
442				 * other keystrokes waiting: control
443				 * character sequence mode.  Interpret
444				 * the control sequence and save the
445				 * corresponding control character as
446				 * the result. */
447				retval = get_control_kbinput(*kbinput);
448			    else {
449				/* If we're in the middle of a byte
450				 * sequence, reset the byte sequence
451				 * counter and save the character we got
452				 * as the result. */
453				byte_digits = 0;
454				retval = *kbinput;
455			    }
456			}
457		    } else {
458			/* Two escapes followed by a non-escape, and
459			 * there are other keystrokes waiting: combined
460			 * meta and escape sequence mode.  Reset the
461			 * escape counter, set meta_key to TRUE, and
462			 * interpret the escape sequence. */
463			escapes = 0;
464			*meta_key = TRUE;
465			retval = parse_escape_seq_kbinput(win,
466				*kbinput);
467		    }
468		    break;
469		case 3:
470		    /* Reset the escape counter. */
471		    escapes = 0;
472		    if (get_key_buffer_len() == 0)
473			/* Three escapes followed by a non-escape, and
474			 * there aren't any other keystrokes waiting:
475			 * normal input mode.  Save the non-escape
476			 * character as the result. */
477			retval = *kbinput;
478		    else
479			/* Three escapes followed by a non-escape, and
480			 * there are other keystrokes waiting: combined
481			 * control character and escape sequence mode.
482			 * Interpret the escape sequence, and interpret
483			 * the result as a control sequence. */
484			retval = get_control_kbinput(
485				parse_escape_seq_kbinput(win,
486				*kbinput));
487		    break;
488	    }
489    }
490
491    if (retval != ERR) {
492	switch (retval) {
493	    case NANO_CONTROL_8:
494		retval = ISSET(REBIND_DELETE) ? NANO_DELETE_KEY :
495			NANO_BACKSPACE_KEY;
496		break;
497	    case KEY_DOWN:
498		retval = NANO_NEXTLINE_KEY;
499		break;
500	    case KEY_UP:
501		retval = NANO_PREVLINE_KEY;
502		break;
503	    case KEY_LEFT:
504		retval = NANO_BACK_KEY;
505		break;
506	    case KEY_RIGHT:
507		retval = NANO_FORWARD_KEY;
508		break;
509#ifdef KEY_HOME
510	    /* HP-UX 10-11 doesn't support KEY_HOME. */
511	    case KEY_HOME:
512		retval = NANO_HOME_KEY;
513		break;
514#endif
515	    case KEY_BACKSPACE:
516		retval = NANO_BACKSPACE_KEY;
517		break;
518	    case KEY_DC:
519		retval = ISSET(REBIND_DELETE) ? NANO_BACKSPACE_KEY :
520			NANO_DELETE_KEY;
521		break;
522	    case KEY_IC:
523		retval = NANO_INSERTFILE_KEY;
524		break;
525	    case KEY_NPAGE:
526		retval = NANO_NEXTPAGE_KEY;
527		break;
528	    case KEY_PPAGE:
529		retval = NANO_PREVPAGE_KEY;
530		break;
531	    case KEY_ENTER:
532		retval = NANO_ENTER_KEY;
533		break;
534	    case KEY_A1:	/* Home (7) on numeric keypad with
535				 * NumLock off. */
536		retval = NANO_HOME_KEY;
537		break;
538	    case KEY_A3:	/* PageUp (9) on numeric keypad with
539				 * NumLock off. */
540		retval = NANO_PREVPAGE_KEY;
541		break;
542	    case KEY_B2:	/* Center (5) on numeric keypad with
543				 * NumLock off. */
544		retval = ERR;
545		break;
546	    case KEY_C1:	/* End (1) on numeric keypad with
547				 * NumLock off. */
548		retval = NANO_END_KEY;
549		break;
550	    case KEY_C3:	/* PageDown (4) on numeric keypad with
551				 * NumLock off. */
552		retval = NANO_NEXTPAGE_KEY;
553		break;
554#ifdef KEY_BEG
555	    /* Slang doesn't support KEY_BEG. */
556	    case KEY_BEG:	/* Center (5) on numeric keypad with
557				 * NumLock off. */
558		retval = ERR;
559		break;
560#endif
561#ifdef KEY_CANCEL
562	    /* Slang doesn't support KEY_CANCEL. */
563	    case KEY_CANCEL:
564		retval = NANO_CANCEL_KEY;
565		break;
566#endif
567#ifdef KEY_END
568	    /* HP-UX 10-11 doesn't support KEY_END. */
569	    case KEY_END:
570		retval = NANO_END_KEY;
571		break;
572#endif
573#ifdef KEY_SBEG
574	    /* Slang doesn't support KEY_SBEG. */
575	    case KEY_SBEG:	/* Center (5) on numeric keypad with
576				 * NumLock off. */
577		retval = ERR;
578		break;
579#endif
580#ifdef KEY_SCANCEL
581	    /* Slang doesn't support KEY_SCANCEL. */
582	    case KEY_SCANCEL:
583		retval = NANO_CANCEL_KEY;
584		break;
585#endif
586#ifdef KEY_SDC
587	    /* Slang doesn't support KEY_SDC. */
588	    case KEY_SDC:
589		retval = ISSET(REBIND_DELETE) ? NANO_BACKSPACE_KEY :
590			NANO_DELETE_KEY;
591		break;
592#endif
593#ifdef KEY_SEND
594	    /* HP-UX 10-11 and Slang don't support KEY_SEND. */
595	    case KEY_SEND:
596		retval = NANO_END_KEY;
597		break;
598#endif
599#ifdef KEY_SHOME
600	    /* HP-UX 10-11 and Slang don't support KEY_SHOME. */
601	    case KEY_SHOME:
602		retval = NANO_HOME_KEY;
603		break;
604#endif
605#ifdef KEY_SIC
606	    /* Slang doesn't support KEY_SIC. */
607	    case KEY_SIC:
608		retval = NANO_INSERTFILE_KEY;
609		break;
610#endif
611#ifdef KEY_SDOWN
612	    /* ncurses and Slang don't support KEY_SDOWN. */
613	    case KEY_SDOWN:
614		retval = NANO_NEXTLINE_KEY;
615		break;
616#endif
617#ifdef KEY_SUP
618	    /* ncurses and Slang don't support KEY_SUP. */
619	    case KEY_SUP:
620		retval = NANO_PREVLINE_KEY;
621		break;
622#endif
623#ifdef KEY_SLEFT
624	    /* Slang doesn't support KEY_SLEFT. */
625	    case KEY_SLEFT:
626		retval = NANO_BACK_KEY;
627		break;
628#endif
629#ifdef KEY_SRIGHT
630	    /* Slang doesn't support KEY_SRIGHT. */
631	    case KEY_SRIGHT:
632		retval = NANO_FORWARD_KEY;
633		break;
634#endif
635#ifdef KEY_SSUSPEND
636	    /* Slang doesn't support KEY_SSUSPEND. */
637	    case KEY_SSUSPEND:
638		retval = NANO_SUSPEND_KEY;
639		break;
640#endif
641#ifdef KEY_SUSPEND
642	    /* Slang doesn't support KEY_SUSPEND. */
643	    case KEY_SUSPEND:
644		retval = NANO_SUSPEND_KEY;
645		break;
646#endif
647#ifdef PDCURSES
648	    case KEY_SHIFT_L:
649	    case KEY_SHIFT_R:
650	    case KEY_CONTROL_L:
651	    case KEY_CONTROL_R:
652	    case KEY_ALT_L:
653	    case KEY_ALT_R:
654		retval = ERR;
655		break;
656#endif
657#if !defined(NANO_TINY) && defined(KEY_RESIZE)
658	    /* Since we don't change the default SIGWINCH handler when
659	     * NANO_TINY is defined, KEY_RESIZE is never generated.
660	     * Also, Slang and SunOS 5.7-5.9 don't support
661	     * KEY_RESIZE. */
662	    case KEY_RESIZE:
663		retval = ERR;
664		break;
665#endif
666	}
667
668	/* If our result is an extended keypad value (i.e. a value
669	 * outside of byte range), set func_key to TRUE. */
670	if (retval != ERR)
671	    *func_key = !is_byte(retval);
672    }
673
674#ifdef DEBUG
675    fprintf(stderr, "parse_kbinput(): kbinput = %d, meta_key = %s, func_key = %s, escapes = %d, byte_digits = %d, retval = %d\n", *kbinput, *meta_key ? "TRUE" : "FALSE", *func_key ? "TRUE" : "FALSE", escapes, byte_digits, retval);
676#endif
677
678    free(kbinput);
679
680    /* Return the result. */
681    return retval;
682}
683
684/* Translate escape sequences, most of which correspond to extended
685 * keypad values, into their corresponding key values.  These sequences
686 * are generated when the keypad doesn't support the needed keys.
687 * Assume that Escape has already been read in. */
688int get_escape_seq_kbinput(const int *seq, size_t seq_len)
689{
690    int retval = ERR;
691
692    if (seq_len > 1) {
693	switch (seq[0]) {
694	    case 'O':
695		switch (seq[1]) {
696		    case '1':
697			if (seq_len >= 3) {
698			    switch (seq[2]) {
699				case ';':
700    if (seq_len >= 4) {
701	switch (seq[3]) {
702	    case '2':
703		if (seq_len >= 5) {
704		    switch (seq[4]) {
705			case 'A': /* Esc O 1 ; 2 A == Shift-Up on
706				   * Terminal. */
707			case 'B': /* Esc O 1 ; 2 B == Shift-Down on
708				   * Terminal. */
709			case 'C': /* Esc O 1 ; 2 C == Shift-Right on
710				   * Terminal. */
711			case 'D': /* Esc O 1 ; 2 D == Shift-Left on
712				   * Terminal. */
713			    retval = get_escape_seq_abcd(seq[4]);
714			    break;
715			case 'P': /* Esc O 1 ; 2 P == F13 on
716				   * Terminal. */
717			    retval = KEY_F(13);
718			    break;
719			case 'Q': /* Esc O 1 ; 2 Q == F14 on
720				   * Terminal. */
721			    retval = KEY_F(14);
722			    break;
723			case 'R': /* Esc O 1 ; 2 R == F15 on
724				   * Terminal. */
725			    retval = KEY_F(15);
726			    break;
727			case 'S': /* Esc O 1 ; 2 S == F16 on
728				   * Terminal. */
729			    retval = KEY_F(16);
730			    break;
731		    }
732		}
733		break;
734	    case '5':
735		if (seq_len >= 5) {
736		    switch (seq[4]) {
737			case 'A': /* Esc O 1 ; 5 A == Ctrl-Up on
738				   * Terminal. */
739			case 'B': /* Esc O 1 ; 5 B == Ctrl-Down on
740				   * Terminal. */
741			case 'C': /* Esc O 1 ; 5 C == Ctrl-Right on
742				   * Terminal. */
743			case 'D': /* Esc O 1 ; 5 D == Ctrl-Left on
744				   * Terminal. */
745			    retval = get_escape_seq_abcd(seq[4]);
746			    break;
747		    }
748		}
749		break;
750	}
751    }
752				    break;
753			    }
754			}
755			break;
756		    case '2':
757			if (seq_len >= 3) {
758			    switch (seq[2]) {
759				case 'P': /* Esc O 2 P == F13 on
760					   * xterm. */
761				    retval = KEY_F(13);
762				    break;
763				case 'Q': /* Esc O 2 Q == F14 on
764					   * xterm. */
765				    retval = KEY_F(14);
766				    break;
767				case 'R': /* Esc O 2 R == F15 on
768					   * xterm. */
769				    retval = KEY_F(15);
770				    break;
771				case 'S': /* Esc O 2 S == F16 on
772					   * xterm. */
773				    retval = KEY_F(16);
774				    break;
775			    }
776			}
777			break;
778		    case 'A': /* Esc O A == Up on VT100/VT320/xterm. */
779		    case 'B': /* Esc O B == Down on
780			       * VT100/VT320/xterm. */
781		    case 'C': /* Esc O C == Right on
782			       * VT100/VT320/xterm. */
783		    case 'D': /* Esc O D == Left on
784			       * VT100/VT320/xterm. */
785			retval = get_escape_seq_abcd(seq[1]);
786			break;
787		    case 'E': /* Esc O E == Center (5) on numeric keypad
788			       * with NumLock off on xterm. */
789			retval = KEY_B2;
790			break;
791		    case 'F': /* Esc O F == End on xterm/Terminal. */
792			retval = NANO_END_KEY;
793			break;
794		    case 'H': /* Esc O H == Home on xterm/Terminal. */
795			retval = NANO_HOME_KEY;
796			break;
797		    case 'M': /* Esc O M == Enter on numeric keypad with
798			       * NumLock off on VT100/VT220/VT320/xterm/
799			       * rxvt/Eterm. */
800			retval = NANO_ENTER_KEY;
801			break;
802		    case 'P': /* Esc O P == F1 on VT100/VT220/VT320/Mach
803			       * console. */
804			retval = KEY_F(1);
805			break;
806		    case 'Q': /* Esc O Q == F2 on VT100/VT220/VT320/Mach
807			       * console. */
808			retval = KEY_F(2);
809			break;
810		    case 'R': /* Esc O R == F3 on VT100/VT220/VT320/Mach
811			       * console. */
812			retval = KEY_F(3);
813			break;
814		    case 'S': /* Esc O S == F4 on VT100/VT220/VT320/Mach
815			       * console. */
816			retval = KEY_F(4);
817			break;
818		    case 'T': /* Esc O T == F5 on Mach console. */
819			retval = KEY_F(5);
820			break;
821		    case 'U': /* Esc O U == F6 on Mach console. */
822			retval = KEY_F(6);
823			break;
824		    case 'V': /* Esc O V == F7 on Mach console. */
825			retval = KEY_F(7);
826			break;
827		    case 'W': /* Esc O W == F8 on Mach console. */
828			retval = KEY_F(8);
829			break;
830		    case 'X': /* Esc O X == F9 on Mach console. */
831			retval = KEY_F(9);
832			break;
833		    case 'Y': /* Esc O Y == F10 on Mach console. */
834			retval = KEY_F(10);
835			break;
836		    case 'a': /* Esc O a == Ctrl-Up on rxvt. */
837		    case 'b': /* Esc O b == Ctrl-Down on rxvt. */
838		    case 'c': /* Esc O c == Ctrl-Right on rxvt. */
839		    case 'd': /* Esc O d == Ctrl-Left on rxvt. */
840			retval = get_escape_seq_abcd(seq[1]);
841			break;
842		    case 'j': /* Esc O j == '*' on numeric keypad with
843			       * NumLock off on VT100/VT220/VT320/xterm/
844			       * rxvt/Eterm/Terminal. */
845			retval = '*';
846			break;
847		    case 'k': /* Esc O k == '+' on numeric keypad with
848			       * NumLock off on VT100/VT220/VT320/xterm/
849			       * rxvt/Eterm/Terminal. */
850			retval = '+';
851			break;
852		    case 'l': /* Esc O l == ',' on numeric keypad with
853			       * NumLock off on VT100/VT220/VT320/xterm/
854			       * rxvt/Eterm/Terminal. */
855			retval = ',';
856			break;
857		    case 'm': /* Esc O m == '-' on numeric keypad with
858			       * NumLock off on VT100/VT220/VT320/xterm/
859			       * rxvt/Eterm/Terminal. */
860			retval = '-';
861			break;
862		    case 'n': /* Esc O n == Delete (.) on numeric keypad
863			       * with NumLock off on VT100/VT220/VT320/
864			       * xterm/rxvt/Eterm/Terminal. */
865			retval = NANO_DELETE_KEY;
866			break;
867		    case 'o': /* Esc O o == '/' on numeric keypad with
868			       * NumLock off on VT100/VT220/VT320/xterm/
869			       * rxvt/Eterm/Terminal. */
870			retval = '/';
871			break;
872		    case 'p': /* Esc O p == Insert (0) on numeric keypad
873			       * with NumLock off on VT100/VT220/VT320/
874			       * rxvt/Eterm/Terminal. */
875			retval = NANO_INSERTFILE_KEY;
876			break;
877		    case 'q': /* Esc O q == End (1) on numeric keypad
878			       * with NumLock off on VT100/VT220/VT320/
879			       * rxvt/Eterm/Terminal. */
880			retval = NANO_END_KEY;
881			break;
882		    case 'r': /* Esc O r == Down (2) on numeric keypad
883			       * with NumLock off on VT100/VT220/VT320/
884			       * rxvt/Eterm/Terminal. */
885			retval = NANO_NEXTLINE_KEY;
886			break;
887		    case 's': /* Esc O s == PageDown (3) on numeric
888			       * keypad with NumLock off on VT100/VT220/
889			       * VT320/rxvt/Eterm/Terminal. */
890			retval = NANO_NEXTPAGE_KEY;
891			break;
892		    case 't': /* Esc O t == Left (4) on numeric keypad
893			       * with NumLock off on VT100/VT220/VT320/
894			       * rxvt/Eterm/Terminal. */
895			retval = NANO_BACK_KEY;
896			break;
897		    case 'u': /* Esc O u == Center (5) on numeric keypad
898			       * with NumLock off on VT100/VT220/VT320/
899			       * rxvt/Eterm. */
900			retval = KEY_B2;
901			break;
902		    case 'v': /* Esc O v == Right (6) on numeric keypad
903			       * with NumLock off on VT100/VT220/VT320/
904			       * rxvt/Eterm/Terminal. */
905			retval = NANO_FORWARD_KEY;
906			break;
907		    case 'w': /* Esc O w == Home (7) on numeric keypad
908			       * with NumLock off on VT100/VT220/VT320/
909			       * rxvt/Eterm/Terminal. */
910			retval = NANO_HOME_KEY;
911			break;
912		    case 'x': /* Esc O x == Up (8) on numeric keypad
913			       * with NumLock off on VT100/VT220/VT320/
914			       * rxvt/Eterm/Terminal. */
915			retval = NANO_PREVLINE_KEY;
916			break;
917		    case 'y': /* Esc O y == PageUp (9) on numeric keypad
918			       * with NumLock off on VT100/VT220/VT320/
919			       * rxvt/Eterm/Terminal. */
920			retval = NANO_PREVPAGE_KEY;
921			break;
922		}
923		break;
924	    case 'o':
925		switch (seq[1]) {
926		    case 'a': /* Esc o a == Ctrl-Up on Eterm. */
927		    case 'b': /* Esc o b == Ctrl-Down on Eterm. */
928		    case 'c': /* Esc o c == Ctrl-Right on Eterm. */
929		    case 'd': /* Esc o d == Ctrl-Left on Eterm. */
930			retval = get_escape_seq_abcd(seq[1]);
931			break;
932		}
933		break;
934	    case '[':
935		switch (seq[1]) {
936		    case '1':
937			if (seq_len >= 3) {
938			    switch (seq[2]) {
939				case '1': /* Esc [ 1 1 ~ == F1 on rxvt/
940					   * Eterm. */
941				    retval = KEY_F(1);
942				    break;
943				case '2': /* Esc [ 1 2 ~ == F2 on rxvt/
944					   * Eterm. */
945				    retval = KEY_F(2);
946				    break;
947				case '3': /* Esc [ 1 3 ~ == F3 on rxvt/
948					   * Eterm. */
949				    retval = KEY_F(3);
950				    break;
951				case '4': /* Esc [ 1 4 ~ == F4 on rxvt/
952					   * Eterm. */
953				    retval = KEY_F(4);
954				    break;
955				case '5': /* Esc [ 1 5 ~ == F5 on xterm/
956					   * rxvt/Eterm. */
957				    retval = KEY_F(5);
958				    break;
959				case '7': /* Esc [ 1 7 ~ == F6 on
960					   * VT220/VT320/Linux console/
961					   * xterm/rxvt/Eterm. */
962				    retval = KEY_F(6);
963				    break;
964				case '8': /* Esc [ 1 8 ~ == F7 on
965					   * VT220/VT320/Linux console/
966					   * xterm/rxvt/Eterm. */
967				    retval = KEY_F(7);
968				    break;
969				case '9': /* Esc [ 1 9 ~ == F8 on
970					   * VT220/VT320/Linux console/
971					   * xterm/rxvt/Eterm. */
972				    retval = KEY_F(8);
973				    break;
974				case ';':
975    if (seq_len >= 4) {
976	switch (seq[3]) {
977	    case '2':
978		if (seq_len >= 5) {
979		    switch (seq[4]) {
980			case 'A': /* Esc [ 1 ; 2 A == Shift-Up on
981				   * xterm. */
982			case 'B': /* Esc [ 1 ; 2 B == Shift-Down on
983				   * xterm. */
984			case 'C': /* Esc [ 1 ; 2 C == Shift-Right on
985				   * xterm. */
986			case 'D': /* Esc [ 1 ; 2 D == Shift-Left on
987				   * xterm. */
988			    retval = get_escape_seq_abcd(seq[4]);
989			    break;
990		    }
991		}
992		break;
993	    case '5':
994		if (seq_len >= 5) {
995		    switch (seq[4]) {
996			case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on
997				   * xterm. */
998			case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on
999				   * xterm. */
1000			case 'C': /* Esc [ 1 ; 5 C == Ctrl-Right on
1001				   * xterm. */
1002			case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on
1003				   * xterm. */
1004			    retval = get_escape_seq_abcd(seq[4]);
1005			    break;
1006		    }
1007		}
1008		break;
1009	}
1010    }
1011				    break;
1012				default: /* Esc [ 1 ~ == Home on
1013					  * VT320/Linux console. */
1014				    retval = NANO_HOME_KEY;
1015				    break;
1016			    }
1017			}
1018			break;
1019		    case '2':
1020			if (seq_len >= 3) {
1021			    switch (seq[2]) {
1022				case '0': /* Esc [ 2 0 ~ == F9 on
1023					   * VT220/VT320/Linux console/
1024					   * xterm/rxvt/Eterm. */
1025				    retval = KEY_F(9);
1026				    break;
1027				case '1': /* Esc [ 2 1 ~ == F10 on
1028					   * VT220/VT320/Linux console/
1029					   * xterm/rxvt/Eterm. */
1030				    retval = KEY_F(10);
1031				    break;
1032				case '3': /* Esc [ 2 3 ~ == F11 on
1033					   * VT220/VT320/Linux console/
1034					   * xterm/rxvt/Eterm. */
1035				    retval = KEY_F(11);
1036				    break;
1037				case '4': /* Esc [ 2 4 ~ == F12 on
1038					   * VT220/VT320/Linux console/
1039					   * xterm/rxvt/Eterm. */
1040				    retval = KEY_F(12);
1041				    break;
1042				case '5': /* Esc [ 2 5 ~ == F13 on
1043					   * VT220/VT320/Linux console/
1044					   * rxvt/Eterm. */
1045				    retval = KEY_F(13);
1046				    break;
1047				case '6': /* Esc [ 2 6 ~ == F14 on
1048					   * VT220/VT320/Linux console/
1049					   * rxvt/Eterm. */
1050				    retval = KEY_F(14);
1051				    break;
1052				case '8': /* Esc [ 2 8 ~ == F15 on
1053					   * VT220/VT320/Linux console/
1054					   * rxvt/Eterm. */
1055				    retval = KEY_F(15);
1056				    break;
1057				case '9': /* Esc [ 2 9 ~ == F16 on
1058					   * VT220/VT320/Linux console/
1059					   * rxvt/Eterm. */
1060				    retval = KEY_F(16);
1061				    break;
1062				default: /* Esc [ 2 ~ == Insert on
1063					  * VT220/VT320/Linux console/
1064					  * xterm/Terminal. */
1065				    retval = NANO_INSERTFILE_KEY;
1066				    break;
1067			    }
1068			}
1069			break;
1070		    case '3': /* Esc [ 3 ~ == Delete on VT220/VT320/
1071			       * Linux console/xterm/Terminal. */
1072			retval = NANO_DELETE_KEY;
1073			break;
1074		    case '4': /* Esc [ 4 ~ == End on VT220/VT320/Linux
1075			       * console/xterm. */
1076			retval = NANO_END_KEY;
1077			break;
1078		    case '5': /* Esc [ 5 ~ == PageUp on VT220/VT320/
1079			       * Linux console/xterm/Terminal;
1080			       * Esc [ 5 ^ == PageUp on Eterm. */
1081			retval = NANO_PREVPAGE_KEY;
1082			break;
1083		    case '6': /* Esc [ 6 ~ == PageDown on VT220/VT320/
1084			       * Linux console/xterm/Terminal;
1085			        * Esc [ 6 ^ == PageDown on Eterm. */
1086			retval = NANO_NEXTPAGE_KEY;
1087			break;
1088		    case '7': /* Esc [ 7 ~ == Home on rxvt. */
1089			retval = NANO_HOME_KEY;
1090			break;
1091		    case '8': /* Esc [ 8 ~ == End on rxvt. */
1092			retval = NANO_END_KEY;
1093			break;
1094		    case '9': /* Esc [ 9 == Delete on Mach console. */
1095			retval = NANO_DELETE_KEY;
1096			break;
1097		    case '@': /* Esc [ @ == Insert on Mach console. */
1098			retval = NANO_INSERTFILE_KEY;
1099			break;
1100		    case 'A': /* Esc [ A == Up on ANSI/VT220/Linux
1101			       * console/FreeBSD console/Mach console/
1102			       * rxvt/Eterm/Terminal. */
1103		    case 'B': /* Esc [ B == Down on ANSI/VT220/Linux
1104			       * console/FreeBSD console/Mach console/
1105			       * rxvt/Eterm/Terminal. */
1106		    case 'C': /* Esc [ C == Right on ANSI/VT220/Linux
1107			       * console/FreeBSD console/Mach console/
1108			       * rxvt/Eterm/Terminal. */
1109		    case 'D': /* Esc [ D == Left on ANSI/VT220/Linux
1110			       * console/FreeBSD console/Mach console/
1111			       * rxvt/Eterm/Terminal. */
1112			retval = get_escape_seq_abcd(seq[1]);
1113			break;
1114		    case 'E': /* Esc [ E == Center (5) on numeric keypad
1115			       * with NumLock off on FreeBSD console/
1116			       * Terminal. */
1117			retval = KEY_B2;
1118			break;
1119		    case 'F': /* Esc [ F == End on FreeBSD
1120			       * console/Eterm. */
1121			retval = NANO_END_KEY;
1122			break;
1123		    case 'G': /* Esc [ G == PageDown on FreeBSD
1124			       * console. */
1125			retval = NANO_NEXTPAGE_KEY;
1126			break;
1127		    case 'H': /* Esc [ H == Home on ANSI/VT220/FreeBSD
1128			       * console/Mach console/Eterm. */
1129			retval = NANO_HOME_KEY;
1130			break;
1131		    case 'I': /* Esc [ I == PageUp on FreeBSD
1132			       * console. */
1133			retval = NANO_PREVPAGE_KEY;
1134			break;
1135		    case 'L': /* Esc [ L == Insert on ANSI/FreeBSD
1136			       * console. */
1137			retval = NANO_INSERTFILE_KEY;
1138			break;
1139		    case 'M': /* Esc [ M == F1 on FreeBSD console. */
1140			retval = KEY_F(1);
1141			break;
1142		    case 'N': /* Esc [ N == F2 on FreeBSD console. */
1143			retval = KEY_F(2);
1144			break;
1145		    case 'O':
1146			if (seq_len >= 3) {
1147			    switch (seq[2]) {
1148				case 'P': /* Esc [ O P == F1 on
1149					   * xterm. */
1150				    retval = KEY_F(1);
1151				    break;
1152				case 'Q': /* Esc [ O Q == F2 on
1153					   * xterm. */
1154				    retval = KEY_F(2);
1155				    break;
1156				case 'R': /* Esc [ O R == F3 on
1157					   * xterm. */
1158				    retval = KEY_F(3);
1159				    break;
1160				case 'S': /* Esc [ O S == F4 on
1161					   * xterm. */
1162				    retval = KEY_F(4);
1163				    break;
1164			    }
1165			} else
1166			    /* Esc [ O == F3 on FreeBSD console. */
1167			    retval = KEY_F(3);
1168			break;
1169		    case 'P': /* Esc [ P == F4 on FreeBSD console. */
1170			retval = KEY_F(4);
1171			break;
1172		    case 'Q': /* Esc [ Q == F5 on FreeBSD console. */
1173			retval = KEY_F(5);
1174			break;
1175		    case 'R': /* Esc [ R == F6 on FreeBSD console. */
1176			retval = KEY_F(6);
1177			break;
1178		    case 'S': /* Esc [ S == F7 on FreeBSD console. */
1179			retval = KEY_F(7);
1180			break;
1181		    case 'T': /* Esc [ T == F8 on FreeBSD console. */
1182			retval = KEY_F(8);
1183			break;
1184		    case 'U': /* Esc [ U == PageDown on Mach console. */
1185			retval = NANO_NEXTPAGE_KEY;
1186			break;
1187		    case 'V': /* Esc [ V == PageUp on Mach console. */
1188			retval = NANO_PREVPAGE_KEY;
1189			break;
1190		    case 'W': /* Esc [ W == F11 on FreeBSD console. */
1191			retval = KEY_F(11);
1192			break;
1193		    case 'X': /* Esc [ X == F12 on FreeBSD console. */
1194			retval = KEY_F(12);
1195			break;
1196		    case 'Y': /* Esc [ Y == End on Mach console. */
1197			retval = NANO_END_KEY;
1198			break;
1199		    case 'Z': /* Esc [ Z == F14 on FreeBSD console. */
1200			retval = KEY_F(14);
1201			break;
1202		    case 'a': /* Esc [ a == Shift-Up on rxvt/Eterm. */
1203		    case 'b': /* Esc [ b == Shift-Down on rxvt/Eterm. */
1204		    case 'c': /* Esc [ c == Shift-Right on rxvt/
1205			       * Eterm. */
1206		    case 'd': /* Esc [ d == Shift-Left on rxvt/Eterm. */
1207			retval = get_escape_seq_abcd(seq[1]);
1208			break;
1209		    case '[':
1210			if (seq_len >= 3) {
1211			    switch (seq[2]) {
1212				case 'A': /* Esc [ [ A == F1 on Linux
1213					   * console. */
1214				    retval = KEY_F(1);
1215				    break;
1216				case 'B': /* Esc [ [ B == F2 on Linux
1217					   * console. */
1218				    retval = KEY_F(2);
1219				    break;
1220				case 'C': /* Esc [ [ C == F3 on Linux
1221					   * console. */
1222				    retval = KEY_F(3);
1223				    break;
1224				case 'D': /* Esc [ [ D == F4 on Linux
1225					   * console. */
1226				    retval = KEY_F(4);
1227				    break;
1228				case 'E': /* Esc [ [ E == F5 on Linux
1229					   * console. */
1230				    retval = KEY_F(5);
1231				    break;
1232			    }
1233			}
1234			break;
1235		}
1236		break;
1237	}
1238    }
1239
1240#ifdef DEBUG
1241    fprintf(stderr, "get_escape_seq_kbinput(): retval = %d\n", retval);
1242#endif
1243
1244    return retval;
1245}
1246
1247/* Return the equivalent arrow key value for the case-insensitive
1248 * letters A (up), B (down), C (right), and D (left).  These are common
1249 * to many escape sequences. */
1250int get_escape_seq_abcd(int kbinput)
1251{
1252    switch (tolower(kbinput)) {
1253	case 'a':
1254	    return NANO_PREVLINE_KEY;
1255	case 'b':
1256	    return NANO_NEXTLINE_KEY;
1257	case 'c':
1258	    return NANO_FORWARD_KEY;
1259	case 'd':
1260	    return NANO_BACK_KEY;
1261	default:
1262	    return ERR;
1263    }
1264}
1265
1266/* Interpret the escape sequence in the keystroke buffer, the first
1267 * character of which is kbinput.  Assume that the keystroke buffer
1268 * isn't empty, and that the initial escape has already been read in. */
1269int parse_escape_seq_kbinput(WINDOW *win, int kbinput)
1270{
1271    int retval, *seq;
1272    size_t seq_len;
1273
1274    /* Put back the non-escape character, get the complete escape
1275     * sequence, translate the sequence into its corresponding key
1276     * value, and save that as the result. */
1277    unget_input(&kbinput, 1);
1278    seq_len = get_key_buffer_len();
1279    seq = get_input(NULL, seq_len);
1280    retval = get_escape_seq_kbinput(seq, seq_len);
1281
1282    free(seq);
1283
1284    /* If we got an unrecognized escape sequence, throw it out. */
1285    if (retval == ERR) {
1286	if (win == edit) {
1287	    statusbar(_("Unknown Command"));
1288	    beep();
1289	}
1290    }
1291
1292#ifdef DEBUG
1293    fprintf(stderr, "parse_escape_seq_kbinput(): kbinput = %d, seq_len = %lu, retval = %d\n", kbinput, (unsigned long)seq_len, retval);
1294#endif
1295
1296    return retval;
1297}
1298
1299/* Translate a byte sequence: turn a three-digit decimal number (from
1300 * 000 to 255) into its corresponding byte value. */
1301int get_byte_kbinput(int kbinput)
1302{
1303    static int byte_digits = 0, byte = 0;
1304    int retval = ERR;
1305
1306    /* Increment the byte digit counter. */
1307    byte_digits++;
1308
1309    switch (byte_digits) {
1310	case 1:
1311	    /* First digit: This must be from zero to two.  Put it in
1312	     * the 100's position of the byte sequence holder. */
1313	    if ('0' <= kbinput && kbinput <= '2')
1314		byte = (kbinput - '0') * 100;
1315	    else
1316		/* This isn't the start of a byte sequence.  Return this
1317		 * character as the result. */
1318		retval = kbinput;
1319	    break;
1320	case 2:
1321	    /* Second digit: This must be from zero to five if the first
1322	     * was two, and may be any decimal value if the first was
1323	     * zero or one.  Put it in the 10's position of the byte
1324	     * sequence holder. */
1325	    if (('0' <= kbinput && kbinput <= '5') || (byte < 200 &&
1326		'6' <= kbinput && kbinput <= '9'))
1327		byte += (kbinput - '0') * 10;
1328	    else
1329		/* This isn't the second digit of a byte sequence.
1330		 * Return this character as the result. */
1331		retval = kbinput;
1332	    break;
1333	case 3:
1334	    /* Third digit: This must be from zero to five if the first
1335	     * was two and the second was between zero and five, and may
1336	     * be any decimal value if the first was zero or one and the
1337	     * second was between six and nine.  Put it in the 1's
1338	     * position of the byte sequence holder. */
1339	    if (('0' <= kbinput && kbinput <= '5') || (byte < 250 &&
1340		'6' <= kbinput && kbinput <= '9')) {
1341		byte += kbinput - '0';
1342		/* If this character is a valid decimal value, then the
1343		 * byte sequence is complete. */
1344		retval = byte;
1345	    } else
1346		/* This isn't the third digit of a byte sequence.
1347		 * Return this character as the result. */
1348		retval = kbinput;
1349	    break;
1350	default:
1351	    /* If there are more than three digits, return this
1352	     * character as the result.  (Maybe we should produce an
1353	     * error instead?) */
1354	    retval = kbinput;
1355	    break;
1356    }
1357
1358    /* If we have a result, reset the byte digit counter and the byte
1359     * sequence holder. */
1360    if (retval != ERR) {
1361	byte_digits = 0;
1362	byte = 0;
1363    }
1364
1365#ifdef DEBUG
1366    fprintf(stderr, "get_byte_kbinput(): kbinput = %d, byte_digits = %d, byte = %d, retval = %d\n", kbinput, byte_digits, byte, retval);
1367#endif
1368
1369    return retval;
1370}
1371
1372#ifdef ENABLE_UTF8
1373/* If the character in kbinput is a valid hexadecimal digit, multiply it
1374 * by factor and add the result to uni. */
1375long add_unicode_digit(int kbinput, long factor, long *uni)
1376{
1377    long retval = ERR;
1378
1379    if ('0' <= kbinput && kbinput <= '9')
1380	*uni += (kbinput - '0') * factor;
1381    else if ('a' <= tolower(kbinput) && tolower(kbinput) <= 'f')
1382	*uni += (tolower(kbinput) - 'a' + 10) * factor;
1383    else
1384	/* If this character isn't a valid hexadecimal value, save it as
1385	 * the result. */
1386	retval = kbinput;
1387
1388    return retval;
1389}
1390
1391/* Translate a Unicode sequence: turn a six-digit hexadecimal number
1392 * (from 000000 to 10FFFF, case-insensitive) into its corresponding
1393 * multibyte value. */
1394long get_unicode_kbinput(int kbinput)
1395{
1396    static int uni_digits = 0;
1397    static long uni = 0;
1398    long retval = ERR;
1399
1400    /* Increment the Unicode digit counter. */
1401    uni_digits++;
1402
1403    switch (uni_digits) {
1404	case 1:
1405	    /* First digit: This must be zero or one.  Put it in the
1406	     * 0x100000's position of the Unicode sequence holder. */
1407	    if ('0' <= kbinput && kbinput <= '1')
1408		uni = (kbinput - '0') * 0x100000;
1409	    else
1410		/* This isn't the first digit of a Unicode sequence.
1411		 * Return this character as the result. */
1412		retval = kbinput;
1413	    break;
1414	case 2:
1415	    /* Second digit: This must be zero if the first was one, and
1416	     * may be any hexadecimal value if the first was zero.  Put
1417	     * it in the 0x10000's position of the Unicode sequence
1418	     * holder. */
1419	    if (uni == 0 || '0' == kbinput)
1420		retval = add_unicode_digit(kbinput, 0x10000, &uni);
1421	    else
1422		/* This isn't the second digit of a Unicode sequence.
1423		 * Return this character as the result. */
1424		retval = kbinput;
1425	    break;
1426	case 3:
1427	    /* Third digit: This may be any hexadecimal value.  Put it
1428	     * in the 0x1000's position of the Unicode sequence
1429	     * holder. */
1430	    retval = add_unicode_digit(kbinput, 0x1000, &uni);
1431	    break;
1432	case 4:
1433	    /* Fourth digit: This may be any hexadecimal value.  Put it
1434	     * in the 0x100's position of the Unicode sequence
1435	     * holder. */
1436	    retval = add_unicode_digit(kbinput, 0x100, &uni);
1437	    break;
1438	case 5:
1439	    /* Fifth digit: This may be any hexadecimal value.  Put it
1440	     * in the 0x10's position of the Unicode sequence holder. */
1441	    retval = add_unicode_digit(kbinput, 0x10, &uni);
1442	    break;
1443	case 6:
1444	    /* Sixth digit: This may be any hexadecimal value.  Put it
1445	     * in the 0x1's position of the Unicode sequence holder. */
1446	    retval = add_unicode_digit(kbinput, 0x1, &uni);
1447	    /* If this character is a valid hexadecimal value, then the
1448	     * Unicode sequence is complete. */
1449	    if (retval == ERR)
1450		retval = uni;
1451	    break;
1452	default:
1453	    /* If there are more than six digits, return this character
1454	     * as the result.  (Maybe we should produce an error
1455	     * instead?) */
1456	    retval = kbinput;
1457	    break;
1458    }
1459
1460    /* If we have a result, reset the Unicode digit counter and the
1461     * Unicode sequence holder. */
1462    if (retval != ERR) {
1463	uni_digits = 0;
1464	uni = 0;
1465    }
1466
1467#ifdef DEBUG
1468    fprintf(stderr, "get_unicode_kbinput(): kbinput = %d, uni_digits = %d, uni = %ld, retval = %ld\n", kbinput, uni_digits, uni, retval);
1469#endif
1470
1471    return retval;
1472}
1473#endif /* ENABLE_UTF8 */
1474
1475/* Translate a control character sequence: turn an ASCII non-control
1476 * character into its corresponding control character. */
1477int get_control_kbinput(int kbinput)
1478{
1479    int retval;
1480
1481     /* Ctrl-Space (Ctrl-2, Ctrl-@, Ctrl-`) */
1482    if (kbinput == ' ' || kbinput == '2')
1483	retval = NANO_CONTROL_SPACE;
1484    /* Ctrl-/ (Ctrl-7, Ctrl-_) */
1485    else if (kbinput == '/')
1486	retval = NANO_CONTROL_7;
1487    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-/, Ctrl-_) */
1488    else if ('3' <= kbinput && kbinput <= '7')
1489	retval = kbinput - 24;
1490    /* Ctrl-8 (Ctrl-?) */
1491    else if (kbinput == '8' || kbinput == '?')
1492	retval = NANO_CONTROL_8;
1493    /* Ctrl-@ (Ctrl-Space, Ctrl-2, Ctrl-`) to Ctrl-_ (Ctrl-/, Ctrl-7) */
1494    else if ('@' <= kbinput && kbinput <= '_')
1495	retval = kbinput - '@';
1496    /* Ctrl-` (Ctrl-2, Ctrl-Space, Ctrl-@) to Ctrl-~ (Ctrl-6, Ctrl-^) */
1497    else if ('`' <= kbinput && kbinput <= '~')
1498	retval = kbinput - '`';
1499    else
1500	retval = kbinput;
1501
1502#ifdef DEBUG
1503    fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1504#endif
1505
1506    return retval;
1507}
1508
1509/* Put the output-formatted characters in output back into the keystroke
1510 * buffer, so that they can be parsed and displayed as output again. */
1511void unparse_kbinput(char *output, size_t output_len)
1512{
1513    int *input;
1514    size_t i;
1515
1516    if (output_len == 0)
1517	return;
1518
1519    input = (int *)nmalloc(output_len * sizeof(int));
1520
1521    for (i = 0; i < output_len; i++)
1522	input[i] = (int)output[i];
1523
1524    unget_input(input, output_len);
1525
1526    free(input);
1527}
1528
1529/* Read in a stream of characters verbatim, and return the length of the
1530 * string in kbinput_len.  Assume nodelay(win) is FALSE. */
1531int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1532{
1533    int *retval;
1534
1535    /* Turn off flow control characters if necessary so that we can type
1536     * them in verbatim, and turn the keypad off if necessary so that we
1537     * don't get extended keypad values. */
1538    if (ISSET(PRESERVE))
1539	disable_flow_control();
1540    if (!ISSET(REBIND_KEYPAD))
1541	keypad(win, FALSE);
1542
1543    /* Read in a stream of characters and interpret it if possible. */
1544    retval = parse_verbatim_kbinput(win, kbinput_len);
1545
1546    /* Turn flow control characters back on if necessary and turn the
1547     * keypad back on if necessary now that we're done. */
1548    if (ISSET(PRESERVE))
1549	enable_flow_control();
1550    if (!ISSET(REBIND_KEYPAD))
1551	keypad(win, TRUE);
1552
1553    return retval;
1554}
1555
1556/* Read in a stream of all available characters, and return the length
1557 * of the string in kbinput_len.  Translate the first few characters of
1558 * the input into the corresponding multibyte value if possible.  After
1559 * that, leave the input as-is. */
1560int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len)
1561{
1562    int *kbinput, *retval;
1563
1564    /* Read in the first keystroke. */
1565    while ((kbinput = get_input(win, 1)) == NULL);
1566
1567#ifdef ENABLE_UTF8
1568    if (using_utf8()) {
1569	/* Check whether the first keystroke is a valid hexadecimal
1570	 * digit. */
1571	long uni = get_unicode_kbinput(*kbinput);
1572
1573	/* If the first keystroke isn't a valid hexadecimal digit, put
1574	 * back the first keystroke. */
1575	if (uni != ERR)
1576	    unget_input(kbinput, 1);
1577
1578	/* Otherwise, read in keystrokes until we have a complete
1579	 * Unicode sequence, and put back the corresponding Unicode
1580	 * value. */
1581	else {
1582	    char *uni_mb;
1583	    int uni_mb_len, *seq, i;
1584
1585	    if (win == edit)
1586		/* TRANSLATORS: This is displayed during the input of a
1587		 * six-digit hexadecimal Unicode character code. */
1588		statusbar(_("Unicode Input"));
1589
1590	    while (uni == ERR) {
1591		while ((kbinput = get_input(win, 1)) == NULL);
1592
1593		uni = get_unicode_kbinput(*kbinput);
1594	    }
1595
1596	    /* Put back the multibyte equivalent of the Unicode
1597	     * value. */
1598	    uni_mb = make_mbchar(uni, &uni_mb_len);
1599
1600	    seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1601
1602	    for (i = 0; i < uni_mb_len; i++)
1603		seq[i] = (unsigned char)uni_mb[i];
1604
1605	    unget_input(seq, uni_mb_len);
1606
1607	    free(seq);
1608	    free(uni_mb);
1609	}
1610    } else
1611#endif /* ENABLE_UTF8 */
1612
1613	/* Put back the first keystroke. */
1614	unget_input(kbinput, 1);
1615
1616    free(kbinput);
1617
1618    /* Get the complete sequence, and save the characters in it as the
1619     * result. */
1620    *kbinput_len = get_key_buffer_len();
1621    retval = get_input(NULL, *kbinput_len);
1622
1623    return retval;
1624}
1625
1626#ifndef DISABLE_MOUSE
1627/* Check for a mouse event, and if one's taken place, save the
1628 * coordinates where it took place in mouse_x and mouse_y.  After that,
1629 * assuming allow_shortcuts is FALSE, if the shortcut list on the
1630 * bottom two lines of the screen is visible and the mouse event took
1631 * place on it, figure out which shortcut was clicked and put back the
1632 * equivalent keystroke(s).  Return FALSE if no keystrokes were
1633 * put back, or TRUE if at least one was.  Assume that KEY_MOUSE has
1634 * already been read in. */
1635bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
1636{
1637    MEVENT mevent;
1638
1639    *mouse_x = -1;
1640    *mouse_y = -1;
1641
1642    /* First, get the actual mouse event. */
1643    if (getmouse(&mevent) == ERR)
1644	return FALSE;
1645
1646    /* If it's not a release or click of the first mouse button, get
1647     * out. */
1648    if (!(mevent.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)))
1649	return FALSE;
1650
1651    /* Save the screen coordinates where the mouse event took place. */
1652    *mouse_x = mevent.x;
1653    *mouse_y = mevent.y;
1654
1655    /* If we're allowing shortcuts, the current shortcut list is being
1656     * displayed on the last two lines of the screen, and the mouse
1657     * event took place inside it, we need to figure out which shortcut
1658     * was clicked and put back the equivalent keystroke(s) for it. */
1659    if (allow_shortcuts && !ISSET(NO_HELP) && wenclose(bottomwin,
1660	*mouse_y, *mouse_x)) {
1661	int i, j;
1662	size_t currslen;
1663	    /* The number of shortcuts in the current shortcut list. */
1664	const shortcut *s = currshortcut;
1665	    /* The actual shortcut we clicked on, starting at the first
1666	     * one in the current shortcut list. */
1667
1668	/* Get the shortcut lists' length. */
1669	if (currshortcut == main_list)
1670	    currslen = MAIN_VISIBLE;
1671	else {
1672	    currslen = length_of_list(currshortcut);
1673
1674	    /* We don't show any more shortcuts than the main list
1675	     * does. */
1676	    if (currslen > MAIN_VISIBLE)
1677		currslen = MAIN_VISIBLE;
1678	}
1679
1680	/* Calculate the width of all of the shortcuts in the list
1681	 * except for the last two, which are longer by (COLS % i)
1682	 * columns so as to not waste space. */
1683	if (currslen < 2)
1684	    i = COLS / (MAIN_VISIBLE / 2);
1685	else
1686	    i = COLS / ((currslen / 2) + (currslen % 2));
1687
1688	/* Calculate the y-coordinate relative to the beginning of
1689	 * the shortcut list in bottomwin, i.e. with the sizes of
1690	 * topwin, edit, and the first line of bottomwin subtracted
1691	 * out, and set j to it. */
1692	j = *mouse_y - (2 - no_more_space()) - editwinrows - 1;
1693
1694	/* If we're on the statusbar, don't do anything. */
1695	if (j < 0)
1696	    return FALSE;
1697
1698	/* Calculate the x-coordinate relative to the beginning of the
1699	 * shortcut list in bottomwin, and add it to j.  j should now be
1700	 * the index in the shortcut list of the shortcut we clicked. */
1701	j = (*mouse_x / i) * 2 + j;
1702
1703	/* Adjust j if we clicked in the last two shortcuts. */
1704	if ((j >= currslen) && (*mouse_x % i < COLS % i))
1705	    j -= 2;
1706
1707	/* If we're beyond the last shortcut, don't do anything. */
1708	if (j >= currslen)
1709	    return FALSE;
1710
1711	/* Go through the shortcut list to determine which shortcut was
1712	 * clicked. */
1713	for (; j > 0; j--)
1714	    s = s->next;
1715
1716	/* And put back the equivalent key.  Assume that each shortcut
1717	 * has, at the very least, an equivalent control key, an
1718	 * equivalent primary meta key sequence, or both. */
1719	if (s->ctrlval != NANO_NO_KEY) {
1720	    unget_kbinput(s->ctrlval, FALSE, FALSE);
1721	    return TRUE;
1722	} else if (s->metaval != NANO_NO_KEY) {
1723	    unget_kbinput(s->metaval, TRUE, FALSE);
1724	    return TRUE;
1725	}
1726    }
1727    return FALSE;
1728}
1729#endif /* !DISABLE_MOUSE */
1730
1731/* Return the shortcut corresponding to the values of kbinput (the key
1732 * itself), meta_key (whether the key is a meta sequence), and func_key
1733 * (whether the key is a function key), if any.  The shortcut will be
1734 * the first one in the list (control key, meta key sequence, function
1735 * key, other meta key sequence) for the corresponding function.  For
1736 * example, passing in a meta key sequence that corresponds to a
1737 * function with a control key, a function key, and a meta key sequence
1738 * will return the control key corresponding to that function. */
1739const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool
1740	*meta_key, bool *func_key)
1741{
1742    const shortcut *s = s_list;
1743    size_t slen = length_of_list(s_list);
1744
1745#ifdef DEBUG
1746    fprintf(stderr, "get_shortcut(): kbinput = %d, meta_key = %s, func_key = %s\n", *kbinput, *meta_key ? "TRUE" : "FALSE", *func_key ? "TRUE" : "FALSE");
1747#endif
1748
1749    /* Check for shortcuts. */
1750    for (; slen > 0; slen--) {
1751	/* We've found a shortcut if:
1752	 *
1753	 * 1. The key exists.
1754	 * 2. The key is a control key in the shortcut list.
1755	 * 3. meta_key is TRUE and the key is the primary or
1756	 *    miscellaneous meta sequence in the shortcut list.
1757	 * 4. func_key is TRUE and the key is a function key in the
1758	 *    shortcut list. */
1759
1760	if (*kbinput != NANO_NO_KEY && (*kbinput == s->ctrlval ||
1761		(*meta_key && (*kbinput == s->metaval || *kbinput ==
1762		s->miscval)) || (*func_key && *kbinput ==
1763		s->funcval))) {
1764	    break;
1765	}
1766
1767	s = s->next;
1768    }
1769
1770    /* Translate the shortcut to either its control key or its meta key
1771     * equivalent.  Assume that the shortcut has an equivalent control
1772     * key, an equivalent primary meta key sequence, or both. */
1773    if (slen > 0) {
1774	if (s->ctrlval != NANO_NO_KEY) {
1775	    *meta_key = FALSE;
1776	    *func_key = FALSE;
1777	    *kbinput = s->ctrlval;
1778	    return s;
1779	} else if (s->metaval != NANO_NO_KEY) {
1780	    *meta_key = TRUE;
1781	    *func_key = FALSE;
1782	    *kbinput = s->metaval;
1783	    return s;
1784	}
1785    }
1786
1787    return NULL;
1788}
1789
1790#ifndef NANO_TINY
1791/* Return the global toggle corresponding to the values of kbinput (the
1792 * key itself) and meta_key (whether the key is a meta sequence), if
1793 * any. */
1794const toggle *get_toggle(int kbinput, bool meta_key)
1795{
1796    const toggle *t = toggles;
1797
1798#ifdef DEBUG
1799    fprintf(stderr, "get_toggle(): kbinput = %d, meta_key = %s\n", kbinput, meta_key ? "TRUE" : "FALSE");
1800#endif
1801
1802    /* Check for toggles. */
1803    for (; t != NULL; t = t->next) {
1804	/* We've found a toggle if the key exists, meta_key is TRUE, and
1805	 * the key is in the meta key toggle list. */
1806	if (t->val != TOGGLE_NO_KEY && meta_key && kbinput == t->val)
1807	    break;
1808    }
1809
1810    return t;
1811}
1812#endif /* !NANO_TINY */
1813
1814/* Move to (x, y) in win, and display a line of n spaces with the
1815 * current attributes. */
1816void blank_line(WINDOW *win, int y, int x, int n)
1817{
1818    wmove(win, y, x);
1819
1820    for (; n > 0; n--)
1821	waddch(win, ' ');
1822}
1823
1824/* Blank the first line of the top portion of the window. */
1825void blank_titlebar(void)
1826{
1827    blank_line(topwin, 0, 0, COLS);
1828}
1829
1830/* If the MORE_SPACE flag isn't set, blank the second line of the top
1831 * portion of the window. */
1832void blank_topbar(void)
1833{
1834    if (!ISSET(MORE_SPACE))
1835	blank_line(topwin, 1, 0, COLS);
1836}
1837
1838/* Blank all the lines of the middle portion of the window, i.e. the
1839 * edit window. */
1840void blank_edit(void)
1841{
1842    int i;
1843
1844    for (i = 0; i < editwinrows; i++)
1845	blank_line(edit, i, 0, COLS);
1846}
1847
1848/* Blank the first line of the bottom portion of the window. */
1849void blank_statusbar(void)
1850{
1851    blank_line(bottomwin, 0, 0, COLS);
1852}
1853
1854/* If the NO_HELP flag isn't set, blank the last two lines of the bottom
1855 * portion of the window. */
1856void blank_bottombars(void)
1857{
1858    if (!ISSET(NO_HELP)) {
1859	blank_line(bottomwin, 1, 0, COLS);
1860	blank_line(bottomwin, 2, 0, COLS);
1861    }
1862}
1863
1864/* Check if the number of keystrokes needed to blank the statusbar has
1865 * been pressed.  If so, blank the statusbar, unless constant cursor
1866 * position display is on. */
1867void check_statusblank(void)
1868{
1869    if (statusblank > 0) {
1870	statusblank--;
1871
1872	if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
1873	    blank_statusbar();
1874	    wnoutrefresh(bottomwin);
1875	    reset_cursor();
1876	    wnoutrefresh(edit);
1877	}
1878    }
1879}
1880
1881/* Convert buf into a string that can be displayed on screen.  The
1882 * caller wants to display buf starting with column start_col, and
1883 * extending for at most len columns.  start_col is zero-based.  len is
1884 * one-based, so len == 0 means you get "" returned.  The returned
1885 * string is dynamically allocated, and should be freed.  If dollars is
1886 * TRUE, the caller might put "$" at the beginning or end of the line if
1887 * it's too long. */
1888char *display_string(const char *buf, size_t start_col, size_t len, bool
1889	dollars)
1890{
1891    size_t start_index;
1892	/* Index in buf of the first character shown. */
1893    size_t column;
1894	/* Screen column that start_index corresponds to. */
1895    size_t alloc_len;
1896	/* The length of memory allocated for converted. */
1897    char *converted;
1898	/* The string we return. */
1899    size_t index;
1900	/* Current position in converted. */
1901    char *buf_mb;
1902    int buf_mb_len;
1903
1904    /* If dollars is TRUE, make room for the "$" at the end of the
1905     * line. */
1906    if (dollars && len > 0 && strlenpt(buf) > start_col + len)
1907	len--;
1908
1909    if (len == 0)
1910	return mallocstrcpy(NULL, "");
1911
1912    buf_mb = charalloc(mb_cur_max());
1913
1914    start_index = actual_x(buf, start_col);
1915    column = strnlenpt(buf, start_index);
1916
1917    assert(column <= start_col);
1918
1919    /* Make sure there's enough room for the initial character, whether
1920     * it's a multibyte control character, a non-control multibyte
1921     * character, a tab character, or a null terminator.  Rationale:
1922     *
1923     * multibyte control character followed by a null terminator:
1924     *     1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0')
1925     * multibyte non-control character followed by a null terminator:
1926     *     mb_cur_max() bytes + 1 byte ('\0')
1927     * tab character followed by a null terminator:
1928     *     mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0')
1929     *
1930     * Since tabsize has a minimum value of 1, it can substitute for 1
1931     * byte above. */
1932    alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
1933    converted = charalloc(alloc_len);
1934
1935    index = 0;
1936
1937    if (buf[start_index] != '\0' && buf[start_index] != '\t' &&
1938	(column < start_col || (dollars && column > 0))) {
1939	/* We don't display all of buf[start_index] since it starts to
1940	 * the left of the screen. */
1941	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1942
1943	if (is_cntrl_mbchar(buf_mb)) {
1944	    if (column < start_col) {
1945		char *ctrl_buf_mb = charalloc(mb_cur_max());
1946		int ctrl_buf_mb_len, i;
1947
1948		ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
1949			&ctrl_buf_mb_len);
1950
1951		for (i = 0; i < ctrl_buf_mb_len; i++)
1952		    converted[index++] = ctrl_buf_mb[i];
1953
1954		start_col += mbwidth(ctrl_buf_mb);
1955
1956		free(ctrl_buf_mb);
1957
1958		start_index += buf_mb_len;
1959	    }
1960	}
1961#ifdef ENABLE_UTF8
1962	else if (using_utf8() && mbwidth(buf_mb) == 2) {
1963	    if (column >= start_col) {
1964		converted[index++] = ' ';
1965		start_col++;
1966	    }
1967
1968	    converted[index++] = ' ';
1969	    start_col++;
1970
1971	    start_index += buf_mb_len;
1972	}
1973#endif
1974    }
1975
1976    while (buf[start_index] != '\0') {
1977	buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
1978
1979	/* Make sure there's enough room for the next character, whether
1980	 * it's a multibyte control character, a non-control multibyte
1981	 * character, a tab character, or a null terminator. */
1982	if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) {
1983	    alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE;
1984	    converted = charealloc(converted, alloc_len);
1985	}
1986
1987	/* If buf contains a tab character, interpret it. */
1988	if (*buf_mb == '\t') {
1989#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
1990	    if (ISSET(WHITESPACE_DISPLAY)) {
1991		int i;
1992
1993		for (i = 0; i < whitespace_len[0]; i++)
1994		    converted[index++] = whitespace[i];
1995	    } else
1996#endif
1997		converted[index++] = ' ';
1998	    start_col++;
1999	    while (start_col % tabsize != 0) {
2000		converted[index++] = ' ';
2001		start_col++;
2002	    }
2003	/* If buf contains a control character, interpret it.  If buf
2004	 * contains an invalid multibyte control character, display it
2005	 * as such.*/
2006	} else if (is_cntrl_mbchar(buf_mb)) {
2007	    char *ctrl_buf_mb = charalloc(mb_cur_max());
2008	    int ctrl_buf_mb_len, i;
2009
2010	    converted[index++] = '^';
2011	    start_col++;
2012
2013	    ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
2014		&ctrl_buf_mb_len);
2015
2016	    for (i = 0; i < ctrl_buf_mb_len; i++)
2017		converted[index++] = ctrl_buf_mb[i];
2018
2019	    start_col += mbwidth(ctrl_buf_mb);
2020
2021	    free(ctrl_buf_mb);
2022	/* If buf contains a space character, interpret it. */
2023	} else if (*buf_mb == ' ') {
2024#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2025	    if (ISSET(WHITESPACE_DISPLAY)) {
2026		int i;
2027
2028		for (i = whitespace_len[0]; i < whitespace_len[0] +
2029			whitespace_len[1]; i++)
2030		    converted[index++] = whitespace[i];
2031	    } else
2032#endif
2033		converted[index++] = ' ';
2034	    start_col++;
2035	/* If buf contains a non-control character, interpret it.  If
2036	 * buf contains an invalid multibyte non-control character,
2037	 * display it as such. */
2038	} else {
2039	    char *nctrl_buf_mb = charalloc(mb_cur_max());
2040	    int nctrl_buf_mb_len, i;
2041
2042	    nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
2043		&nctrl_buf_mb_len);
2044
2045	    for (i = 0; i < nctrl_buf_mb_len; i++)
2046		converted[index++] = nctrl_buf_mb[i];
2047
2048	    start_col += mbwidth(nctrl_buf_mb);
2049
2050	    free(nctrl_buf_mb);
2051	}
2052
2053	start_index += buf_mb_len;
2054    }
2055
2056    free(buf_mb);
2057
2058    assert(alloc_len >= index + 1);
2059
2060    /* Null-terminate converted. */
2061    converted[index] = '\0';
2062
2063    /* Make sure converted takes up no more than len columns. */
2064    index = actual_x(converted, len);
2065    null_at(&converted, index);
2066
2067    return converted;
2068}
2069
2070/* If path is NULL, we're in normal editing mode, so display the current
2071 * version of nano, the current filename, and whether the current file
2072 * has been modified on the titlebar.  If path isn't NULL, we're in the
2073 * file browser, and path contains the directory to start the file
2074 * browser in, so display the current version of nano and the contents
2075 * of path on the titlebar. */
2076void titlebar(const char *path)
2077{
2078    int space = COLS;
2079	/* The space we have available for display. */
2080    size_t verlen = strlenpt(PACKAGE_STRING) + 1;
2081	/* The length of the version message in columns, plus one for
2082	 * padding. */
2083    const char *prefix;
2084	/* "DIR:", "File:", or "New Buffer".  Goes before filename. */
2085    size_t prefixlen;
2086	/* The length of the prefix in columns, plus one for padding. */
2087    const char *state;
2088	/* "Modified", "View", or "".  Shows the state of this
2089	 * buffer. */
2090    size_t statelen = 0;
2091	/* The length of the state in columns, or the length of
2092	 * "Modified" if the state is blank and we're not in the file
2093	 * browser. */
2094    char *exppath = NULL;
2095	/* The filename, expanded for display. */
2096    bool newfie = FALSE;
2097	/* Do we say "New Buffer"? */
2098    bool dots = FALSE;
2099	/* Do we put an ellipsis before the path? */
2100
2101    assert(path != NULL || openfile->filename != NULL);
2102
2103    wattron(topwin, reverse_attr);
2104
2105    blank_titlebar();
2106
2107    /* space has to be at least 4: two spaces before the version message,
2108     * at least one character of the version message, and one space
2109     * after the version message. */
2110    if (space < 4)
2111	space = 0;
2112    else {
2113	/* Limit verlen to 1/3 the length of the screen in columns,
2114	 * minus three columns for spaces. */
2115	if (verlen > (COLS / 3) - 3)
2116	    verlen = (COLS / 3) - 3;
2117    }
2118
2119    if (space >= 4) {
2120	/* Add a space after the version message, and account for both
2121	 * it and the two spaces before it. */
2122	mvwaddnstr(topwin, 0, 2, PACKAGE_STRING,
2123		actual_x(PACKAGE_STRING, verlen));
2124	verlen += 3;
2125
2126	/* Account for the full length of the version message. */
2127	space -= verlen;
2128    }
2129
2130#ifndef DISABLE_BROWSER
2131    /* Don't display the state if we're in the file browser. */
2132    if (path != NULL)
2133	state = "";
2134    else
2135#endif
2136	state = openfile->modified ? _("Modified") : ISSET(VIEW_MODE) ?
2137		_("View") : "";
2138
2139    statelen = strlenpt((state[0] == '\0' && path == NULL) ?
2140	_("Modified") : state);
2141
2142    /* If possible, add a space before state. */
2143    if (space > 0 && statelen < space)
2144	statelen++;
2145    else
2146	goto the_end;
2147
2148#ifndef DISABLE_BROWSER
2149    /* path should be a directory if we're in the file browser. */
2150    if (path != NULL)
2151	prefix = _("DIR:");
2152    else
2153#endif
2154    if (openfile->filename[0] == '\0') {
2155	prefix = _("New Buffer");
2156	newfie = TRUE;
2157    } else
2158	prefix = _("File:");
2159
2160    prefixlen = strnlenpt(prefix, space - statelen) + 1;
2161
2162    /* If newfie is FALSE, add a space after prefix. */
2163    if (!newfie && prefixlen + statelen < space)
2164	prefixlen++;
2165
2166    /* If we're not in the file browser, set path to the current
2167     * filename. */
2168    if (path == NULL)
2169	path = openfile->filename;
2170
2171    /* Account for the full lengths of the prefix and the state. */
2172    if (space >= prefixlen + statelen)
2173	space -= prefixlen + statelen;
2174    else
2175	space = 0;
2176	/* space is now the room we have for the filename. */
2177
2178    if (!newfie) {
2179	size_t lenpt = strlenpt(path), start_col;
2180
2181	/* Don't set dots to TRUE if we have fewer than eight columns
2182	 * (i.e. one column for padding, plus seven columns for a
2183	 * filename). */
2184	dots = (space >= 8 && lenpt >= space);
2185
2186	if (dots) {
2187	    start_col = lenpt - space + 3;
2188	    space -= 3;
2189	} else
2190	    start_col = 0;
2191
2192	exppath = display_string(path, start_col, space, FALSE);
2193    }
2194
2195    /* If dots is TRUE, we will display something like "File:
2196     * ...ename". */
2197    if (dots) {
2198	mvwaddnstr(topwin, 0, verlen - 1, prefix, actual_x(prefix,
2199		prefixlen));
2200	if (space <= -3 || newfie)
2201	    goto the_end;
2202	waddch(topwin, ' ');
2203	waddnstr(topwin, "...", space + 3);
2204	if (space <= 0)
2205	    goto the_end;
2206	waddstr(topwin, exppath);
2207    } else {
2208	size_t exppathlen = newfie ? 0 : strlenpt(exppath);
2209	    /* The length of the expanded filename. */
2210
2211	/* There is room for the whole filename, so we center it. */
2212	mvwaddnstr(topwin, 0, verlen + ((space - exppathlen) / 3),
2213		prefix, actual_x(prefix, prefixlen));
2214	if (!newfie) {
2215	    waddch(topwin, ' ');
2216	    waddstr(topwin, exppath);
2217	}
2218    }
2219
2220  the_end:
2221    free(exppath);
2222
2223    if (state[0] != '\0') {
2224	if (statelen >= COLS - 1)
2225	    mvwaddnstr(topwin, 0, 0, state, actual_x(state, COLS));
2226	else {
2227	    assert(COLS - statelen - 1 >= 0);
2228
2229	    mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2230		actual_x(state, statelen));
2231	}
2232    }
2233
2234    wattroff(topwin, reverse_attr);
2235
2236    wnoutrefresh(topwin);
2237    reset_cursor();
2238    wnoutrefresh(edit);
2239}
2240
2241/* Mark the current file as modified if it isn't already, and then
2242 * update the titlebar to display the file's new status. */
2243void set_modified(void)
2244{
2245    if (!openfile->modified) {
2246	openfile->modified = TRUE;
2247	titlebar(NULL);
2248    }
2249}
2250
2251/* Display a message on the statusbar, and set disable_cursorpos to
2252 * TRUE, so that the message won't be immediately overwritten if
2253 * constant cursor position display is on. */
2254void statusbar(const char *msg, ...)
2255{
2256    va_list ap;
2257    char *bar, *foo;
2258    size_t start_x, foo_len;
2259#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2260    bool old_whitespace;
2261#endif
2262
2263    va_start(ap, msg);
2264
2265    /* Curses mode is turned off.  If we use wmove() now, it will muck
2266     * up the terminal settings.  So we just use vfprintf(). */
2267    if (isendwin()) {
2268	vfprintf(stderr, msg, ap);
2269	va_end(ap);
2270	return;
2271    }
2272
2273    blank_statusbar();
2274
2275#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2276    old_whitespace = ISSET(WHITESPACE_DISPLAY);
2277    UNSET(WHITESPACE_DISPLAY);
2278#endif
2279    bar = charalloc(mb_cur_max() * (COLS - 3));
2280    vsnprintf(bar, mb_cur_max() * (COLS - 3), msg, ap);
2281    va_end(ap);
2282    foo = display_string(bar, 0, COLS - 4, FALSE);
2283#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
2284    if (old_whitespace)
2285	SET(WHITESPACE_DISPLAY);
2286#endif
2287    free(bar);
2288    foo_len = strlenpt(foo);
2289    start_x = (COLS - foo_len - 4) / 2;
2290
2291    wmove(bottomwin, 0, start_x);
2292    wattron(bottomwin, reverse_attr);
2293    waddstr(bottomwin, "[ ");
2294    waddstr(bottomwin, foo);
2295    free(foo);
2296    waddstr(bottomwin, " ]");
2297    wattroff(bottomwin, reverse_attr);
2298    wnoutrefresh(bottomwin);
2299    reset_cursor();
2300    wnoutrefresh(edit);
2301	/* Leave the cursor at its position in the edit window, not in
2302	 * the statusbar. */
2303
2304    disable_cursorpos = TRUE;
2305
2306    /* If we're doing quick statusbar blanking, and constant cursor
2307     * position display is off, blank the statusbar after only one
2308     * keystroke.  Otherwise, blank it after twenty-six keystrokes, as
2309     * Pico does. */
2310    statusblank =
2311#ifndef NANO_TINY
2312	ISSET(QUICK_BLANK) && !ISSET(CONST_UPDATE) ? 1 :
2313#endif
2314	26;
2315}
2316
2317/* Display the shortcut list in s on the last two rows of the bottom
2318 * portion of the window. */
2319void bottombars(const shortcut *s)
2320{
2321    size_t i, colwidth, slen;
2322
2323    if (ISSET(NO_HELP))
2324	return;
2325
2326    if (s == main_list) {
2327	slen = MAIN_VISIBLE;
2328
2329	assert(slen <= length_of_list(s));
2330    } else {
2331	slen = length_of_list(s);
2332
2333	/* Don't show any more shortcuts than the main list does. */
2334	if (slen > MAIN_VISIBLE)
2335	    slen = MAIN_VISIBLE;
2336    }
2337
2338    /* There will be this many characters per column, except for the
2339     * last two, which will be longer by (COLS % colwidth) columns so as
2340     * to not waste space.  We need at least three columns to display
2341     * anything properly. */
2342    colwidth = COLS / ((slen / 2) + (slen % 2));
2343
2344    blank_bottombars();
2345
2346    for (i = 0; i < slen; i++, s = s->next) {
2347	const char *keystr;
2348	char foo[4] = "";
2349
2350	/* Yucky sentinel values that we can't handle a better way. */
2351	if (s->ctrlval == NANO_CONTROL_SPACE)
2352	    strcpy(foo, "^ ");
2353	else if (s->ctrlval == NANO_CONTROL_8)
2354	    strcpy(foo, "^?");
2355	/* Normal values.  Assume that the shortcut has an equivalent
2356	 * control key, meta key sequence, or both. */
2357	else if (s->ctrlval != NANO_NO_KEY)
2358	    sprintf(foo, "^%c", s->ctrlval + 64);
2359	else if (s->metaval != NANO_NO_KEY)
2360	    sprintf(foo, "M-%c", toupper(s->metaval));
2361
2362	keystr = foo;
2363
2364	wmove(bottomwin, 1 + i % 2, (i / 2) * colwidth);
2365	onekey(keystr, s->desc, colwidth + (COLS % colwidth));
2366    }
2367
2368    wnoutrefresh(bottomwin);
2369    reset_cursor();
2370    wnoutrefresh(edit);
2371}
2372
2373/* Write a shortcut key to the help area at the bottom of the window.
2374 * keystroke is e.g. "^G" and desc is e.g. "Get Help".  We are careful
2375 * to write at most len characters, even if len is very small and
2376 * keystroke and desc are long.  Note that waddnstr(,,(size_t)-1) adds
2377 * the whole string!  We do not bother padding the entry with blanks. */
2378void onekey(const char *keystroke, const char *desc, size_t len)
2379{
2380    size_t keystroke_len = strlenpt(keystroke) + 1;
2381
2382    assert(keystroke != NULL && desc != NULL);
2383
2384    wattron(bottomwin, reverse_attr);
2385    waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2386    wattroff(bottomwin, reverse_attr);
2387
2388    if (len > keystroke_len)
2389	len -= keystroke_len;
2390    else
2391	len = 0;
2392
2393    if (len > 0) {
2394	waddch(bottomwin, ' ');
2395	waddnstr(bottomwin, desc, actual_x(desc, len));
2396    }
2397}
2398
2399/* Reset current_y, based on the position of current, and put the cursor
2400 * in the edit window at (current_y, current_x). */
2401void reset_cursor(void)
2402{
2403    /* If we haven't opened any files yet, put the cursor in the top
2404     * left corner of the edit window and get out. */
2405    if (openfile == NULL) {
2406	wmove(edit, 0, 0);
2407	return;
2408    }
2409
2410    openfile->current_y = openfile->current->lineno -
2411	openfile->edittop->lineno;
2412    if (openfile->current_y < editwinrows) {
2413	size_t xpt = xplustabs();
2414
2415	wmove(edit, openfile->current_y, xpt - get_page_start(xpt));
2416     }
2417}
2418
2419/* edit_draw() takes care of the job of actually painting a line into
2420 * the edit window.  fileptr is the line to be painted, at row line of
2421 * the window.  converted is the actual string to be written to the
2422 * window, with tabs and control characters replaced by strings of
2423 * regular characters.  start is the column number of the first
2424 * character of this page.  That is, the first character of converted
2425 * corresponds to character number actual_x(fileptr->data, start) of the
2426 * line. */
2427void edit_draw(const filestruct *fileptr, const char *converted, int
2428	line, size_t start)
2429{
2430#if !defined(NANO_TINY) || defined(ENABLE_COLOR)
2431    size_t startpos = actual_x(fileptr->data, start);
2432	/* The position in fileptr->data of the leftmost character
2433	 * that displays at least partially on the window. */
2434    size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1;
2435	/* The position in fileptr->data of the first character that is
2436	 * completely off the window to the right.
2437	 *
2438	 * Note that endpos might be beyond the null terminator of the
2439	 * string. */
2440#endif
2441
2442    assert(openfile != NULL && fileptr != NULL && converted != NULL);
2443    assert(strlenpt(converted) <= COLS);
2444
2445    /* Just paint the string in any case (we'll add color or reverse on
2446     * just the text that needs it). */
2447    mvwaddstr(edit, line, 0, converted);
2448
2449#ifdef ENABLE_COLOR
2450    /* If color syntaxes are available and turned on, we need to display
2451     * them. */
2452    if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
2453	const colortype *tmpcolor = openfile->colorstrings;
2454
2455	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
2456	    int x_start;
2457		/* Starting column for mvwaddnstr.  Zero-based. */
2458	    int paintlen;
2459		/* Number of chars to paint on this line.  There are
2460		 * COLS characters on a whole line. */
2461	    size_t index;
2462		/* Index in converted where we paint. */
2463	    regmatch_t startmatch;
2464		/* Match position for start_regex. */
2465	    regmatch_t endmatch;
2466		/* Match position for end_regex. */
2467
2468	    /* radar:16401178 */
2469	    memset(&startmatch, 0, sizeof(startmatch));
2470	    memset(&endmatch, 0, sizeof(endmatch));
2471
2472	    if (tmpcolor->bright)
2473		wattron(edit, A_BOLD);
2474	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
2475	    /* Two notes about regexec().  A return value of zero means
2476	     * that there is a match.  Also, rm_eo is the first
2477	     * non-matching character after the match. */
2478
2479	    /* First case, tmpcolor is a single-line expression. */
2480	    if (tmpcolor->end == NULL) {
2481		size_t k = 0;
2482
2483		/* We increment k by rm_eo, to move past the end of the
2484		 * last match.  Even though two matches may overlap, we
2485		 * want to ignore them, so that we can highlight e.g. C
2486		 * strings correctly. */
2487		while (k < endpos) {
2488		    /* Note the fifth parameter to regexec().  It says
2489		     * not to match the beginning-of-line character
2490		     * unless k is zero.  If regexec() returns
2491		     * REG_NOMATCH, there are no more matches in the
2492		     * line. */
2493		    if (regexec(tmpcolor->start, &fileptr->data[k], 1,
2494			&startmatch, (k == 0) ? 0 : REG_NOTBOL) ==
2495			REG_NOMATCH)
2496			break;
2497		    /* Translate the match to the beginning of the
2498		     * line. */
2499		    startmatch.rm_so += k;
2500		    startmatch.rm_eo += k;
2501
2502		    /* Skip over a zero-length regex match. */
2503		    if (startmatch.rm_so == startmatch.rm_eo)
2504			startmatch.rm_eo++;
2505		    else if (startmatch.rm_so < endpos &&
2506			startmatch.rm_eo > startpos) {
2507			x_start = (startmatch.rm_so <= startpos) ? 0 :
2508				strnlenpt(fileptr->data,
2509				startmatch.rm_so) - start;
2510
2511			index = actual_x(converted, x_start);
2512
2513			paintlen = actual_x(converted + index,
2514				strnlenpt(fileptr->data,
2515				startmatch.rm_eo) - start - x_start);
2516
2517			assert(0 <= x_start && 0 <= paintlen);
2518
2519			mvwaddnstr(edit, line, x_start, converted +
2520				index, paintlen);
2521		    }
2522		    k = startmatch.rm_eo;
2523		}
2524	    } else {
2525		/* This is a multi-line regex.  There are two steps.
2526		 * First, we have to see if the beginning of the line is
2527		 * colored by a start on an earlier line, and an end on
2528		 * this line or later.
2529		 *
2530		 * We find the first line before fileptr matching the
2531		 * start.  If every match on that line is followed by an
2532		 * end, then go to step two.  Otherwise, find the next
2533		 * line after start_line matching the end.  If that line
2534		 * is not before fileptr, then paint the beginning of
2535		 * this line. */
2536		const filestruct *start_line = fileptr->prev;
2537		    /* The first line before fileptr matching start. */
2538		regoff_t start_col;
2539		    /* Where it starts in that line. */
2540		const filestruct *end_line;
2541
2542		while (start_line != NULL && regexec(tmpcolor->start,
2543			start_line->data, 1, &startmatch, 0) ==
2544			REG_NOMATCH) {
2545		    /* If there is an end on this line, there is no need
2546		     * to look for starts on earlier lines. */
2547		    if (regexec(tmpcolor->end, start_line->data, 0,
2548			NULL, 0) == 0)
2549			goto step_two;
2550		    start_line = start_line->prev;
2551		}
2552
2553		/* Skip over a zero-length regex match. */
2554		if (startmatch.rm_so == startmatch.rm_eo)
2555		    startmatch.rm_eo++;
2556		else {
2557		    /* No start found, so skip to the next step. */
2558		    if (start_line == NULL)
2559			goto step_two;
2560		    /* Now start_line is the first line before fileptr
2561		     * containing a start match.  Is there a start on
2562		     * this line not followed by an end on this line? */
2563		    start_col = 0;
2564		    while (TRUE) {
2565			start_col += startmatch.rm_so;
2566			startmatch.rm_eo -= startmatch.rm_so;
2567			if (regexec(tmpcolor->end, start_line->data +
2568				start_col + startmatch.rm_eo, 0, NULL,
2569				(start_col + startmatch.rm_eo == 0) ?
2570				0 : REG_NOTBOL) == REG_NOMATCH)
2571			    /* No end found after this start. */
2572			    break;
2573			start_col++;
2574			if (regexec(tmpcolor->start, start_line->data +
2575				start_col, 1, &startmatch,
2576				REG_NOTBOL) == REG_NOMATCH)
2577			    /* No later start on this line. */
2578			    goto step_two;
2579		    }
2580		    /* Indeed, there is a start not followed on this
2581		     * line by an end. */
2582
2583		    /* We have already checked that there is no end
2584		     * before fileptr and after the start.  Is there an
2585		     * end after the start at all?  We don't paint
2586		     * unterminated starts. */
2587		    end_line = fileptr;
2588		    while (end_line != NULL && regexec(tmpcolor->end,
2589			end_line->data, 1, &endmatch, 0) == REG_NOMATCH)
2590			end_line = end_line->next;
2591
2592		    /* No end found, or it is too early. */
2593		    if (end_line == NULL || (end_line == fileptr &&
2594			endmatch.rm_eo <= startpos))
2595			goto step_two;
2596
2597		    /* Now paint the start of fileptr.  If the start of
2598		     * fileptr is on a different line from the end,
2599		     * paintlen is -1, meaning that everything on the
2600		     * line gets painted.  Otherwise, paintlen is the
2601		     * expanded location of the end of the match minus
2602		     * the expanded location of the beginning of the
2603		     * page. */
2604		    if (end_line != fileptr)
2605			paintlen = -1;
2606		    else
2607			paintlen = actual_x(converted,
2608				strnlenpt(fileptr->data,
2609				endmatch.rm_eo) - start);
2610
2611		    mvwaddnstr(edit, line, 0, converted, paintlen);
2612
2613  step_two:
2614		    /* Second step, we look for starts on this line. */
2615		    start_col = 0;
2616
2617		    while (start_col < endpos) {
2618			if (regexec(tmpcolor->start, fileptr->data +
2619				start_col, 1, &startmatch, (start_col ==
2620				0) ? 0 : REG_NOTBOL) == REG_NOMATCH ||
2621				start_col + startmatch.rm_so >= endpos)
2622			    /* No more starts on this line. */
2623			    break;
2624			/* Translate the match to be relative to the
2625			 * beginning of the line. */
2626			startmatch.rm_so += start_col;
2627			startmatch.rm_eo += start_col;
2628
2629			x_start = (startmatch.rm_so <= startpos) ? 0 :
2630				strnlenpt(fileptr->data,
2631				startmatch.rm_so) - start;
2632
2633			index = actual_x(converted, x_start);
2634
2635			if (regexec(tmpcolor->end, fileptr->data +
2636				startmatch.rm_eo, 1, &endmatch,
2637				(startmatch.rm_eo == 0) ? 0 :
2638				REG_NOTBOL) == 0) {
2639			    /* Translate the end match to be relative to
2640			     * the beginning of the line. */
2641			    endmatch.rm_so += startmatch.rm_eo;
2642			    endmatch.rm_eo += startmatch.rm_eo;
2643			    /* There is an end on this line.  But does
2644			     * it appear on this page, and is the match
2645			     * more than zero characters long? */
2646			    if (endmatch.rm_eo > startpos &&
2647				endmatch.rm_eo > startmatch.rm_so) {
2648				paintlen = actual_x(converted + index,
2649					strnlenpt(fileptr->data,
2650					endmatch.rm_eo) - start -
2651					x_start);
2652
2653				assert(0 <= x_start && x_start < COLS);
2654
2655				mvwaddnstr(edit, line, x_start,
2656					converted + index, paintlen);
2657			    }
2658			} else {
2659			    /* There is no end on this line.  But we
2660			     * haven't yet looked for one on later
2661			     * lines. */
2662			    end_line = fileptr->next;
2663
2664			    while (end_line != NULL &&
2665				regexec(tmpcolor->end, end_line->data,
2666				0, NULL, 0) == REG_NOMATCH)
2667				end_line = end_line->next;
2668
2669			    if (end_line != NULL) {
2670				assert(0 <= x_start && x_start < COLS);
2671
2672				mvwaddnstr(edit, line, x_start,
2673					converted + index, -1);
2674				/* We painted to the end of the line, so
2675				 * don't bother checking any more
2676				 * starts. */
2677				break;
2678			    }
2679			}
2680			start_col = startmatch.rm_so + 1;
2681		    }
2682		}
2683	    }
2684
2685	    wattroff(edit, A_BOLD);
2686	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2687	}
2688    }
2689#endif /* ENABLE_COLOR */
2690
2691#ifndef NANO_TINY
2692    /* If the mark is on, we need to display it. */
2693    if (openfile->mark_set && (fileptr->lineno <=
2694	openfile->mark_begin->lineno || fileptr->lineno <=
2695	openfile->current->lineno) && (fileptr->lineno >=
2696	openfile->mark_begin->lineno || fileptr->lineno >=
2697	openfile->current->lineno)) {
2698	/* fileptr is at least partially selected. */
2699	const filestruct *top;
2700	    /* Either current or mark_begin, whichever is first. */
2701	size_t top_x;
2702	    /* current_x or mark_begin_x, corresponding to top. */
2703	const filestruct *bot;
2704	size_t bot_x;
2705	int x_start;
2706	    /* Starting column for mvwaddnstr().  Zero-based. */
2707	int paintlen;
2708	    /* Number of characters to paint on this line.  There are
2709	     * COLS characters on a whole line. */
2710	size_t index;
2711	    /* Index in converted where we paint. */
2712
2713	mark_order(&top, &top_x, &bot, &bot_x, NULL);
2714
2715	if (top->lineno < fileptr->lineno || top_x < startpos)
2716	    top_x = startpos;
2717	if (bot->lineno > fileptr->lineno || bot_x > endpos)
2718	    bot_x = endpos;
2719
2720	/* The selected bit of fileptr is on this page. */
2721	if (top_x < endpos && bot_x > startpos) {
2722	    assert(startpos <= top_x);
2723
2724	    /* x_start is the expanded location of the beginning of the
2725	     * mark minus the beginning of the page. */
2726	    x_start = strnlenpt(fileptr->data, top_x) - start;
2727
2728	    /* If the end of the mark is off the page, paintlen is -1,
2729	     * meaning that everything on the line gets painted.
2730	     * Otherwise, paintlen is the expanded location of the end
2731	     * of the mark minus the expanded location of the beginning
2732	     * of the mark. */
2733	    if (bot_x >= endpos)
2734		paintlen = -1;
2735	    else
2736		paintlen = strnlenpt(fileptr->data, bot_x) - (x_start +
2737			start);
2738
2739	    /* If x_start is before the beginning of the page, shift
2740	     * paintlen x_start characters to compensate, and put
2741	     * x_start at the beginning of the page. */
2742	    if (x_start < 0) {
2743		paintlen += x_start;
2744		x_start = 0;
2745	    }
2746
2747	    assert(x_start >= 0 && x_start <= strlen(converted));
2748
2749	    index = actual_x(converted, x_start);
2750
2751	    if (paintlen > 0)
2752		paintlen = actual_x(converted + index, paintlen);
2753
2754	    wattron(edit, reverse_attr);
2755	    mvwaddnstr(edit, line, x_start, converted + index,
2756		paintlen);
2757	    wattroff(edit, reverse_attr);
2758	}
2759    }
2760#endif /* !NANO_TINY */
2761}
2762
2763/* Just update one line in the edit buffer.  This is basically a wrapper
2764 * for edit_draw().  The line will be displayed starting with
2765 * fileptr->data[index].  Likely arguments are current_x or zero. */
2766void update_line(const filestruct *fileptr, size_t index)
2767{
2768    int line;
2769	/* The line in the edit window that we want to update. */
2770    char *converted;
2771	/* fileptr->data converted to have tabs and control characters
2772	 * expanded. */
2773    size_t page_start;
2774
2775    assert(fileptr != NULL);
2776
2777    line = fileptr->lineno - openfile->edittop->lineno;
2778
2779    if (line < 0 || line >= editwinrows)
2780	return;
2781
2782    /* First, blank out the line. */
2783    blank_line(edit, line, 0, COLS);
2784
2785    /* Next, convert variables that index the line to their equivalent
2786     * positions in the expanded line. */
2787    index = strnlenpt(fileptr->data, index);
2788    page_start = get_page_start(index);
2789
2790    /* Expand the line, replacing tabs with spaces, and control
2791     * characters with their displayed forms. */
2792    converted = display_string(fileptr->data, page_start, COLS, TRUE);
2793
2794    /* Paint the line. */
2795    edit_draw(fileptr, converted, line, page_start);
2796    free(converted);
2797
2798    if (page_start > 0)
2799	mvwaddch(edit, line, 0, '$');
2800    if (strlenpt(fileptr->data) > page_start + COLS)
2801	mvwaddch(edit, line, COLS - 1, '$');
2802}
2803
2804/* Return TRUE if we need an update after moving horizontally, and FALSE
2805 * otherwise.  We need one if the mark is on or if pww_save and
2806 * placewewant are on different pages. */
2807bool need_horizontal_update(size_t pww_save)
2808{
2809    return
2810#ifndef NANO_TINY
2811	openfile->mark_set ||
2812#endif
2813	get_page_start(pww_save) !=
2814	get_page_start(openfile->placewewant);
2815}
2816
2817/* Return TRUE if we need an update after moving vertically, and FALSE
2818 * otherwise.  We need one if the mark is on or if pww_save and
2819 * placewewant are on different pages. */
2820bool need_vertical_update(size_t pww_save)
2821{
2822    return
2823#ifndef NANO_TINY
2824	openfile->mark_set ||
2825#endif
2826	get_page_start(pww_save) !=
2827	get_page_start(openfile->placewewant);
2828}
2829
2830/* Scroll the edit window in the given direction and the given number
2831 * of lines, and draw new lines on the blank lines left after the
2832 * scrolling.  direction is the direction to scroll, either UP_DIR or
2833 * DOWN_DIR, and nlines is the number of lines to scroll.  We change
2834 * edittop, and assume that current and current_x are up to date.  We
2835 * also assume that scrollok(edit) is FALSE. */
2836void edit_scroll(scroll_dir direction, ssize_t nlines)
2837{
2838    bool do_redraw = need_vertical_update(0);
2839    const filestruct *foo;
2840    ssize_t i;
2841
2842    /* Don't bother scrolling less than one line. */
2843    if (nlines < 1)
2844	return;
2845
2846    /* Part 1: nlines is the number of lines we're going to scroll the
2847     * text of the edit window. */
2848
2849    /* Move the top line of the edit window up or down (depending on the
2850     * value of direction) nlines lines, or as many lines as we can if
2851     * there are fewer than nlines lines available. */
2852    for (i = nlines; i > 0; i--) {
2853	if (direction == UP_DIR) {
2854	    if (openfile->edittop == openfile->fileage)
2855		break;
2856	    openfile->edittop = openfile->edittop->prev;
2857	} else {
2858	    if (openfile->edittop == openfile->filebot)
2859		break;
2860	    openfile->edittop = openfile->edittop->next;
2861	}
2862    }
2863
2864    /* Limit nlines to the number of lines we could scroll. */
2865    nlines -= i;
2866
2867    /* Don't bother scrolling zero lines or more than the number of
2868     * lines in the edit window minus one; in both cases, get out, and
2869     * in the latter case, call edit_refresh() beforehand. */
2870    if (nlines == 0)
2871	return;
2872
2873    if (nlines >= editwinrows) {
2874	edit_refresh();
2875	return;
2876    }
2877
2878    /* Scroll the text of the edit window up or down nlines lines,
2879     * depending on the value of direction. */
2880    scrollok(edit, TRUE);
2881    wscrl(edit, (direction == UP_DIR) ? -nlines : nlines);
2882    scrollok(edit, FALSE);
2883
2884    /* Part 2: nlines is the number of lines in the scrolled region of
2885     * the edit window that we need to draw. */
2886
2887    /* If the top or bottom line of the file is now visible in the edit
2888     * window, we need to draw the entire edit window. */
2889    if ((direction == UP_DIR && openfile->edittop ==
2890	openfile->fileage) || (direction == DOWN_DIR &&
2891	openfile->edittop->lineno + editwinrows - 1 >=
2892	openfile->filebot->lineno))
2893	nlines = editwinrows;
2894
2895    /* If the scrolled region contains only one line, and the line
2896     * before it is visible in the edit window, we need to draw it too.
2897     * If the scrolled region contains more than one line, and the lines
2898     * before and after the scrolled region are visible in the edit
2899     * window, we need to draw them too. */
2900    nlines += (nlines == 1) ? 1 : 2;
2901
2902    if (nlines > editwinrows)
2903	nlines = editwinrows;
2904
2905    /* If we scrolled up, we're on the line before the scrolled
2906     * region. */
2907    foo = openfile->edittop;
2908
2909    /* If we scrolled down, move down to the line before the scrolled
2910     * region. */
2911    if (direction == DOWN_DIR) {
2912	for (i = editwinrows - nlines; i > 0 && foo != NULL; i--)
2913	    foo = foo->next;
2914    }
2915
2916    /* Draw new lines on any blank lines before or inside the scrolled
2917     * region.  If we scrolled down and we're on the top line, or if we
2918     * scrolled up and we're on the bottom line, the line won't be
2919     * blank, so we don't need to draw it unless the mark is on or we're
2920     * not on the first page. */
2921    for (i = nlines; i > 0 && foo != NULL; i--) {
2922	if ((i == nlines && direction == DOWN_DIR) || (i == 1 &&
2923		direction == UP_DIR)) {
2924	    if (do_redraw)
2925		update_line(foo, (foo == openfile->current) ?
2926			openfile->current_x : 0);
2927	} else
2928	    update_line(foo, (foo == openfile->current) ?
2929		openfile->current_x : 0);
2930	foo = foo->next;
2931    }
2932}
2933
2934/* Update any lines between old_current and current that need to be
2935 * updated.  Use this if we've moved without changing any text. */
2936void edit_redraw(const filestruct *old_current, size_t pww_save)
2937{
2938    bool do_redraw = need_vertical_update(0) ||
2939	need_vertical_update(pww_save);
2940    const filestruct *foo = NULL;
2941
2942    /* If either old_current or current is offscreen, scroll the edit
2943     * window until it's onscreen and get out. */
2944    if (old_current->lineno < openfile->edittop->lineno ||
2945	old_current->lineno >= openfile->edittop->lineno +
2946	editwinrows || openfile->current->lineno <
2947	openfile->edittop->lineno || openfile->current->lineno >=
2948	openfile->edittop->lineno + editwinrows) {
2949	filestruct *old_edittop = openfile->edittop;
2950	ssize_t nlines;
2951
2952#ifndef NANO_TINY
2953	/* If the mark is on, update all the lines between old_current
2954	 * and either the old first line or old last line (depending on
2955	 * whether we've scrolled up or down) of the edit window. */
2956	if (openfile->mark_set) {
2957	    ssize_t old_lineno;
2958
2959	    if (old_edittop->lineno < openfile->edittop->lineno)
2960		old_lineno = old_edittop->lineno;
2961	    else
2962		old_lineno = (old_edittop->lineno + editwinrows <=
2963			openfile->filebot->lineno) ?
2964			old_edittop->lineno + editwinrows :
2965			openfile->filebot->lineno;
2966
2967	    foo = old_current;
2968
2969	    while (foo->lineno != old_lineno) {
2970		update_line(foo, 0);
2971
2972		foo = (foo->lineno > old_lineno) ? foo->prev :
2973			foo->next;
2974	    }
2975	}
2976#endif /* !NANO_TINY */
2977
2978	/* Put edittop in range of current, get the difference in lines
2979	 * between the original edittop and the current edittop, and
2980	 * then restore the original edittop. */
2981	edit_update(
2982#ifndef NANO_TINY
2983		ISSET(SMOOTH_SCROLL) ? NONE :
2984#endif
2985		CENTER);
2986
2987	nlines = openfile->edittop->lineno - old_edittop->lineno;
2988
2989	openfile->edittop = old_edittop;
2990
2991	/* Update old_current if we're not on the same page as
2992	 * before. */
2993	if (do_redraw)
2994	    update_line(old_current, 0);
2995
2996	/* Scroll the edit window up or down until edittop is in range
2997	 * of current. */
2998	if (nlines < 0)
2999	    edit_scroll(UP_DIR, -nlines);
3000	else
3001	    edit_scroll(DOWN_DIR, nlines);
3002
3003#ifndef NANO_TINY
3004	/* If the mark is on, update all the lines between the old first
3005	 * line or old last line of the edit window (depending on
3006	 * whether we've scrolled up or down) and current. */
3007	if (openfile->mark_set) {
3008	    while (foo->lineno != openfile->current->lineno) {
3009		update_line(foo, 0);
3010
3011		foo = (foo->lineno > openfile->current->lineno) ?
3012			foo->prev : foo->next;
3013	    }
3014	}
3015#endif /* !NANO_TINY */
3016
3017	return;
3018    }
3019
3020    /* Update old_current and current if we're not on the same page as
3021     * before.  If the mark is on, update all the lines between
3022     * old_current and current too. */
3023    foo = old_current;
3024
3025    while (foo != openfile->current) {
3026	if (do_redraw)
3027	    update_line(foo, 0);
3028
3029#ifndef NANO_TINY
3030	if (!openfile->mark_set)
3031#endif
3032	    break;
3033
3034#ifndef NANO_TINY
3035	foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
3036		foo->next;
3037#endif
3038    }
3039
3040    if (do_redraw)
3041	update_line(openfile->current, openfile->current_x);
3042}
3043
3044/* Refresh the screen without changing the position of lines.  Use this
3045 * if we've moved and changed text. */
3046void edit_refresh(void)
3047{
3048    const filestruct *foo;
3049    int nlines;
3050
3051    if (openfile->current->lineno < openfile->edittop->lineno ||
3052	openfile->current->lineno >= openfile->edittop->lineno +
3053	editwinrows)
3054	/* Put the top line of the edit window in range of the current
3055	 * line. */
3056	edit_update(
3057#ifndef NANO_TINY
3058		ISSET(SMOOTH_SCROLL) ? NONE :
3059#endif
3060		CENTER);
3061
3062    foo = openfile->edittop;
3063
3064#ifdef DEBUG
3065    fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3066#endif
3067
3068    for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3069	update_line(foo, (foo == openfile->current) ?
3070		openfile->current_x : 0);
3071	foo = foo->next;
3072    }
3073
3074    for (; nlines < editwinrows; nlines++)
3075	blank_line(edit, nlines, 0, COLS);
3076
3077    reset_cursor();
3078
3079    wnoutrefresh(edit);
3080}
3081
3082/* Move edittop to put it in range of current, keeping current in the
3083 * same place.  location determines how we move it: if it's CENTER, we
3084 * center current, and if it's NONE, we put current current_y lines
3085 * below edittop. */
3086void edit_update(update_type location)
3087{
3088    filestruct *foo = openfile->current;
3089    int goal;
3090
3091    /* If location is CENTER, we move edittop up (editwinrows / 2)
3092     * lines.  This puts current at the center of the screen.  If
3093     * location is NONE, we move edittop up current_y lines if current_y
3094     * is in range of the screen, 0 lines if current_y is less than 0,
3095     * or (editwinrows - 1) lines if current_y is greater than
3096     * (editwinrows - 1).  This puts current at the same place on the
3097     * screen as before, or at the top or bottom of the screen if
3098     * edittop is beyond either. */
3099    if (location == CENTER)
3100	goal = editwinrows / 2;
3101    else {
3102	goal = openfile->current_y;
3103
3104	/* Limit goal to (editwinrows - 1) lines maximum. */
3105	if (goal > editwinrows - 1)
3106	    goal = editwinrows - 1;
3107    }
3108
3109    for (; goal > 0 && foo->prev != NULL; goal--)
3110	foo = foo->prev;
3111
3112    openfile->edittop = foo;
3113}
3114
3115/* Unconditionally redraw the entire screen. */
3116void total_redraw(void)
3117{
3118#ifdef USE_SLANG
3119    /* Slang curses emulation brain damage, part 4: Slang doesn't define
3120     * curscr. */
3121    SLsmg_touch_screen();
3122    SLsmg_refresh();
3123#else
3124    wrefresh(curscr);
3125#endif
3126}
3127
3128/* Unconditionally redraw the entire screen, and then refresh it using
3129 * the current file. */
3130void total_refresh(void)
3131{
3132    total_redraw();
3133    titlebar(NULL);
3134    edit_refresh();
3135    bottombars(currshortcut);
3136}
3137
3138/* Display the main shortcut list on the last two rows of the bottom
3139 * portion of the window. */
3140void display_main_list(void)
3141{
3142    bottombars(main_list);
3143}
3144
3145/* If constant is TRUE, we display the current cursor position only if
3146 * disable_cursorpos is FALSE.  Otherwise, we display it
3147 * unconditionally and set disable_cursorpos to FALSE.  If constant is
3148 * TRUE and disable_cursorpos is TRUE, we also set disable_cursorpos to
3149 * FALSE, so that we leave the current statusbar alone this time, and
3150 * display the current cursor position next time. */
3151void do_cursorpos(bool constant)
3152{
3153    filestruct *f;
3154    char c;
3155    size_t i, cur_xpt = xplustabs() + 1;
3156    size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3157    int linepct, colpct, charpct;
3158
3159    assert(openfile->fileage != NULL && openfile->current != NULL);
3160
3161    f = openfile->current->next;
3162    c = openfile->current->data[openfile->current_x];
3163
3164    openfile->current->next = NULL;
3165    openfile->current->data[openfile->current_x] = '\0';
3166
3167    i = get_totsize(openfile->fileage, openfile->current);
3168
3169    openfile->current->data[openfile->current_x] = c;
3170    openfile->current->next = f;
3171
3172    if (constant && disable_cursorpos) {
3173	disable_cursorpos = FALSE;
3174	return;
3175    }
3176
3177    /* Display the current cursor position on the statusbar, and set
3178     * disable_cursorpos to FALSE. */
3179    linepct = 100 * openfile->current->lineno /
3180	openfile->filebot->lineno;
3181    colpct = 100 * cur_xpt / cur_lenpt;
3182    charpct = (openfile->totsize == 0) ? 0 : 100 * i /
3183	openfile->totsize;
3184
3185    statusbar(
3186	_("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3187	(long)openfile->current->lineno,
3188	(long)openfile->filebot->lineno, linepct,
3189	(unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3190	(unsigned long)i, (unsigned long)openfile->totsize, charpct);
3191
3192    disable_cursorpos = FALSE;
3193}
3194
3195/* Unconditionally display the current cursor position. */
3196void do_cursorpos_void(void)
3197{
3198    do_cursorpos(FALSE);
3199}
3200
3201/* Highlight the current word being replaced or spell checked.  We
3202 * expect word to have tabs and control characters expanded. */
3203void do_replace_highlight(bool highlight, const char *word)
3204{
3205    size_t y = xplustabs(), word_len = strlenpt(word);
3206
3207    y = get_page_start(y) + COLS - y;
3208	/* Now y is the number of columns that we can display on this
3209	 * line. */
3210
3211    assert(y > 0);
3212
3213    if (word_len > y)
3214	y--;
3215
3216    reset_cursor();
3217
3218    if (highlight)
3219	wattron(edit, reverse_attr);
3220
3221    /* This is so we can show zero-length matches. */
3222    if (word_len == 0)
3223	waddch(edit, ' ');
3224    else
3225	waddnstr(edit, word, actual_x(word, y));
3226
3227    if (word_len > y)
3228	waddch(edit, '$');
3229
3230    if (highlight)
3231	wattroff(edit, reverse_attr);
3232}
3233
3234#ifdef NANO_EXTRA
3235#define CREDIT_LEN 55
3236#define XLCREDIT_LEN 8
3237
3238/* Easter egg: Display credits.  Assume nodelay(edit) and scrollok(edit)
3239 * are FALSE. */
3240void do_credits(void)
3241{
3242    bool old_more_space = ISSET(MORE_SPACE);
3243    bool old_no_help = ISSET(NO_HELP);
3244    int kbinput = ERR, crpos = 0, xlpos = 0;
3245    const char *credits[CREDIT_LEN] = {
3246	NULL,				/* "The nano text editor" */
3247	NULL,				/* "version" */
3248	VERSION,
3249	"",
3250	NULL,				/* "Brought to you by:" */
3251	"Chris Allegretta",
3252	"Jordi Mallach",
3253	"Adam Rogoyski",
3254	"Rob Siemborski",
3255	"Rocco Corsi",
3256	"David Lawrence Ramsey",
3257	"David Benbennick",
3258	"Mike Frysinger",
3259	"Ken Tyler",
3260	"Sven Guckes",
3261	NULL,				/* credits[15], handled below. */
3262	"Pauli Virtanen",
3263	"Daniele Medri",
3264	"Clement Laforet",
3265	"Tedi Heriyanto",
3266	"Bill Soudan",
3267	"Christian Weisgerber",
3268	"Erik Andersen",
3269	"Big Gaute",
3270	"Joshua Jensen",
3271	"Ryan Krebs",
3272	"Albert Chin",
3273	"",
3274	NULL,				/* "Special thanks to:" */
3275	"Plattsburgh State University",
3276	"Benet Laboratories",
3277	"Amy Allegretta",
3278	"Linda Young",
3279	"Jeremy Robichaud",
3280	"Richard Kolb II",
3281	NULL,				/* "The Free Software Foundation" */
3282	"Linus Torvalds",
3283	NULL,				/* "For ncurses:" */
3284	"Thomas Dickey",
3285	"Pavel Curtis",
3286	"Zeyd Ben-Halim",
3287	"Eric S. Raymond",
3288	NULL,				/* "and anyone else we forgot..." */
3289	NULL,				/* "Thank you for using nano!" */
3290	"",
3291	"",
3292	"",
3293	"",
3294	"(c) 1999, 2000, 2001, 2002, 2003, 2004 Chris Allegretta",
3295	"(c) 2005, 2006, 2007 David Lawrence Ramsey",
3296	"",
3297	"",
3298	"",
3299	"",
3300	"http://www.nano-editor.org/"
3301    };
3302
3303    const char *xlcredits[XLCREDIT_LEN] = {
3304	N_("The nano text editor"),
3305	N_("version"),
3306	N_("Brought to you by:"),
3307	N_("Special thanks to:"),
3308	N_("The Free Software Foundation"),
3309	N_("For ncurses:"),
3310	N_("and anyone else we forgot..."),
3311	N_("Thank you for using nano!")
3312    };
3313
3314    /* credits[15]: Make sure this name is displayed properly, since we
3315     * can't dynamically assign it above, using Unicode 00F6 (Latin
3316     * Small Letter O with Diaresis) if applicable. */
3317    credits[15] =
3318#ifdef ENABLE_UTF8
3319	 using_utf8() ? "Florian K\xC3\xB6nig" :
3320#endif
3321	"Florian K\xF6nig";
3322
3323    if (!old_more_space || !old_no_help) {
3324	SET(MORE_SPACE);
3325	SET(NO_HELP);
3326	window_init();
3327    }
3328
3329    curs_set(0);
3330    nodelay(edit, TRUE);
3331
3332    blank_titlebar();
3333    blank_topbar();
3334    blank_edit();
3335    blank_statusbar();
3336    blank_bottombars();
3337
3338    wrefresh(topwin);
3339    wrefresh(edit);
3340    wrefresh(bottomwin);
3341    napms(700);
3342
3343    for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3344	if ((kbinput = wgetch(edit)) != ERR)
3345	    break;
3346
3347	if (crpos < CREDIT_LEN) {
3348	    const char *what;
3349	    size_t start_x;
3350
3351	    if (credits[crpos] == NULL) {
3352		assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3353
3354		what = _(xlcredits[xlpos]);
3355		xlpos++;
3356	    } else
3357		what = credits[crpos];
3358
3359	    start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3360	    mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
3361		start_x, what);
3362	}
3363
3364	wrefresh(edit);
3365
3366	if ((kbinput = wgetch(edit)) != ERR)
3367	    break;
3368	napms(700);
3369
3370	scrollok(edit, TRUE);
3371	wscrl(edit, 1);
3372	scrollok(edit, FALSE);
3373	wrefresh(edit);
3374
3375	if ((kbinput = wgetch(edit)) != ERR)
3376	    break;
3377	napms(700);
3378
3379	scrollok(edit, TRUE);
3380	wscrl(edit, 1);
3381	scrollok(edit, FALSE);
3382	wrefresh(edit);
3383    }
3384
3385    if (kbinput != ERR)
3386	ungetch(kbinput);
3387
3388    if (!old_more_space || !old_no_help) {
3389	UNSET(MORE_SPACE);
3390	UNSET(NO_HELP);
3391	window_init();
3392    }
3393
3394    curs_set(1);
3395    nodelay(edit, FALSE);
3396
3397    total_refresh();
3398}
3399#endif /* NANO_EXTRA */
3400