editline.c (22625B)
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. 8 * 9 * This software is not subject to any license of the American Telephone 10 * and Telegraph Company or of the Regents of the University of California. 11 * 12 * Permission is granted to anyone to use this software for any purpose on 13 * any computer system, and to alter it and redistribute it freely, subject 14 * to the following restrictions: 15 * 1. The authors are not responsible for the consequences of use of this 16 * software, no matter how awful, even if they arise from flaws in it. 17 * 2. The origin of this software must not be misrepresented, either by 18 * explicit claim or by omission. Since few users ever read sources, 19 * credits must appear in the documentation. 20 * 3. Altered versions must be plainly marked as such, and must not be 21 * misrepresented as being the original software. Since few users 22 * ever read sources, credits must appear in the documentation. 23 * 4. This notice may not be removed or altered. 24 */ 25 26 27 /* 28 ** Main editing routines for editline library. 29 */ 30 #include "editline.h" 31 #include <signal.h> 32 #include <ctype.h> 33 #include <unistd.h> 34 35 /* 36 ** Manifest constants. 37 */ 38 #define SCREEN_WIDTH 80 39 #define SCREEN_ROWS 24 40 #define NO_ARG (-1) 41 #define DEL 127 42 #define CTL(x) ((x) & 0x1F) 43 #define ISCTL(x) ((x) && (x) < ' ') 44 #define UNCTL(x) ((x) + 64) 45 #define META(x) ((x) | 0x80) 46 #define ISMETA(x) ((x) & 0x80) 47 #define UNMETA(x) ((x) & 0x7F) 48 #if !defined(HIST_SIZE) 49 #define HIST_SIZE 20 50 #endif /* !defined(HIST_SIZE) */ 51 52 /* 53 ** Command status codes. 54 */ 55 typedef enum _STATUS { 56 CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal 57 } STATUS; 58 59 /* 60 ** The type of case-changing to perform. 61 */ 62 typedef enum _CASE { 63 TOupper, TOlower 64 } CASE; 65 66 /* 67 ** Key to command mapping. 68 */ 69 typedef struct _KEYMAP { 70 CHAR Key; 71 STATUS (*Function)(); 72 } KEYMAP; 73 74 /* 75 ** Command history structure. 76 */ 77 typedef struct _HISTORY { 78 int Size; 79 int Pos; 80 CHAR *Lines[HIST_SIZE]; 81 } HISTORY; 82 83 /* 84 ** Globals. 85 */ 86 unsigned rl_eof; 87 unsigned rl_erase; 88 unsigned rl_intr; 89 unsigned rl_kill; 90 unsigned rl_quit; 91 92 STATIC CHAR NIL[] = ""; 93 STATIC CONST CHAR *Input = NIL; 94 STATIC CHAR *Line; 95 STATIC CONST char *Prompt; 96 STATIC CHAR *Yanked; 97 STATIC char *Screen; 98 STATIC CONST char NEWLINE[]= CRLF; 99 STATIC HISTORY H; 100 STATIC int Repeat; 101 STATIC int End; 102 STATIC int Mark; 103 STATIC int OldPoint; 104 STATIC int Point; 105 STATIC int PushBack; 106 STATIC int Pushed; 107 STATIC int Signal; 108 FORWARD CONST KEYMAP Map[32]; 109 FORWARD CONST KEYMAP MetaMap[16]; 110 STATIC SIZE_T Length; 111 STATIC SIZE_T ScreenCount; 112 STATIC SIZE_T ScreenSize; 113 STATIC char *backspace; 114 STATIC int TTYwidth; 115 STATIC int TTYrows; 116 117 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */ 118 int rl_meta_chars = 0; 119 120 /* 121 ** Declarations. 122 */ 123 STATIC CHAR *editinput(); 124 #if defined(USE_TERMCAP) 125 #include <stdlib.h> 126 #include <curses.h> 127 #include <term.h> 128 #endif /* defined(USE_TERMCAP) */ 129 130 /* 131 ** TTY input/output functions. 132 */ 133 134 STATIC void 135 TTYflush() 136 { 137 if (ScreenCount) { 138 /* Dummy assignment avoids GCC warning on 139 * "attribute warn_unused_result" */ 140 ssize_t dummy = write(1, Screen, ScreenCount); 141 (void)dummy; 142 ScreenCount = 0; 143 } 144 } 145 146 STATIC void 147 TTYput(CHAR c) 148 { 149 Screen[ScreenCount] = c; 150 if (++ScreenCount >= ScreenSize - 1) { 151 ScreenSize += SCREEN_INC; 152 RENEW(Screen, char, ScreenSize); 153 } 154 } 155 156 STATIC void 157 TTYputs(CONST CHAR *p) 158 { 159 while (*p) 160 TTYput(*p++); 161 } 162 163 STATIC void 164 TTYshow(CHAR c) 165 { 166 if (c == DEL) { 167 TTYput('^'); 168 TTYput('?'); 169 } 170 else if (ISCTL(c)) { 171 TTYput('^'); 172 TTYput(UNCTL(c)); 173 } 174 else if (rl_meta_chars && ISMETA(c)) { 175 TTYput('M'); 176 TTYput('-'); 177 TTYput(UNMETA(c)); 178 } 179 else 180 TTYput(c); 181 } 182 183 STATIC void 184 TTYstring(CHAR *p) 185 { 186 while (*p) 187 TTYshow(*p++); 188 } 189 190 STATIC unsigned int 191 TTYget() 192 { 193 CHAR c; 194 195 TTYflush(); 196 if (Pushed) { 197 Pushed = 0; 198 return PushBack; 199 } 200 if (*Input) 201 return *Input++; 202 return read(0, &c, (SIZE_T)1) == 1 ? c : EOF; 203 } 204 205 #define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b')) 206 207 STATIC void 208 TTYbackn(int n) 209 { 210 while (--n >= 0) 211 TTYback(); 212 } 213 214 STATIC void 215 TTYinfo() 216 { 217 static int init; 218 #if defined(USE_TERMCAP) 219 char *term; 220 char buff[2048]; 221 char *bp, *p; 222 #endif /* defined(USE_TERMCAP) */ 223 #if defined(TIOCGWINSZ) 224 struct winsize W; 225 #endif /* defined(TIOCGWINSZ) */ 226 227 if (init) { 228 #if defined(TIOCGWINSZ) 229 /* Perhaps we got resized. */ 230 if (ioctl(0, TIOCGWINSZ, &W) >= 0 231 && W.ws_col > 0 && W.ws_row > 0) { 232 TTYwidth = (int)W.ws_col; 233 TTYrows = (int)W.ws_row; 234 } 235 #endif /* defined(TIOCGWINSZ) */ 236 return; 237 } 238 init++; 239 240 TTYwidth = TTYrows = 0; 241 #if defined(USE_TERMCAP) 242 bp = &buff[0]; 243 if ((term = getenv("TERM")) == NULL) 244 term = "dumb"; 245 if (tgetent(buff, term) < 0) { 246 TTYwidth = SCREEN_WIDTH; 247 TTYrows = SCREEN_ROWS; 248 return; 249 } 250 p = tgetstr("le", &bp); 251 backspace = p ? strdup(p) : NULL; 252 TTYwidth = tgetnum("co"); 253 TTYrows = tgetnum("li"); 254 #endif /* defined(USE_TERMCAP) */ 255 256 #if defined(TIOCGWINSZ) 257 if (ioctl(0, TIOCGWINSZ, &W) >= 0) { 258 TTYwidth = (int)W.ws_col; 259 TTYrows = (int)W.ws_row; 260 } 261 #endif /* defined(TIOCGWINSZ) */ 262 263 if (TTYwidth <= 0 || TTYrows <= 0) { 264 TTYwidth = SCREEN_WIDTH; 265 TTYrows = SCREEN_ROWS; 266 } 267 } 268 269 270 STATIC void 271 reposition() 272 { 273 int i; 274 CHAR *p; 275 276 TTYput('\r'); 277 TTYputs((CONST CHAR *)Prompt); 278 for (i = Point, p = Line; --i >= 0; p++) 279 TTYshow(*p); 280 } 281 282 STATIC void 283 left(STATUS Change) 284 { 285 TTYback(); 286 if (Point) { 287 if (ISCTL(Line[Point - 1])) 288 TTYback(); 289 else if (rl_meta_chars && ISMETA(Line[Point - 1])) { 290 TTYback(); 291 TTYback(); 292 } 293 } 294 if (Change == CSmove) 295 Point--; 296 } 297 298 STATIC void 299 right(STATUS Change) 300 { 301 TTYshow(Line[Point]); 302 if (Change == CSmove) 303 Point++; 304 } 305 306 STATIC STATUS 307 ring_bell() 308 { 309 TTYput('\07'); 310 TTYflush(); 311 return CSstay; 312 } 313 314 STATIC STATUS 315 do_macro(unsigned int c) 316 { 317 CHAR name[4]; 318 319 name[0] = '_'; 320 name[1] = c; 321 name[2] = '_'; 322 name[3] = '\0'; 323 324 if ((Input = (CHAR *)getenv((char *)name)) == NULL) { 325 Input = NIL; 326 return ring_bell(); 327 } 328 return CSstay; 329 } 330 331 STATIC STATUS 332 do_forward(STATUS move) 333 { 334 int i; 335 CHAR *p; 336 337 i = 0; 338 do { 339 p = &Line[Point]; 340 for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++) 341 if (move == CSmove) 342 right(CSstay); 343 344 for (; Point < End && isalnum(*p); Point++, p++) 345 if (move == CSmove) 346 right(CSstay); 347 348 if (Point == End) 349 break; 350 } while (++i < Repeat); 351 352 return CSstay; 353 } 354 355 STATIC STATUS 356 do_case(CASE type) 357 { 358 int i; 359 int end; 360 int count; 361 CHAR *p; 362 363 (void)do_forward(CSstay); 364 if (OldPoint != Point) { 365 if ((count = Point - OldPoint) < 0) 366 count = -count; 367 Point = OldPoint; 368 if ((end = Point + count) > End) 369 end = End; 370 for (i = Point, p = &Line[i]; i < end; i++, p++) { 371 if (type == TOupper) { 372 if (islower(*p)) 373 *p = toupper(*p); 374 } 375 else if (isupper(*p)) 376 *p = tolower(*p); 377 right(CSmove); 378 } 379 } 380 return CSstay; 381 } 382 383 STATIC STATUS 384 case_down_word() 385 { 386 return do_case(TOlower); 387 } 388 389 STATIC STATUS 390 case_up_word() 391 { 392 return do_case(TOupper); 393 } 394 395 STATIC void 396 ceol() 397 { 398 int extras; 399 int i; 400 CHAR *p; 401 402 for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) { 403 TTYput(' '); 404 if (ISCTL(*p)) { 405 TTYput(' '); 406 extras++; 407 } 408 else if (rl_meta_chars && ISMETA(*p)) { 409 TTYput(' '); 410 TTYput(' '); 411 extras += 2; 412 } 413 } 414 415 for (i += extras; i > Point; i--) 416 TTYback(); 417 } 418 419 STATIC void 420 clear_line() 421 { 422 Point = -strlen(Prompt); 423 TTYput('\r'); 424 ceol(); 425 Point = 0; 426 End = 0; 427 Line[0] = '\0'; 428 } 429 430 STATIC STATUS 431 insert_string(CHAR *p) 432 { 433 SIZE_T len; 434 int i; 435 CHAR *new; 436 CHAR *q; 437 438 len = strlen((char *)p); 439 if (End + len >= Length) { 440 if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL) 441 return CSstay; 442 if (Length) { 443 COPYFROMTO(new, Line, Length); 444 DISPOSE(Line); 445 } 446 Line = new; 447 Length += len + MEM_INC; 448 } 449 450 for (q = &Line[Point], i = End - Point; --i >= 0; ) 451 q[len + i] = q[i]; 452 COPYFROMTO(&Line[Point], p, len); 453 End += len; 454 Line[End] = '\0'; 455 TTYstring(&Line[Point]); 456 Point += len; 457 458 return Point == End ? CSstay : CSmove; 459 } 460 461 STATIC STATUS 462 redisplay() 463 { 464 TTYputs((CONST CHAR *)NEWLINE); 465 TTYputs((CONST CHAR *)Prompt); 466 TTYstring(Line); 467 return CSmove; 468 } 469 470 STATIC STATUS 471 toggle_meta_mode() 472 { 473 rl_meta_chars = ! rl_meta_chars; 474 return redisplay(); 475 } 476 477 478 STATIC CHAR * 479 next_hist() 480 { 481 return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos]; 482 } 483 484 STATIC CHAR * 485 prev_hist() 486 { 487 return H.Pos == 0 ? NULL : H.Lines[--H.Pos]; 488 } 489 490 STATIC STATUS 491 do_insert_hist(CHAR *p) 492 { 493 if (p == NULL) 494 return ring_bell(); 495 Point = 0; 496 reposition(); 497 ceol(); 498 End = 0; 499 return insert_string(p); 500 } 501 502 STATIC STATUS 503 do_hist(CHAR *(*move)()) 504 { 505 CHAR *p; 506 int i; 507 508 i = 0; 509 do { 510 if ((p = (*move)()) == NULL) 511 return ring_bell(); 512 } while (++i < Repeat); 513 return do_insert_hist(p); 514 } 515 516 STATIC STATUS 517 h_next() 518 { 519 return do_hist(next_hist); 520 } 521 522 STATIC STATUS 523 h_prev() 524 { 525 return do_hist(prev_hist); 526 } 527 528 STATIC STATUS 529 h_first() 530 { 531 return do_insert_hist(H.Lines[H.Pos = 0]); 532 } 533 534 STATIC STATUS 535 h_last() 536 { 537 return do_insert_hist(H.Lines[H.Pos = H.Size - 1]); 538 } 539 540 /* 541 ** Return zero if pat appears as a substring in text. 542 */ 543 STATIC int 544 substrcmp(char *text, char *pat, size_t len) 545 { 546 char c; 547 548 if ((c = *pat) == '\0') 549 return *text == '\0'; 550 for ( ; *text; text++) 551 if (*text == c && strncmp(text, pat, len) == 0) 552 return 0; 553 return 1; 554 } 555 556 STATIC CHAR * 557 search_hist(CHAR *search, CHAR *(*move)()) 558 { 559 static CHAR *old_search; 560 int len; 561 int pos; 562 int (*match)(char *, char *, size_t); 563 char *pat; 564 565 /* Save or get remembered search pattern. */ 566 if (search && *search) { 567 if (old_search) 568 DISPOSE(old_search); 569 old_search = (CHAR *)strdup((char *)search); 570 } 571 else { 572 if (old_search == NULL || *old_search == '\0') 573 return NULL; 574 search = old_search; 575 } 576 577 /* Set up pattern-finder. */ 578 if (*search == '^') { 579 match = (int(*)(char *, char *, size_t))strncmp; 580 pat = (char *)(search + 1); 581 } 582 else { 583 match = substrcmp; 584 pat = (char *)search; 585 } 586 len = strlen(pat); 587 588 for (pos = H.Pos; (*move)() != NULL; ) 589 if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0) 590 return H.Lines[H.Pos]; 591 H.Pos = pos; 592 return NULL; 593 } 594 595 STATIC STATUS 596 h_search() 597 { 598 static int Searching; 599 CONST char *old_prompt; 600 CHAR *(*move)(); 601 CHAR *p; 602 603 if (Searching) 604 return ring_bell(); 605 Searching = 1; 606 607 clear_line(); 608 old_prompt = Prompt; 609 Prompt = "Search: "; 610 TTYputs((CONST CHAR *)Prompt); 611 move = Repeat == NO_ARG ? prev_hist : next_hist; 612 p = editinput(); 613 Prompt = old_prompt; 614 Searching = 0; 615 TTYputs((CONST CHAR *)Prompt); 616 if (p == NULL && Signal > 0) { 617 Signal = 0; 618 clear_line(); 619 return redisplay(); 620 } 621 p = search_hist(p, move); 622 clear_line(); 623 if (p == NULL) { 624 (void)ring_bell(); 625 return redisplay(); 626 } 627 return do_insert_hist(p); 628 } 629 630 STATIC STATUS 631 fd_char() 632 { 633 int i; 634 635 i = 0; 636 do { 637 if (Point >= End) 638 break; 639 right(CSmove); 640 } while (++i < Repeat); 641 return CSstay; 642 } 643 644 STATIC void 645 save_yank(int begin, int i) 646 { 647 if (Yanked) { 648 DISPOSE(Yanked); 649 Yanked = NULL; 650 } 651 652 if (i < 1) 653 return; 654 655 if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) { 656 COPYFROMTO(Yanked, &Line[begin], i); 657 Yanked[i] = '\0'; 658 } 659 } 660 661 STATIC STATUS 662 delete_string(int count) 663 { 664 int i; 665 CHAR *p; 666 667 if (count <= 0 || End == Point) 668 return ring_bell(); 669 670 if (count == 1 && Point == End - 1) { 671 /* Optimize common case of delete at end of line. */ 672 End--; 673 p = &Line[Point]; 674 i = 1; 675 TTYput(' '); 676 if (ISCTL(*p)) { 677 i = 2; 678 TTYput(' '); 679 } 680 else if (rl_meta_chars && ISMETA(*p)) { 681 i = 3; 682 TTYput(' '); 683 TTYput(' '); 684 } 685 TTYbackn(i); 686 *p = '\0'; 687 return CSmove; 688 } 689 if (Point + count > End && (count = End - Point) <= 0) 690 return CSstay; 691 692 if (count > 1) 693 save_yank(Point, count); 694 695 for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++) 696 p[0] = p[count]; 697 ceol(); 698 End -= count; 699 TTYstring(&Line[Point]); 700 return CSmove; 701 } 702 703 STATIC STATUS 704 bk_char() 705 { 706 int i; 707 708 i = 0; 709 do { 710 if (Point == 0) 711 break; 712 left(CSmove); 713 } while (++i < Repeat); 714 715 return CSstay; 716 } 717 718 STATIC STATUS 719 bk_del_char() 720 { 721 int i; 722 723 i = 0; 724 do { 725 if (Point == 0) 726 break; 727 left(CSmove); 728 } while (++i < Repeat); 729 730 return delete_string(i); 731 } 732 733 STATIC STATUS 734 kill_line() 735 { 736 int i; 737 738 if (Repeat != NO_ARG) { 739 if (Repeat < Point) { 740 i = Point; 741 Point = Repeat; 742 reposition(); 743 (void)delete_string(i - Point); 744 } 745 else if (Repeat > Point) { 746 right(CSmove); 747 (void)delete_string(Repeat - Point - 1); 748 } 749 return CSmove; 750 } 751 752 save_yank(Point, End - Point); 753 Line[Point] = '\0'; 754 ceol(); 755 End = Point; 756 return CSstay; 757 } 758 759 STATIC STATUS 760 insert_char(int c) 761 { 762 STATUS s; 763 CHAR buff[2]; 764 CHAR *p; 765 CHAR *q; 766 int i; 767 768 if (Repeat == NO_ARG || Repeat < 2) { 769 buff[0] = c; 770 buff[1] = '\0'; 771 return insert_string(buff); 772 } 773 774 if ((p = NEW(CHAR, Repeat + 1)) == NULL) 775 return CSstay; 776 for (i = Repeat, q = p; --i >= 0; ) 777 *q++ = c; 778 *q = '\0'; 779 Repeat = 0; 780 s = insert_string(p); 781 DISPOSE(p); 782 return s; 783 } 784 785 STATIC STATUS 786 meta() 787 { 788 unsigned int c; 789 CONST KEYMAP *kp; 790 791 if ((int)(c = TTYget()) == EOF) 792 return CSeof; 793 #if defined(ANSI_ARROWS) 794 /* Also include VT-100 arrows. */ 795 if (c == '[' || c == 'O') 796 switch (c = TTYget()) { 797 default: return ring_bell(); 798 case EOF: return CSeof; 799 case 'A': return h_prev(); 800 case 'B': return h_next(); 801 case 'C': return fd_char(); 802 case 'D': return bk_char(); 803 } 804 #endif /* defined(ANSI_ARROWS) */ 805 806 if (isdigit(c)) { 807 for (Repeat = c - '0'; (int)(c = TTYget()) != EOF && isdigit(c); ) 808 Repeat = Repeat * 10 + c - '0'; 809 Pushed = 1; 810 PushBack = c; 811 return CSstay; 812 } 813 814 if (isupper(c)) 815 return do_macro(c); 816 for (OldPoint = Point, kp = MetaMap; kp->Function; kp++) 817 if (kp->Key == c) 818 return (*kp->Function)(); 819 820 return ring_bell(); 821 } 822 823 STATIC STATUS 824 emacs(unsigned int c) 825 { 826 STATUS s; 827 const KEYMAP *kp; 828 829 if (rl_meta_chars && ISMETA(c)) { 830 Pushed = 1; 831 PushBack = UNMETA(c); 832 return meta(); 833 } 834 for (kp = Map; kp->Function; kp++) 835 if (kp->Key == c) 836 break; 837 s = kp->Function ? (*kp->Function)() : insert_char((int)c); 838 if (!Pushed) 839 /* No pushback means no repeat count; hacky, but true. */ 840 Repeat = NO_ARG; 841 return s; 842 } 843 844 STATIC STATUS 845 TTYspecial(unsigned int c) 846 { 847 if (ISMETA(c)) 848 return CSdispatch; 849 850 if (c == rl_erase || (int)c == DEL) 851 return bk_del_char(); 852 if (c == rl_kill) { 853 if (Point != 0) { 854 Point = 0; 855 reposition(); 856 } 857 Repeat = NO_ARG; 858 return kill_line(); 859 } 860 if (c == rl_eof && Point == 0 && End == 0) 861 return CSeof; 862 if (c == rl_intr) { 863 Signal = SIGINT; 864 return CSsignal; 865 } 866 if (c == rl_quit) { 867 Signal = SIGQUIT; 868 return CSeof; 869 } 870 871 return CSdispatch; 872 } 873 874 STATIC CHAR * 875 editinput() 876 { 877 unsigned int c; 878 879 Repeat = NO_ARG; 880 OldPoint = Point = Mark = End = 0; 881 Line[0] = '\0'; 882 883 Signal = -1; 884 while ((int)(c = TTYget()) != EOF) 885 switch (TTYspecial(c)) { 886 case CSdone: 887 return Line; 888 case CSeof: 889 return NULL; 890 case CSsignal: 891 return (CHAR *)""; 892 case CSmove: 893 reposition(); 894 break; 895 case CSdispatch: 896 switch (emacs(c)) { 897 case CSdone: 898 return Line; 899 case CSeof: 900 return NULL; 901 case CSsignal: 902 return (CHAR *)""; 903 case CSmove: 904 reposition(); 905 break; 906 case CSdispatch: 907 case CSstay: 908 break; 909 } 910 break; 911 case CSstay: 912 break; 913 } 914 if (strlen((char *)Line)) 915 return Line; 916 free(Line); 917 return NULL; 918 } 919 920 STATIC void 921 hist_add(CHAR *p) 922 { 923 int i; 924 925 if ((p = (CHAR *)strdup((char *)p)) == NULL) 926 return; 927 if (H.Size < HIST_SIZE) 928 H.Lines[H.Size++] = p; 929 else { 930 DISPOSE(H.Lines[0]); 931 for (i = 0; i < HIST_SIZE - 1; i++) 932 H.Lines[i] = H.Lines[i + 1]; 933 H.Lines[i] = p; 934 } 935 H.Pos = H.Size - 1; 936 } 937 938 /* 939 ** For compatibility with FSF readline. 940 */ 941 /* ARGSUSED0 */ 942 void 943 rl_reset_terminal(char *p) 944 { 945 (void)p; 946 } 947 948 void 949 rl_initialize() 950 { 951 } 952 953 char * 954 readline(CONST char *prompt) 955 { 956 CHAR *line; 957 int s; 958 959 if (Line == NULL) { 960 Length = MEM_INC; 961 if ((Line = NEW(CHAR, Length)) == NULL) 962 return NULL; 963 } 964 965 TTYinfo(); 966 rl_ttyset(0); 967 hist_add(NIL); 968 ScreenSize = SCREEN_INC; 969 Screen = NEW(char, ScreenSize); 970 Prompt = prompt ? prompt : (char *)NIL; 971 TTYputs((CONST CHAR *)Prompt); 972 if ((line = editinput()) != NULL) { 973 line = (CHAR *)strdup((char *)line); 974 TTYputs((CONST CHAR *)NEWLINE); 975 TTYflush(); 976 } 977 rl_ttyset(1); 978 DISPOSE(Screen); 979 DISPOSE(H.Lines[--H.Size]); 980 if (Signal > 0) { 981 s = Signal; 982 Signal = 0; 983 (void)kill(getpid(), s); 984 } 985 return (char *)line; 986 } 987 988 void 989 add_history(char *p) 990 { 991 if (p == NULL || *p == '\0') 992 return; 993 994 #if defined(UNIQUE_HISTORY) 995 if (H.Size && strcmp(p, (char *)H.Lines[H.Size - 1]) == 0) 996 return; 997 #endif /* defined(UNIQUE_HISTORY) */ 998 hist_add((CHAR *)p); 999 } 1000 1001 1002 STATIC STATUS 1003 beg_line() 1004 { 1005 if (Point) { 1006 Point = 0; 1007 return CSmove; 1008 } 1009 return CSstay; 1010 } 1011 1012 STATIC STATUS 1013 del_char() 1014 { 1015 return delete_string(Repeat == NO_ARG ? 1 : Repeat); 1016 } 1017 1018 STATIC STATUS 1019 end_line() 1020 { 1021 if (Point != End) { 1022 Point = End; 1023 return CSmove; 1024 } 1025 return CSstay; 1026 } 1027 1028 STATIC STATUS 1029 accept_line() 1030 { 1031 Line[End] = '\0'; 1032 return CSdone; 1033 } 1034 1035 STATIC STATUS 1036 transpose() 1037 { 1038 CHAR c; 1039 1040 if (Point) { 1041 if (Point == End) 1042 left(CSmove); 1043 c = Line[Point - 1]; 1044 left(CSstay); 1045 Line[Point - 1] = Line[Point]; 1046 TTYshow(Line[Point - 1]); 1047 Line[Point++] = c; 1048 TTYshow(c); 1049 } 1050 return CSstay; 1051 } 1052 1053 STATIC STATUS 1054 quote() 1055 { 1056 unsigned int c; 1057 1058 return (int)(c = TTYget()) == EOF ? CSeof : insert_char((int)c); 1059 } 1060 1061 STATIC STATUS 1062 wipe() 1063 { 1064 int i; 1065 1066 if (Mark > End) 1067 return ring_bell(); 1068 1069 if (Point > Mark) { 1070 i = Point; 1071 Point = Mark; 1072 Mark = i; 1073 reposition(); 1074 } 1075 1076 return delete_string(Mark - Point); 1077 } 1078 1079 STATIC STATUS 1080 mk_set() 1081 { 1082 Mark = Point; 1083 return CSstay; 1084 } 1085 1086 STATIC STATUS 1087 exchange() 1088 { 1089 unsigned int c; 1090 1091 if ((c = TTYget()) != CTL('X')) 1092 return (int)c == EOF ? CSeof : ring_bell(); 1093 1094 if ((int)(c = Mark) <= End) { 1095 Mark = Point; 1096 Point = c; 1097 return CSmove; 1098 } 1099 return CSstay; 1100 } 1101 1102 STATIC STATUS 1103 yank() 1104 { 1105 if (Yanked && *Yanked) 1106 return insert_string(Yanked); 1107 return CSstay; 1108 } 1109 1110 STATIC STATUS 1111 copy_region() 1112 { 1113 if (Mark > End) 1114 return ring_bell(); 1115 1116 if (Point > Mark) 1117 save_yank(Mark, Point - Mark); 1118 else 1119 save_yank(Point, Mark - Point); 1120 1121 return CSstay; 1122 } 1123 1124 STATIC STATUS 1125 move_to_char() 1126 { 1127 unsigned int c; 1128 int i; 1129 CHAR *p; 1130 1131 if ((int)(c = TTYget()) == EOF) 1132 return CSeof; 1133 for (i = Point + 1, p = &Line[i]; i < End; i++, p++) 1134 if (*p == c) { 1135 Point = i; 1136 return CSmove; 1137 } 1138 return CSstay; 1139 } 1140 1141 STATIC STATUS 1142 fd_word() 1143 { 1144 return do_forward(CSmove); 1145 } 1146 1147 STATIC STATUS 1148 fd_kill_word() 1149 { 1150 int i; 1151 1152 (void)do_forward(CSstay); 1153 if (OldPoint != Point) { 1154 i = Point - OldPoint; 1155 Point = OldPoint; 1156 return delete_string(i); 1157 } 1158 return CSstay; 1159 } 1160 1161 STATIC STATUS 1162 bk_word() 1163 { 1164 int i; 1165 CHAR *p; 1166 1167 i = 0; 1168 do { 1169 for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--) 1170 left(CSmove); 1171 1172 for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--) 1173 left(CSmove); 1174 1175 if (Point == 0) 1176 break; 1177 } while (++i < Repeat); 1178 1179 return CSstay; 1180 } 1181 1182 STATIC STATUS 1183 bk_kill_word() 1184 { 1185 (void)bk_word(); 1186 if (OldPoint != Point) 1187 return delete_string(OldPoint - Point); 1188 return CSstay; 1189 } 1190 1191 STATIC int 1192 argify(CHAR *line, CHAR ***avp) 1193 { 1194 CHAR *c; 1195 CHAR **p; 1196 CHAR **new; 1197 int ac; 1198 int i; 1199 1200 i = MEM_INC; 1201 if ((*avp = p = NEW(CHAR*, i))== NULL) 1202 return 0; 1203 1204 for (c = line; isspace(*c); c++) 1205 continue; 1206 if (*c == '\n' || *c == '\0') 1207 return 0; 1208 1209 for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) { 1210 if (isspace(*c)) { 1211 *c++ = '\0'; 1212 if (*c && *c != '\n') { 1213 if (ac + 1 == i) { 1214 new = NEW(CHAR*, i + MEM_INC); 1215 if (new == NULL) { 1216 p[ac] = NULL; 1217 return ac; 1218 } 1219 COPYFROMTO(new, p, i * sizeof (char **)); 1220 i += MEM_INC; 1221 DISPOSE(p); 1222 *avp = p = new; 1223 } 1224 p[ac++] = c; 1225 } 1226 } 1227 else 1228 c++; 1229 } 1230 *c = '\0'; 1231 p[ac] = NULL; 1232 return ac; 1233 } 1234 1235 STATIC STATUS 1236 last_argument() 1237 { 1238 CHAR **av; 1239 CHAR *p; 1240 STATUS s; 1241 int ac; 1242 1243 if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL) 1244 return ring_bell(); 1245 1246 if ((p = (CHAR *)strdup((char *)p)) == NULL) 1247 return CSstay; 1248 ac = argify(p, &av); 1249 1250 if (Repeat != NO_ARG) 1251 s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell(); 1252 else 1253 s = ac ? insert_string(av[ac - 1]) : CSstay; 1254 1255 if (ac) 1256 DISPOSE(av); 1257 DISPOSE(p); 1258 return s; 1259 } 1260 1261 STATIC CONST KEYMAP Map[32] = { 1262 { CTL('@'), ring_bell }, 1263 { CTL('A'), beg_line }, 1264 { CTL('B'), bk_char }, 1265 { CTL('D'), del_char }, 1266 { CTL('E'), end_line }, 1267 { CTL('F'), fd_char }, 1268 { CTL('G'), ring_bell }, 1269 { CTL('H'), bk_del_char }, 1270 { CTL('J'), accept_line }, 1271 { CTL('K'), kill_line }, 1272 { CTL('L'), redisplay }, 1273 { CTL('M'), accept_line }, 1274 { CTL('N'), h_next }, 1275 { CTL('O'), ring_bell }, 1276 { CTL('P'), h_prev }, 1277 { CTL('Q'), ring_bell }, 1278 { CTL('R'), h_search }, 1279 { CTL('S'), ring_bell }, 1280 { CTL('T'), transpose }, 1281 { CTL('U'), ring_bell }, 1282 { CTL('V'), quote }, 1283 { CTL('W'), wipe }, 1284 { CTL('X'), exchange }, 1285 { CTL('Y'), yank }, 1286 { CTL('Z'), ring_bell }, 1287 { CTL('['), meta }, 1288 { CTL(']'), move_to_char }, 1289 { CTL('^'), ring_bell }, 1290 { CTL('_'), ring_bell }, 1291 { 0, NULL } 1292 }; 1293 1294 STATIC CONST KEYMAP MetaMap[16]= { 1295 { CTL('H'), bk_kill_word }, 1296 { DEL, bk_kill_word }, 1297 { ' ', mk_set }, 1298 { '.', last_argument }, 1299 { '<', h_first }, 1300 { '>', h_last }, 1301 { 'b', bk_word }, 1302 { 'd', fd_kill_word }, 1303 { 'f', fd_word }, 1304 { 'l', case_down_word }, 1305 { 'm', toggle_meta_mode }, 1306 { 'u', case_up_word }, 1307 { 'y', yank }, 1308 { 'w', copy_region }, 1309 { 0, NULL } 1310 };