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