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