Add Nano tool - user-friendly text editor
[tomato.git] / release / src / router / nano / src / winio.c
blob1aef2a9b8c43b6c274282c77b7c0395a113fa67f
1 /* $Id: winio.c 4484 2010-03-07 19:35:46Z 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 **************************************************************************/
24 #include "proto.h"
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <ctype.h>
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? */
44 /* Control character compatibility:
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.
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.
70 * Escape sequence compatibility:
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:
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.) */
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)
108 int input;
109 size_t errcount;
111 /* If the keystroke buffer isn't empty, get out. */
112 if (key_buffer != NULL)
113 return;
115 /* Read in the first character using blocking input. */
116 #ifndef NANO_TINY
117 allow_pending_sigwinch(TRUE);
118 #endif
120 /* Just before reading in the first character, display any pending
121 * screen updates. */
122 doupdate();
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++;
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);
141 #ifndef NANO_TINY
142 allow_pending_sigwinch(FALSE);
143 #endif
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;
151 /* Read in the remaining characters using non-blocking input. */
152 nodelay(win, TRUE);
154 while (TRUE) {
155 #ifndef NANO_TINY
156 allow_pending_sigwinch(TRUE);
157 #endif
159 input = wgetch(win);
161 /* If there aren't any more characters, stop reading. */
162 if (input == ERR)
163 break;
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;
172 #ifndef NANO_TINY
173 allow_pending_sigwinch(FALSE);
174 #endif
177 /* Switch back to non-blocking input. */
178 nodelay(win, FALSE);
180 #ifdef DEBUG
181 fprintf(stderr, "get_key_buffer(): key_buffer_len = %lu\n", (unsigned long)key_buffer_len);
182 #endif
185 /* Return the length of the keystroke buffer. */
186 size_t get_key_buffer_len(void)
188 return key_buffer_len;
191 /* Add the keystrokes in input to the keystroke buffer. */
192 void unget_input(int *input, size_t input_len)
194 #ifndef NANO_TINY
195 allow_pending_sigwinch(TRUE);
196 allow_pending_sigwinch(FALSE);
197 #endif
199 /* If input is empty, get out. */
200 if (input_len == 0)
201 return;
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;
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));
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));
222 /* Copy input to the beginning of the keystroke buffer. */
223 memcpy(key_buffer, input, input_len * sizeof(int));
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)
232 if (!func_key)
233 kbinput = (char)kbinput;
235 unget_input(&kbinput, 1);
237 if (meta_key) {
238 kbinput = NANO_CONTROL_3;
239 unget_input(&kbinput, 1);
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)
250 int *input;
252 #ifndef NANO_TINY
253 allow_pending_sigwinch(TRUE);
254 allow_pending_sigwinch(FALSE);
255 #endif
257 if (key_buffer_len == 0) {
258 if (win != NULL) {
259 get_key_buffer(win);
261 if (key_buffer_len == 0)
262 return NULL;
263 } else
264 return NULL;
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;
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));
278 /* Copy input_len keystrokes from the beginning of the keystroke
279 * buffer into input. */
280 memcpy(input, key_buffer, input_len * sizeof(int));
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));
296 return input;
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)
310 int kbinput;
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);
316 /* If we read from the edit window, blank the statusbar if we need
317 * to. */
318 if (win == edit)
319 check_statusblank();
321 return kbinput;
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)
330 static int escapes = 0, byte_digits = 0;
331 int *kbinput, retval = ERR;
333 *meta_key = FALSE;
334 *func_key = FALSE;
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);
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;
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;
408 byte_digits++;
409 byte = get_byte_kbinput(*kbinput);
411 if (byte != ERR) {
412 char *byte_mb;
413 int byte_mb_len, *seq, i;
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;
423 /* Put back the multibyte equivalent of
424 * the byte value. */
425 byte_mb = make_mbchar((long)byte,
426 &byte_mb_len);
428 seq = (int *)nmalloc(byte_mb_len *
429 sizeof(int));
431 for (i = 0; i < byte_mb_len; i++)
432 seq[i] = (unsigned char)byte_mb[i];
434 unget_input(seq, byte_mb_len);
436 free(byte_mb);
437 free(seq);
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;
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);
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;
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, *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, CANCEL_MSG)->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
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);
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
647 free(kbinput);
649 /* Return the result. */
650 return retval;
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)
659 int retval = ERR;
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;
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;
718 break;
721 break;
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;
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;
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;
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;
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;
977 break;
980 break;
981 default: /* Esc [ 1 ~ == Home on
982 * VT320/Linux console. */
983 retval = sc_seq_or(DO_HOME, 0);;
984 break;
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;
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;
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;
1203 break;
1205 break;
1209 #ifdef DEBUG
1210 fprintf(stderr, "get_escape_seq_kbinput(): retval = %d\n", retval);
1211 #endif
1213 return retval;
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)
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;
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)
1240 int retval, *seq;
1241 size_t seq_len;
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);
1251 free(seq);
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();
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
1265 return retval;
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)
1272 static int byte_digits = 0, byte = 0;
1273 int retval = ERR;
1275 /* Increment the byte digit counter. */
1276 byte_digits++;
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;
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;
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
1338 return retval;
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)
1346 long retval = ERR;
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;
1357 return retval;
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)
1365 static int uni_digits = 0;
1366 static long uni = 0;
1367 long retval = ERR;
1369 /* Increment the Unicode digit counter. */
1370 uni_digits++;
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;
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;
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
1440 return retval;
1442 #endif /* ENABLE_UTF8 */
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)
1448 int retval;
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;
1471 #ifdef DEBUG
1472 fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval);
1473 #endif
1475 return retval;
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)
1482 int *input;
1483 size_t i;
1485 if (output_len == 0)
1486 return;
1488 input = (int *)nmalloc(output_len * sizeof(int));
1490 for (i = 0; i < output_len; i++)
1491 input[i] = (int)output[i];
1493 unget_input(input, output_len);
1495 free(input);
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)
1502 int *retval;
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);
1512 /* Read in a stream of characters and interpret it if possible. */
1513 retval = parse_verbatim_kbinput(win, kbinput_len);
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);
1522 return retval;
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)
1531 int *kbinput, *retval;
1533 /* Read in the first keystroke. */
1534 while ((kbinput = get_input(win, 1)) == NULL);
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);
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);
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;
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"));
1559 while (uni == ERR) {
1560 while ((kbinput = get_input(win, 1)) == NULL);
1562 uni = get_unicode_kbinput(*kbinput);
1565 /* Put back the multibyte equivalent of the Unicode
1566 * value. */
1567 uni_mb = make_mbchar(uni, &uni_mb_len);
1569 seq = (int *)nmalloc(uni_mb_len * sizeof(int));
1571 for (i = 0; i < uni_mb_len; i++)
1572 seq[i] = (unsigned char)uni_mb[i];
1574 unget_input(seq, uni_mb_len);
1576 free(seq);
1577 free(uni_mb);
1579 } else
1580 #endif /* ENABLE_UTF8 */
1582 /* Put back the first keystroke. */
1583 unget_input(kbinput, 1);
1585 free(kbinput);
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);
1592 return retval;
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)
1612 MEVENT mevent;
1613 bool in_bottomwin;
1614 subnfunc *f;
1616 *mouse_x = -1;
1617 *mouse_y = -1;
1619 /* First, get the actual mouse event. */
1620 if (getmouse(&mevent) == ERR)
1621 return -1;
1623 /* Save the screen coordinates where the mouse event took place. */
1624 *mouse_x = mevent.x;
1625 *mouse_y = mevent.y;
1627 in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);
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. */
1647 /* Translate the mouse event coordinates so that they're
1648 * relative to bottomwin. */
1649 wmouse_trafo(bottomwin, mouse_y, mouse_x, FALSE);
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;
1659 return 0;
1662 /* Calculate the y-coordinate relative to the beginning of
1663 * the shortcut list in bottomwin. */
1664 j = *mouse_y - 1;
1666 /* Get the shortcut lists' length. */
1667 if (currmenu == MMAIN)
1668 currslen = MAIN_VISIBLE;
1669 else {
1670 currslen = length_of_list(currmenu);
1672 /* We don't show any more shortcuts than the main list
1673 * does. */
1674 if (currslen > MAIN_VISIBLE)
1675 currslen = MAIN_VISIBLE;
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));
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;
1692 /* Adjust j if we released on the last two shortcuts. */
1693 if ((j >= currslen) && (*mouse_x % i < COLS % i))
1694 j -= 2;
1696 /* Ignore releases/clicks of the first mouse button beyond
1697 * the last shortcut. */
1698 if (j >= currslen)
1699 return 2;
1701 /* Go through the shortcut list to determine which shortcut
1702 * we released/clicked on. */
1703 f = allfuncs;
1705 for (; j > 0; j--) {
1706 if (f->next != NULL)
1707 f = f->next;
1709 while (f->next != NULL && ((f->menus & currmenu) == 0
1710 #ifndef DISABLE_HELP
1711 || strlen(f->help) == 0
1712 #endif
1714 f = f->next;
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);
1724 } else
1725 /* Handle releases/clicks of the first mouse button that
1726 * aren't on the current shortcut list elsewhere. */
1727 return 0;
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);
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);
1741 if (in_edit || (in_bottomwin && *mouse_y == 0)) {
1742 int i;
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(DO_UP_VOID, 0) : sc_seq_or(DO_DOWN_VOID, 0), FALSE,
1750 FALSE);
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;
1759 #endif
1761 /* Ignore all other mouse events. */
1762 return 2;
1764 #endif /* !DISABLE_MOUSE */
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)
1777 sc *s;
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
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;
1794 #ifdef DEBUG
1795 fprintf (stderr, "matched nothing btw meta was %d\n", *meta_key);
1796 #endif
1798 return NULL;
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)
1807 int kbinput;
1808 bool func_key = FALSE, meta_key = FALSE;
1809 const sc *s;
1810 const subnfunc *f;
1812 kbinput = parse_kbinput(win, &meta_key, &func_key);
1813 if (kbinput == 0)
1814 return NULL;
1816 s = get_shortcut(currmenu, &kbinput, &meta_key, &func_key);
1817 if (!s)
1818 return NULL;
1820 f = sctofunc((sc *) s);
1821 return f;
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)
1831 wmove(win, y, x);
1833 for (; n > 0; n--)
1834 waddch(win, ' ');
1837 /* Blank the first line of the top portion of the window. */
1838 void blank_titlebar(void)
1840 blank_line(topwin, 0, 0, COLS);
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)
1847 if (!ISSET(MORE_SPACE))
1848 blank_line(topwin, 1, 0, COLS);
1851 /* Blank all the lines of the middle portion of the window, i.e. the
1852 * edit window. */
1853 void blank_edit(void)
1855 int i;
1857 for (i = 0; i < editwinrows; i++)
1858 blank_line(edit, i, 0, COLS);
1861 /* Blank the first line of the bottom portion of the window. */
1862 void blank_statusbar(void)
1864 blank_line(bottomwin, 0, 0, COLS);
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)
1871 if (!ISSET(NO_HELP)) {
1872 blank_line(bottomwin, 1, 0, COLS);
1873 blank_line(bottomwin, 2, 0, COLS);
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)
1882 if (statusblank > 0) {
1883 statusblank--;
1885 if (statusblank == 0 && !ISSET(CONST_UPDATE)) {
1886 blank_statusbar();
1887 wnoutrefresh(bottomwin);
1888 reset_cursor();
1889 wnoutrefresh(edit);
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)
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;
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--;
1922 if (len == 0)
1923 return mallocstrcpy(NULL, "");
1925 buf_mb = charalloc(mb_cur_max());
1927 start_index = actual_x(buf, start_col);
1928 column = strnlenpt(buf, start_index);
1930 assert(column <= start_col);
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:
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')
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);
1948 index = 0;
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);
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;
1961 ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
1962 &ctrl_buf_mb_len);
1964 for (i = 0; i < ctrl_buf_mb_len; i++)
1965 converted[index++] = ctrl_buf_mb[i];
1967 start_col += mbwidth(ctrl_buf_mb);
1969 free(ctrl_buf_mb);
1971 start_index += buf_mb_len;
1974 #ifdef ENABLE_UTF8
1975 else if (using_utf8() && mbwidth(buf_mb) == 2) {
1976 if (column >= start_col) {
1977 converted[index++] = ' ';
1978 start_col++;
1981 converted[index++] = ' ';
1982 start_col++;
1984 start_index += buf_mb_len;
1986 #endif
1989 while (buf[start_index] != '\0') {
1990 buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL);
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);
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;
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++;
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;
2023 converted[index++] = '^';
2024 start_col++;
2026 ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb,
2027 &ctrl_buf_mb_len);
2029 for (i = 0; i < ctrl_buf_mb_len; i++)
2030 converted[index++] = ctrl_buf_mb[i];
2032 start_col += mbwidth(ctrl_buf_mb);
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;
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;
2055 nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb,
2056 &nctrl_buf_mb_len);
2058 for (i = 0; i < nctrl_buf_mb_len; i++)
2059 converted[index++] = nctrl_buf_mb[i];
2061 start_col += mbwidth(nctrl_buf_mb);
2063 free(nctrl_buf_mb);
2066 start_index += buf_mb_len;
2069 free(buf_mb);
2071 assert(alloc_len >= index + 1);
2073 /* Null-terminate converted. */
2074 converted[index] = '\0';
2076 /* Make sure converted takes up no more than len columns. */
2077 index = actual_x(converted, len);
2078 null_at(&converted, index);
2080 return converted;
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)
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? */
2114 assert(path != NULL || openfile->filename != NULL);
2116 wattron(topwin, reverse_attr);
2118 blank_titlebar();
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;
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;
2139 /* Account for the full length of the version message. */
2140 space -= verlen;
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") : "";
2152 statelen = strlenpt((*state == '\0' && path == NULL) ?
2153 _("Modified") : state);
2155 /* If possible, add a space before state. */
2156 if (space > 0 && statelen < space)
2157 statelen++;
2158 else
2159 goto the_end;
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:");
2173 prefixlen = strnlenpt(prefix, space - statelen) + 1;
2175 /* If newfie is FALSE, add a space after prefix. */
2176 if (!newfie && prefixlen + statelen < space)
2177 prefixlen++;
2179 /* If we're not in the file browser, set path to the current
2180 * filename. */
2181 if (path == NULL)
2182 path = openfile->filename;
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. */
2191 if (!newfie) {
2192 size_t lenpt = strlenpt(path), start_col;
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);
2199 if (dots) {
2200 start_col = lenpt - space + 3;
2201 space -= 3;
2202 } else
2203 start_col = 0;
2205 exppath = display_string(path, start_col, space, FALSE);
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. */
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);
2233 the_end:
2234 free(exppath);
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);
2242 mvwaddnstr(topwin, 0, COLS - statelen - 1, state,
2243 actual_x(state, statelen));
2247 wattroff(topwin, reverse_attr);
2249 wnoutrefresh(topwin);
2250 reset_cursor();
2251 wnoutrefresh(edit);
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)
2258 if (!openfile->modified) {
2259 openfile->modified = TRUE;
2260 titlebar(NULL);
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, ...)
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
2276 va_start(ap, msg);
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;
2286 blank_statusbar();
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;
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. */
2317 disable_cursorpos = TRUE;
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
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)
2334 size_t i, colwidth, slen;
2335 subnfunc *f;
2336 const sc *s;
2338 if (ISSET(NO_HELP))
2339 return;
2341 if (menu == MMAIN) {
2342 slen = MAIN_VISIBLE;
2344 assert(slen <= length_of_list(menu));
2345 } else {
2346 slen = length_of_list(menu);
2348 /* Don't show any more shortcuts than the main list does. */
2349 if (slen > MAIN_VISIBLE)
2350 slen = MAIN_VISIBLE;
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));
2359 blank_bottombars();
2361 #ifdef DEBUG
2362 fprintf(stderr, "In bottombars, and slen == \"%d\"\n", (int) slen);
2363 #endif
2365 for (f = allfuncs, i = 0; i < slen && f != NULL; f = f->next) {
2367 #ifdef DEBUG
2368 fprintf(stderr, "Checking menu items....");
2369 #endif
2370 if ((f->menus & menu) == 0)
2371 continue;
2373 if (!f->desc || strlen(f->desc) == 0)
2374 continue;
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;
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++;
2394 wnoutrefresh(bottomwin);
2395 reset_cursor();
2396 wnoutrefresh(edit);
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)
2406 size_t keystroke_len = strlenpt(keystroke) + 1;
2408 assert(keystroke != NULL && desc != NULL);
2410 wattron(bottomwin, reverse_attr);
2411 waddnstr(bottomwin, keystroke, actual_x(keystroke, len));
2412 wattroff(bottomwin, reverse_attr);
2414 if (len > keystroke_len)
2415 len -= keystroke_len;
2416 else
2417 len = 0;
2419 if (len > 0) {
2420 waddch(bottomwin, ' ');
2421 waddnstr(bottomwin, desc, actual_x(desc, len));
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)
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;
2437 xpt = xplustabs();
2439 if (ISSET(SOFTWRAP)) {
2440 filestruct *tmp;
2441 openfile->current_y = 0;
2443 for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next)
2444 openfile->current_y += 1 + strlenpt(tmp->data) / COLS;
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;
2453 if (openfile->current_y < editwinrows)
2454 wmove(edit, openfile->current_y, xpt - get_page_start(xpt));
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)
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.
2477 * Note that endpos might be beyond the null terminator of the
2478 * string. */
2479 #endif
2481 assert(openfile != NULL && fileptr != NULL && converted != NULL);
2482 assert(strlenpt(converted) <= COLS);
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);
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;
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 */
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. */
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. */
2522 /* First case, tmpcolor is a single-line expression. */
2523 if (tmpcolor->end == NULL) {
2524 size_t k = 0;
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;
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;
2554 index = actual_x(converted, x_start);
2556 paintlen = actual_x(converted + index,
2557 strnlenpt(fileptr->data,
2558 startmatch.rm_eo) - start - x_start);
2560 assert(0 <= x_start && 0 <= paintlen);
2562 mvwaddnstr(edit, line, x_start, converted +
2563 index, paintlen);
2565 k = startmatch.rm_eo;
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.
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];
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;
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;
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;
2639 /* Indeed, there is a start not followed on this
2640 * line by an end. */
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;
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;
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;
2672 mvwaddnstr(edit, line, 0, converted, paintlen);
2673 step_two:
2674 /* Second step, we look for starts on this line. */
2675 start_col = 0;
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;
2689 x_start = (startmatch.rm_so <= startpos) ? 0 :
2690 strnlenpt(fileptr->data,
2691 startmatch.rm_so) - start;
2693 index = actual_x(converted, x_start);
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);
2713 assert(0 <= x_start && x_start < COLS);
2715 mvwaddnstr(edit, line, x_start,
2716 converted + index, paintlen);
2717 if (paintlen > 0)
2718 fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
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;
2727 while (end_line != NULL &&
2728 regexec(tmpcolor->end, end_line->data,
2729 0, NULL, 0) == REG_NOMATCH)
2730 end_line = end_line->next;
2732 if (end_line != NULL) {
2733 assert(0 <= x_start && x_start < COLS);
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;
2744 start_col = startmatch.rm_so + 1;
2749 wattroff(edit, A_BOLD);
2750 wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
2753 #endif /* ENABLE_COLOR */
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. */
2777 mark_order(&top, &top_x, &bot, &bot_x, NULL);
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;
2784 /* The selected bit of fileptr is on this page. */
2785 if (top_x < endpos && bot_x > startpos) {
2786 assert(startpos <= top_x);
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;
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);
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;
2811 assert(x_start >= 0 && x_start <= strlen(converted));
2813 index = actual_x(converted, x_start);
2815 if (paintlen > 0)
2816 paintlen = actual_x(converted + index, paintlen);
2818 wattron(edit, reverse_attr);
2819 mvwaddnstr(edit, line, x_start, converted + index,
2820 paintlen);
2821 wattroff(edit, reverse_attr);
2824 #endif /* !NANO_TINY */
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)
2832 int update_line(filestruct *fileptr, size_t index)
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;
2843 assert(fileptr != NULL);
2845 if (ISSET(SOFTWRAP)) {
2846 for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next) {
2847 line += 1 + (strlenpt(tmp->data) / COLS);
2849 } else
2850 line = fileptr->lineno - openfile->edittop->lineno;
2852 if (line < 0 || line >= editwinrows)
2853 return 1;
2855 /* First, blank out the line. */
2856 blank_line(edit, line, 0, COLS);
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);
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));
2870 #ifdef DEBUG
2871 if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
2872 fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
2873 #endif
2876 /* Paint the line. */
2877 edit_draw(fileptr, converted, line, page_start);
2878 free(converted);
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);
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
2902 /* Paint the line. */
2903 edit_draw(fileptr, converted, line, index);
2904 free(converted);
2905 extralinesused++;
2908 return extralinesused;
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)
2916 return
2917 #ifndef NANO_TINY
2918 openfile->mark_set ||
2919 #endif
2920 get_page_start(pww_save) !=
2921 get_page_start(openfile->placewewant);
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)
2929 return
2930 #ifndef NANO_TINY
2931 openfile->mark_set ||
2932 #endif
2933 get_page_start(pww_save) !=
2934 get_page_start(openfile->placewewant);
2937 /* When edittop changes, try and figure out how many lines
2938 * we really have to work with (i.e. set maxrows)
2940 void compute_maxrows(void)
2942 int n;
2943 filestruct *foo = openfile->edittop;
2945 if (!ISSET(SOFTWRAP)) {
2946 maxrows = editwinrows;
2947 return;
2950 maxrows = 0;
2951 for (n = 0; n < editwinrows && foo; n++) {
2952 maxrows ++;
2953 n += strlenpt(foo->data) / COLS;
2954 foo = foo->next;
2957 if (n < editwinrows)
2958 maxrows += editwinrows - n;
2960 #ifdef DEBUG
2961 fprintf(stderr, "compute_maxrows(): maxrows = %ld\n", maxrows);
2962 #endif
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)
2973 filestruct *foo;
2974 ssize_t i, extracuzsoft = 0;
2975 bool do_redraw = FALSE;
2977 /* Don't bother scrolling less than one line. */
2978 if (nlines < 1)
2979 return;
2981 if (need_vertical_update(0))
2982 do_redraw = TRUE;
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)
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
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;
3014 /* Part 1: nlines is the number of lines we're going to scroll the
3015 * text of the edit window. */
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;
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;
3039 /* Limit nlines to the number of lines we could scroll. */
3040 nlines -= i;
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;
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);
3057 /* Part 2: nlines is the number of lines in the scrolled region of
3058 * the edit window that we need to draw. */
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;
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;
3075 if (nlines > editwinrows)
3076 nlines = editwinrows;
3078 /* If we scrolled up, we're on the line before the scrolled
3079 * region. */
3080 foo = openfile->edittop;
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;
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;
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)
3111 bool do_redraw = need_vertical_update(0) ||
3112 need_vertical_update(pww_save);
3113 filestruct *foo = NULL;
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) {
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 ssize_t nlines;
3130 #ifndef NANO_TINY
3131 /* If the mark is on, update all the lines between old_current
3132 * and either the old first line or old last line (depending on
3133 * whether we've scrolled up or down) of the edit window. */
3134 if (openfile->mark_set) {
3135 ssize_t old_lineno;
3137 if (old_edittop->lineno < openfile->edittop->lineno)
3138 old_lineno = old_edittop->lineno;
3139 else
3140 old_lineno = (old_edittop->lineno + maxrows <=
3141 openfile->filebot->lineno) ?
3142 old_edittop->lineno + editwinrows :
3143 openfile->filebot->lineno;
3145 foo = old_current;
3147 while (foo->lineno != old_lineno) {
3148 update_line(foo, 0);
3150 foo = (foo->lineno > old_lineno) ? foo->prev :
3151 foo->next;
3154 #endif /* !NANO_TINY */
3156 /* Put edittop in range of current, get the difference in lines
3157 * between the original edittop and the current edittop, and
3158 * then restore the original edittop. */
3159 edit_update(CENTER);
3161 /* Update old_current if we're not on the same page as
3162 * before. */
3163 if (do_redraw)
3164 update_line(old_current, 0);
3166 #ifndef NANO_TINY
3167 /* If the mark is on, update all the lines between the old first
3168 * line or old last line of the edit window (depending on
3169 * whether we've scrolled up or down) and current. */
3170 if (openfile->mark_set) {
3171 while (foo->lineno != openfile->current->lineno) {
3172 update_line(foo, 0);
3174 foo = (foo->lineno > openfile->current->lineno) ?
3175 foo->prev : foo->next;
3178 #endif /* !NANO_TINY */
3180 return;
3183 /* Update old_current and current if we're not on the same page as
3184 * before. If the mark is on, update all the lines between
3185 * old_current and current too. */
3186 foo = old_current;
3188 while (foo != openfile->current) {
3189 if (do_redraw)
3190 update_line(foo, 0);
3192 #ifndef NANO_TINY
3193 if (!openfile->mark_set)
3194 #endif
3195 break;
3197 #ifndef NANO_TINY
3198 foo = (foo->lineno > openfile->current->lineno) ? foo->prev :
3199 foo->next;
3200 #endif
3203 if (do_redraw)
3204 update_line(openfile->current, openfile->current_x);
3207 /* Refresh the screen without changing the position of lines. Use this
3208 * if we've moved and changed text. */
3209 void edit_refresh(void)
3211 filestruct *foo;
3212 int nlines;
3214 /* Figure out what maxrows should really be */
3215 compute_maxrows();
3217 if (openfile->current->lineno < openfile->edittop->lineno ||
3218 openfile->current->lineno >= openfile->edittop->lineno +
3219 maxrows) {
3221 #ifdef DEBUG
3222 fprintf(stderr, "edit_refresh(): line = %d, edittop %d + maxrows %d\n", openfile->current->lineno, openfile->edittop->lineno, maxrows);
3223 #endif
3225 /* Put the top line of the edit window in range of the current
3226 * line. */
3227 edit_update(CENTER);
3230 foo = openfile->edittop;
3232 #ifdef DEBUG
3233 fprintf(stderr, "edit_refresh(): edittop->lineno = %ld\n", (long)openfile->edittop->lineno);
3234 #endif
3236 for (nlines = 0; nlines < editwinrows && foo != NULL; nlines++) {
3237 nlines += update_line(foo, (foo == openfile->current) ?
3238 openfile->current_x : 0);
3239 foo = foo->next;
3242 for (; nlines < editwinrows; nlines++)
3243 blank_line(edit, nlines, 0, COLS);
3245 reset_cursor();
3246 wnoutrefresh(edit);
3249 /* Move edittop to put it in range of current, keeping current in the
3250 * same place. location determines how we move it: if it's CENTER, we
3251 * center current, and if it's NONE, we put current current_y lines
3252 * below edittop. */
3253 void edit_update(update_type location)
3255 filestruct *foo = openfile->current;
3256 int goal;
3258 /* If location is CENTER, we move edittop up (editwinrows / 2)
3259 * lines. This puts current at the center of the screen. If
3260 * location is NONE, we move edittop up current_y lines if current_y
3261 * is in range of the screen, 0 lines if current_y is less than 0,
3262 * or (editwinrows - 1) lines if current_y is greater than
3263 * (editwinrows - 1). This puts current at the same place on the
3264 * screen as before, or at the top or bottom of the screen if
3265 * edittop is beyond either. */
3266 if (location == CENTER)
3267 goal = editwinrows / 2;
3268 else {
3269 goal = openfile->current_y;
3271 /* Limit goal to (editwinrows - 1) lines maximum. */
3272 if (goal > editwinrows - 1)
3273 goal = editwinrows - 1;
3276 for (; goal > 0 && foo->prev != NULL; goal--) {
3277 foo = foo->prev;
3278 if (ISSET(SOFTWRAP) && foo)
3279 goal -= strlenpt(foo->data) / COLS;
3281 openfile->edittop = foo;
3282 #ifdef DEBUG
3283 fprintf(stderr, "edit_udpate(), setting edittop to lineno %d\n", openfile->edittop->lineno);
3284 #endif
3285 compute_maxrows();
3286 edit_refresh_needed = TRUE;
3289 /* Unconditionally redraw the entire screen. */
3290 void total_redraw(void)
3292 #ifdef USE_SLANG
3293 /* Slang curses emulation brain damage, part 4: Slang doesn't define
3294 * curscr. */
3295 SLsmg_touch_screen();
3296 SLsmg_refresh();
3297 #else
3298 wrefresh(curscr);
3299 #endif
3302 /* Unconditionally redraw the entire screen, and then refresh it using
3303 * the current file. */
3304 void total_refresh(void)
3306 total_redraw();
3307 titlebar(NULL);
3308 edit_refresh();
3309 bottombars(currmenu);
3312 /* Display the main shortcut list on the last two rows of the bottom
3313 * portion of the window. */
3314 void display_main_list(void)
3316 bottombars(MMAIN);
3319 /* If constant is TRUE, we display the current cursor position only if
3320 * disable_cursorpos is FALSE. Otherwise, we display it
3321 * unconditionally and set disable_cursorpos to FALSE. If constant is
3322 * TRUE and disable_cursorpos is TRUE, we also set disable_cursorpos to
3323 * FALSE, so that we leave the current statusbar alone this time, and
3324 * display the current cursor position next time. */
3325 void do_cursorpos(bool constant)
3327 filestruct *f;
3328 char c;
3329 size_t i, cur_xpt = xplustabs() + 1;
3330 size_t cur_lenpt = strlenpt(openfile->current->data) + 1;
3331 int linepct, colpct, charpct;
3333 assert(openfile->fileage != NULL && openfile->current != NULL);
3335 f = openfile->current->next;
3336 c = openfile->current->data[openfile->current_x];
3338 openfile->current->next = NULL;
3339 openfile->current->data[openfile->current_x] = '\0';
3341 i = get_totsize(openfile->fileage, openfile->current);
3343 openfile->current->data[openfile->current_x] = c;
3344 openfile->current->next = f;
3346 if (constant && disable_cursorpos) {
3347 disable_cursorpos = FALSE;
3348 return;
3351 /* Display the current cursor position on the statusbar, and set
3352 * disable_cursorpos to FALSE. */
3353 linepct = 100 * openfile->current->lineno /
3354 openfile->filebot->lineno;
3355 colpct = 100 * cur_xpt / cur_lenpt;
3356 charpct = (openfile->totsize == 0) ? 0 : 100 * i /
3357 openfile->totsize;
3359 statusbar(
3360 _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"),
3361 (long)openfile->current->lineno,
3362 (long)openfile->filebot->lineno, linepct,
3363 (unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,
3364 (unsigned long)i, (unsigned long)openfile->totsize, charpct);
3366 disable_cursorpos = FALSE;
3369 /* Unconditionally display the current cursor position. */
3370 void do_cursorpos_void(void)
3372 do_cursorpos(FALSE);
3375 void enable_nodelay(void)
3377 nodelay_mode = TRUE;
3378 nodelay(edit, TRUE);
3381 void disable_nodelay(void)
3383 nodelay_mode = FALSE;
3384 nodelay(edit, FALSE);
3388 /* Highlight the current word being replaced or spell checked. We
3389 * expect word to have tabs and control characters expanded. */
3390 void do_replace_highlight(bool highlight, const char *word)
3392 size_t y = xplustabs(), word_len = strlenpt(word);
3394 y = get_page_start(y) + COLS - y;
3395 /* Now y is the number of columns that we can display on this
3396 * line. */
3398 assert(y > 0);
3400 if (word_len > y)
3401 y--;
3403 reset_cursor();
3404 wnoutrefresh(edit);
3406 if (highlight)
3407 wattron(edit, reverse_attr);
3409 /* This is so we can show zero-length matches. */
3410 if (word_len == 0)
3411 waddch(edit, ' ');
3412 else
3413 waddnstr(edit, word, actual_x(word, y));
3415 if (word_len > y)
3416 waddch(edit, '$');
3418 if (highlight)
3419 wattroff(edit, reverse_attr);
3422 #ifdef NANO_EXTRA
3423 #define CREDIT_LEN 55
3424 #define XLCREDIT_LEN 8
3426 /* Easter egg: Display credits. Assume nodelay(edit) and scrollok(edit)
3427 * are FALSE. */
3428 void do_credits(void)
3430 bool old_more_space = ISSET(MORE_SPACE);
3431 bool old_no_help = ISSET(NO_HELP);
3432 int kbinput = ERR, crpos = 0, xlpos = 0;
3433 const char *credits[CREDIT_LEN] = {
3434 NULL, /* "The nano text editor" */
3435 NULL, /* "version" */
3436 VERSION,
3438 NULL, /* "Brought to you by:" */
3439 "Chris Allegretta",
3440 "Jordi Mallach",
3441 "Adam Rogoyski",
3442 "Rob Siemborski",
3443 "Rocco Corsi",
3444 "David Lawrence Ramsey",
3445 "David Benbennick",
3446 "Mike Frysinger",
3447 "Ken Tyler",
3448 "Sven Guckes",
3449 NULL, /* credits[15], handled below. */
3450 "Pauli Virtanen",
3451 "Daniele Medri",
3452 "Clement Laforet",
3453 "Tedi Heriyanto",
3454 "Bill Soudan",
3455 "Christian Weisgerber",
3456 "Erik Andersen",
3457 "Big Gaute",
3458 "Joshua Jensen",
3459 "Ryan Krebs",
3460 "Albert Chin",
3462 NULL, /* "Special thanks to:" */
3463 "Plattsburgh State University",
3464 "Benet Laboratories",
3465 "Amy Allegretta",
3466 "Linda Young",
3467 "Jeremy Robichaud",
3468 "Richard Kolb II",
3469 NULL, /* "The Free Software Foundation" */
3470 "Linus Torvalds",
3471 NULL, /* "For ncurses:" */
3472 "Thomas Dickey",
3473 "Pavel Curtis",
3474 "Zeyd Ben-Halim",
3475 "Eric S. Raymond",
3476 NULL, /* "and anyone else we forgot..." */
3477 NULL, /* "Thank you for using nano!" */
3482 "(C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007",
3483 "Free Software Foundation, Inc.",
3488 "http://www.nano-editor.org/"
3491 const char *xlcredits[XLCREDIT_LEN] = {
3492 N_("The nano text editor"),
3493 N_("version"),
3494 N_("Brought to you by:"),
3495 N_("Special thanks to:"),
3496 N_("The Free Software Foundation"),
3497 N_("For ncurses:"),
3498 N_("and anyone else we forgot..."),
3499 N_("Thank you for using nano!")
3502 /* credits[15]: Make sure this name is displayed properly, since we
3503 * can't dynamically assign it above, using Unicode 00F6 (Latin
3504 * Small Letter O with Diaresis) if applicable. */
3505 credits[15] =
3506 #ifdef ENABLE_UTF8
3507 using_utf8() ? "Florian K\xC3\xB6nig" :
3508 #endif
3509 "Florian K\xF6nig";
3511 if (!old_more_space || !old_no_help) {
3512 SET(MORE_SPACE);
3513 SET(NO_HELP);
3514 window_init();
3517 curs_set(0);
3518 nodelay(edit, TRUE);
3520 blank_titlebar();
3521 blank_topbar();
3522 blank_edit();
3523 blank_statusbar();
3524 blank_bottombars();
3526 wrefresh(topwin);
3527 wrefresh(edit);
3528 wrefresh(bottomwin);
3529 napms(700);
3531 for (crpos = 0; crpos < CREDIT_LEN + editwinrows / 2; crpos++) {
3532 if ((kbinput = wgetch(edit)) != ERR)
3533 break;
3535 if (crpos < CREDIT_LEN) {
3536 const char *what;
3537 size_t start_x;
3539 if (credits[crpos] == NULL) {
3540 assert(0 <= xlpos && xlpos < XLCREDIT_LEN);
3542 what = _(xlcredits[xlpos]);
3543 xlpos++;
3544 } else
3545 what = credits[crpos];
3547 start_x = COLS / 2 - strlenpt(what) / 2 - 1;
3548 mvwaddstr(edit, editwinrows - 1 - (editwinrows % 2),
3549 start_x, what);
3552 wrefresh(edit);
3554 if ((kbinput = wgetch(edit)) != ERR)
3555 break;
3556 napms(700);
3558 scrollok(edit, TRUE);
3559 wscrl(edit, 1);
3560 scrollok(edit, FALSE);
3561 wrefresh(edit);
3563 if ((kbinput = wgetch(edit)) != ERR)
3564 break;
3565 napms(700);
3567 scrollok(edit, TRUE);
3568 wscrl(edit, 1);
3569 scrollok(edit, FALSE);
3570 wrefresh(edit);
3573 if (kbinput != ERR)
3574 ungetch(kbinput);
3576 if (!old_more_space || !old_no_help) {
3577 UNSET(MORE_SPACE);
3578 UNSET(NO_HELP);
3579 window_init();
3582 curs_set(1);
3583 nodelay(edit, FALSE);
3585 total_refresh();
3587 #endif /* NANO_EXTRA */